dolibarr 21.0.0-alpha
ProductAttribute.class.php
Go to the documentation of this file.
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-2024 Frédéric France <frederic.france@free.fr>
5 * Copyright (C) 2024 MDW <mdeweerd@users.noreply.github.com>
6 *
7 * This program is free software; you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License as published by
9 * the Free Software Foundation; either version 3 of the License, or
10 * (at your option) any later version.
11 *
12 * This program is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 * GNU General Public License for more details.
16 *
17 * You should have received a copy of the GNU General Public License
18 * along with this program. If not, see <https://www.gnu.org/licenses/>.
19 */
20
27require_once DOL_DOCUMENT_ROOT.'/core/class/commonobject.class.php';
28
37{
42 public $db;
43
47 public $module = 'variants';
48
52 public $element = 'productattribute';
53
57 public $table_element = 'product_attribute';
58
62 public $table_element_line = 'product_attribute_value';
63
67 public $fk_element = 'fk_product_attribute';
68
72 public $picto = 'product';
73
102 public $fields = array(
103 'rowid' => array('type' => 'integer', 'label' => 'TechnicalID', 'enabled' => 1, 'position' => 1, 'notnull' => 1, 'visible' => 0, 'noteditable' => 1, 'index' => 1, 'css' => 'left', 'comment' => "Id"),
104 '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'),
105 'ref_ext' => array('type' => 'varchar(255)', 'label' => 'ExternalRef', 'enabled' => 1, 'visible' => 0, 'position' => 20, 'searchall' => 1),
106 'label' => array('type' => 'varchar(255)', 'label' => 'Label', 'enabled' => 1, 'position' => 30, 'notnull' => 1, 'visible' => 1, 'searchall' => 1, 'css' => 'minwidth300', 'help' => "", 'showoncombobox' => 1,),
107 'position' => array('type' => 'integer', 'label' => 'Rank', 'enabled' => 1, 'visible' => 0, 'default' => '0', 'position' => 40, 'notnull' => 1,),
108 );
109
113 public $id;
114
118 public $ref;
119
123 public $ref_ext;
124
128 public $label;
129
135 public $rang;
136
140 public $position;
141
145 public $lines = array();
146
150 public $line;
151
155 public $is_used_by_products;
156
157
163 public function __construct(DoliDB $db)
164 {
165 global $conf, $langs;
166
167 $this->db = $db;
168
169 $this->ismultientitymanaged = 1;
170 $this->isextrafieldmanaged = 0;
171 $this->entity = $conf->entity;
172
173 if (!getDolGlobalString('MAIN_SHOW_TECHNICAL_ID') && isset($this->fields['rowid'])) {
174 $this->fields['rowid']['visible'] = 0;
175 }
176 if (!isModEnabled('multicompany') && isset($this->fields['entity'])) {
177 $this->fields['entity']['enabled'] = 0;
178 }
179
180 // Unset fields that are disabled
181 foreach ($this->fields as $key => $val) {
182 if (isset($val['enabled']) && empty($val['enabled'])) {
183 unset($this->fields[$key]);
184 }
185 }
186
187 // Translate some data of arrayofkeyval
188 if (is_object($langs)) {
189 foreach ($this->fields as $key => $val) {
190 if (!empty($val['arrayofkeyval']) && is_array($val['arrayofkeyval'])) {
191 foreach ($val['arrayofkeyval'] as $key2 => $val2) {
192 $this->fields[$key]['arrayofkeyval'][$key2] = $langs->trans($val2);
193 }
194 }
195 }
196 }
197 }
198
206 public function create(User $user, $notrigger = 0)
207 {
208 global $langs;
209 $error = 0;
210
211 // Clean parameters
212 $this->ref = strtoupper(dol_sanitizeFileName(dol_string_nospecial(trim($this->ref)))); // Ref must be uppercase
213 $this->label = trim($this->label);
214 $this->position = $this->position > 0 ? $this->position : 0;
215
216 // Position to use
217 if (empty($this->position)) {
218 $positionmax = $this->getMaxAttributesPosition();
219 $this->position = $positionmax + 1;
220 }
221
222 // Check parameters
223 if (empty($this->ref)) {
224 $this->errors[] = $langs->trans("ErrorFieldRequired", $langs->transnoentitiesnoconv("Ref"));
225 $error++;
226 }
227 if (empty($this->label)) {
228 $this->errors[] = $langs->trans("ErrorFieldRequired", $langs->transnoentitiesnoconv("Label"));
229 $error++;
230 }
231 if ($error) {
232 dol_syslog(__METHOD__ . ' ' . $this->errorsToString(), LOG_ERR);
233 return -1;
234 }
235
236 $this->db->begin();
237
238 $sql = "INSERT INTO " . MAIN_DB_PREFIX . $this->table_element . " (";
239 $sql .= " ref, ref_ext, label, entity, position";
240 $sql .= ")";
241 $sql .= " VALUES (";
242 $sql .= " '" . $this->db->escape($this->ref) . "'";
243 $sql .= ", '" . $this->db->escape($this->ref_ext) . "'";
244 $sql .= ", '" . $this->db->escape($this->label) . "'";
245 $sql .= ", " . ((int) $this->entity);
246 $sql .= ", " . ((int) $this->position);
247 $sql .= ")";
248
249 dol_syslog(__METHOD__, LOG_DEBUG);
250 $resql = $this->db->query($sql);
251 if (!$resql) {
252 $this->errors[] = "Error " . $this->db->lasterror();
253 $error++;
254 }
255
256 if (!$error) {
257 $this->id = $this->db->last_insert_id(MAIN_DB_PREFIX . $this->table_element);
258 }
259
260 if (!$error && !$notrigger) {
261 // Call trigger
262 $result = $this->call_trigger('PRODUCT_ATTRIBUTE_CREATE', $user);
263 if ($result < 0) {
264 $error++;
265 }
266 // End call triggers
267 }
268
269 if (!$error) {
270 $this->db->commit();
271 return $this->id;
272 } else {
273 $this->db->rollback();
274 return -1 * $error;
275 }
276 }
277
284 public function fetch($id)
285 {
286 global $langs;
287 $error = 0;
288
289 // Clean parameters
290 $id = $id > 0 ? $id : 0;
291
292 // Check parameters
293 if (empty($id)) {
294 $this->errors[] = $langs->trans("ErrorFieldRequired", $langs->transnoentitiesnoconv("TechnicalID"));
295 $error++;
296 }
297 if ($error) {
298 dol_syslog(__METHOD__ . ' ' . $this->errorsToString(), LOG_ERR);
299 return -1;
300 }
301
302 $sql = "SELECT rowid, ref, ref_ext, label, position";
303 $sql .= " FROM " . MAIN_DB_PREFIX . $this->table_element;
304 $sql .= " WHERE rowid = " . ((int) $id);
305 $sql .= " AND entity IN (" . getEntity('product') . ")";
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_syslog(__METHOD__ . ' ' . $this->errorsToString(), LOG_ERR);
312 return -1;
313 }
314
315 $numrows = $this->db->num_rows($resql);
316 if ($numrows) {
317 $obj = $this->db->fetch_object($resql);
318
319 $this->id = $obj->rowid;
320 $this->ref = $obj->ref;
321 $this->ref_ext = $obj->ref_ext;
322 $this->label = $obj->label;
323 $this->rang = $obj->position; // deprecated
324 $this->position = $obj->position;
325 }
326 $this->db->free($resql);
327
328 return $numrows;
329 }
330
336 public function fetchAll()
337 {
338 $return = array();
339
340 $sql = "SELECT rowid, ref, ref_ext, label, position";
341 $sql .= " FROM " . MAIN_DB_PREFIX . $this->table_element;
342 $sql .= " WHERE entity IN (" . getEntity('product') . ")";
343 $sql .= $this->db->order("position", "asc");
344
345 dol_syslog(__METHOD__, LOG_DEBUG);
346 $resql = $this->db->query($sql);
347 if (!$resql) {
348 $this->errors[] = "Error " . $this->db->lasterror();
349 dol_print_error($this->db);
350 return $return;
351 }
352
353 while ($obj = $this->db->fetch_object($resql)) {
354 $tmp = new ProductAttribute($this->db);
355
356 $tmp->id = $obj->rowid;
357 $tmp->ref = $obj->ref;
358 $tmp->ref_ext = $obj->ref_ext;
359 $tmp->label = $obj->label;
360 $tmp->rang = $obj->position; // deprecated
361 $tmp->position = $obj->position;
362
363 $return[] = $tmp;
364 }
365
366 return $return;
367 }
368
376 public function update(User $user, $notrigger = 0)
377 {
378 global $langs;
379 $error = 0;
380
381 // Clean parameters
382 $this->id = $this->id > 0 ? $this->id : 0;
383 $this->ref = strtoupper(dol_sanitizeFileName(dol_string_nospecial(trim($this->ref)))); // Ref must be uppercase
384 $this->label = trim($this->label);
385
386 // Check parameters
387 if (empty($this->id)) {
388 $this->errors[] = $langs->trans("ErrorFieldRequired", $langs->transnoentitiesnoconv("TechnicalID"));
389 $error++;
390 }
391 if (empty($this->ref)) {
392 $this->errors[] = $langs->trans("ErrorFieldRequired", $langs->transnoentitiesnoconv("Ref"));
393 $error++;
394 }
395 if (empty($this->label)) {
396 $this->errors[] = $langs->trans("ErrorFieldRequired", $langs->transnoentitiesnoconv("Label"));
397 $error++;
398 }
399 if ($error) {
400 dol_syslog(__METHOD__ . ' ' . $this->errorsToString(), LOG_ERR);
401 return -1;
402 }
403
404 $this->db->begin();
405
406 $sql = "UPDATE " . MAIN_DB_PREFIX . $this->table_element . " SET";
407
408 $sql .= " ref = '" . $this->db->escape($this->ref) . "'";
409 $sql .= ", ref_ext = '" . $this->db->escape($this->ref_ext) . "'";
410 $sql .= ", label = '" . $this->db->escape($this->label) . "'";
411 $sql .= ", position = " . ((int) $this->position);
412
413 $sql .= " WHERE rowid = " . ((int) $this->id);
414
415 dol_syslog(__METHOD__, LOG_DEBUG);
416 $resql = $this->db->query($sql);
417 if (!$resql) {
418 $this->errors[] = "Error " . $this->db->lasterror();
419 $error++;
420 }
421
422 if (!$error && !$notrigger) {
423 // Call trigger
424 $result = $this->call_trigger('PRODUCT_ATTRIBUTE_MODIFY', $user);
425 if ($result < 0) {
426 $error++;
427 }
428 // End call triggers
429 }
430
431 if (!$error) {
432 $this->db->commit();
433 return 1;
434 } else {
435 $this->db->rollback();
436 return -1 * $error;
437 }
438 }
439
447 public function delete(User $user, $notrigger = 0)
448 {
449 global $langs;
450 $error = 0;
451
452 // Clean parameters
453 $this->id = $this->id > 0 ? $this->id : 0;
454
455 // Check parameters
456 if (empty($this->id)) {
457 $this->errors[] = $langs->trans("ErrorFieldRequired", $langs->transnoentitiesnoconv("TechnicalID"));
458 $error++;
459 }
460 if ($error) {
461 dol_syslog(__METHOD__ . ' ' . $this->errorsToString(), LOG_ERR);
462 return -1;
463 }
464
465 $result = $this->isUsed();
466 if ($result < 0) {
467 return -1;
468 } elseif ($result > 0) {
469 $this->errors[] = $langs->trans('ErrorAttributeIsUsedIntoProduct');
470 return -1;
471 }
472
473 $this->db->begin();
474
475 if (!$notrigger) {
476 // Call trigger
477 $result = $this->call_trigger('PRODUCT_ATTRIBUTE_DELETE', $user);
478 if ($result < 0) {
479 $error++;
480 }
481 // End call triggers
482 }
483
484 if (!$error) {
485 // Delete values
486 $sql = "DELETE FROM " . MAIN_DB_PREFIX . $this->table_element_line;
487 $sql .= " WHERE " . $this->fk_element . " = " . ((int) $this->id);
488
489 dol_syslog(__METHOD__ . ' - Delete values', LOG_DEBUG);
490 $resql = $this->db->query($sql);
491 if (!$resql) {
492 $this->errors[] = "Error " . $this->db->lasterror();
493 $error++;
494 }
495 }
496
497 if (!$error) {
498 $sql = "DELETE FROM " . MAIN_DB_PREFIX . $this->table_element;
499 $sql .= " WHERE rowid = " . ((int) $this->id);
500
501 dol_syslog(__METHOD__ . ' - Delete attribute', LOG_DEBUG);
502 $resql = $this->db->query($sql);
503 if (!$resql) {
504 $this->errors[] = "Error " . $this->db->lasterror();
505 $error++;
506 }
507 }
508
509 if (!$error) {
510 $this->db->commit();
511 return 1;
512 } else {
513 $this->db->rollback();
514 return -1 * $error;
515 }
516 }
517
518 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
525 public function fetch_lines($filters = '')
526 {
527 // phpcs:enable
528 global $langs;
529
530 $this->lines = array();
531
532 $error = 0;
533
534 // Clean parameters
535 $this->id = $this->id > 0 ? $this->id : 0;
536
537 // Check parameters
538 if (empty($this->id)) {
539 $this->errors[] = $langs->trans("ErrorFieldRequired", $langs->transnoentitiesnoconv("TechnicalID"));
540 $error++;
541 }
542 if ($error) {
543 dol_syslog(__METHOD__ . ' ' . $this->errorsToString(), LOG_ERR);
544 return -1;
545 }
546
547 $sql = "SELECT td.rowid, td.fk_product_attribute, td.ref, td.value, td.position";
548 $sql .= " FROM " . MAIN_DB_PREFIX . $this->table_element_line . " AS td";
549 $sql .= " LEFT JOIN " . MAIN_DB_PREFIX . $this->table_element . " AS t ON t.rowid = td." . $this->fk_element;
550 $sql .= " WHERE t.rowid = " . ((int) $this->id);
551 $sql .= " AND t.entity IN (" . getEntity('product') . ")";
552 if ($filters) {
553 $sql .= $filters;
554 }
555 $sql .= $this->db->order("td.position", "asc");
556
557 dol_syslog(__METHOD__, LOG_DEBUG);
558 $resql = $this->db->query($sql);
559 if (!$resql) {
560 $this->errors[] = "Error " . $this->db->lasterror();
561 dol_syslog(__METHOD__ . ' ' . $this->errorsToString(), LOG_ERR);
562 return -3;
563 }
564
565 $num = $this->db->num_rows($resql);
566 if ($num) {
567 $i = 0;
568 while ($i < $num) {
569 $obj = $this->db->fetch_object($resql);
570
571 $line = new ProductAttributeValue($this->db);
572
573 $line->id = $obj->rowid;
574 $line->fk_product_attribute = $obj->fk_product_attribute;
575 $line->ref = $obj->ref;
576 $line->value = $obj->value;
577 $line->position = $obj->position;
578
579 $this->lines[$i] = $line;
580 $i++;
581 }
582 }
583 $this->db->free($resql);
584
585 return $num;
586 }
587
594 public function getLinesArray($filters = '')
595 {
596 return $this->fetch_lines($filters);
597 }
598
612 public function addLine($ref, $value, $position = -1, $notrigger = 0)
613 {
614 global $langs, $user;
615 dol_syslog(__METHOD__ . " id=".$this->id.", ref=".$ref.", value=".$value.", notrigger=".$notrigger);
616 $error = 0;
617
618 // Clean parameters
619 $this->id = $this->id > 0 ? $this->id : 0;
620
621 // Check parameters
622 if (empty($this->id)) {
623 $this->errors[] = $langs->trans("ErrorFieldRequired", $langs->transnoentitiesnoconv("TechnicalID"));
624 $error++;
625 }
626 if ($error) {
627 dol_syslog(__METHOD__ . ' ' . $this->errorsToString(), LOG_ERR);
628 return -1;
629 }
630
631 $this->db->begin();
632
633 //Fetch current line from the database and then clone the object and set it in $oldcopy property
634 $this->line = new ProductAttributeValue($this->db);
635
636 // Position to use
637 $positiontouse = $position;
638 if ($positiontouse == -1) {
639 $positionmax = $this->line_max(0);
640 $positiontouse = $positionmax + 1;
641 }
642
643 $this->line->context = $this->context;
644 $this->line->fk_product_attribute = $this->id;
645 $this->line->ref = $ref;
646 $this->line->value = $value;
647 $this->line->position = $positiontouse;
648
649 $result = $this->line->create($user, $notrigger);
650
651 if ($result < 0) {
652 $this->error = $this->line->error;
653 $this->errors = $this->line->errors;
654 $this->db->rollback();
655 return -1;
656 } else {
657 $this->db->commit();
658 return $this->line->id;
659 }
660 }
661
662
672 public function updateLine($lineid, $ref, $value, $notrigger = 0)
673 {
674 global $user;
675
676 dol_syslog(__METHOD__ . " lineid=$lineid, ref=$ref, value=$value, notrigger=$notrigger");
677
678 // Clean parameters
679 $lineid = $lineid > 0 ? $lineid : 0;
680
681 $this->db->begin();
682
683 //Fetch current line from the database and then clone the object and set it in $oldcopy property
684 $this->line = new ProductAttributeValue($this->db);
685 $result = $this->line->fetch($lineid);
686 if ($result > 0) {
687 $this->line->oldcopy = clone $this->line;
688
689 $this->line->context = $this->context;
690 $this->line->ref = $ref;
691 $this->line->value = $value;
692
693 $result = $this->line->update($user, $notrigger);
694 }
695
696 if ($result < 0) {
697 $this->error = $this->line->error;
698 $this->errors = $this->line->errors;
699 $this->db->rollback();
700 return -1;
701 } else {
702 $this->db->commit();
703 return $result;
704 }
705 }
706
715 public function deleteLine(User $user, $lineid, $notrigger = 0)
716 {
717 dol_syslog(__METHOD__ . " lineid=$lineid, notrigger=$notrigger");
718
719 // Clean parameters
720 $lineid = $lineid > 0 ? $lineid : 0;
721
722 $this->db->begin();
723
724 //Fetch current line from the database
725 $this->line = new ProductAttributeValue($this->db);
726 $result = $this->line->fetch($lineid);
727 if ($result > 0) {
728 $this->line->context = $this->context;
729
730 $result = $this->line->delete($user, $notrigger);
731 }
732
733 if ($result < 0) {
734 $this->error = $this->line->error;
735 $this->errors = $this->line->errors;
736 $this->db->rollback();
737 return -1;
738 } else {
739 $this->db->commit();
740 return $result;
741 }
742 }
743
749 public function countChildValues()
750 {
751 global $langs;
752 $error = 0;
753 $count = 0;
754
755 // Clean parameters
756 $this->id = $this->id > 0 ? $this->id : 0;
757
758 // Check parameters
759 if (empty($this->id)) {
760 $this->errors[] = $langs->trans("ErrorFieldRequired", $langs->transnoentitiesnoconv("TechnicalID"));
761 $error++;
762 }
763 if ($error) {
764 dol_syslog(__METHOD__ . ' ' . $this->errorsToString(), LOG_ERR);
765 return -1;
766 }
767
768 $sql = "SELECT COUNT(*) AS count";
769 $sql .= " FROM " . MAIN_DB_PREFIX . $this->table_element_line;
770 $sql .= " WHERE " . $this->fk_element . " = " . ((int) $this->id);
771
772 dol_syslog(__METHOD__, LOG_DEBUG);
773 $resql = $this->db->query($sql);
774 if (!$resql) {
775 $this->errors[] = "Error " . $this->db->lasterror();
776 dol_syslog(__METHOD__ . ' ' . $this->errorsToString(), LOG_ERR);
777 return -1;
778 }
779
780 if ($obj = $this->db->fetch_object($resql)) {
781 $count = $obj->count;
782 }
783
784 return $count;
785 }
786
792 public function countChildProducts()
793 {
794 global $langs;
795 $error = 0;
796 $count = 0;
797
798 // Clean parameters
799 $this->id = ($this->id > 0) ? $this->id : 0;
800
801 // Check parameters
802 if (empty($this->id)) {
803 $this->errors[] = $langs->trans("ErrorFieldRequired", $langs->transnoentitiesnoconv("TechnicalID"));
804 $error++;
805 }
806 if ($error) {
807 dol_syslog(__METHOD__ . ' ' . $this->errorsToString(), LOG_ERR);
808 return -1;
809 }
810
811 $sql = "SELECT COUNT(*) AS count";
812 $sql .= " FROM " . MAIN_DB_PREFIX . "product_attribute_combination2val AS pac2v";
813 $sql .= " LEFT JOIN " . MAIN_DB_PREFIX . "product_attribute_combination AS pac ON pac2v.fk_prod_combination = pac.rowid";
814 $sql .= " WHERE pac2v.fk_prod_attr = " . ((int) $this->id);
815 $sql .= " AND pac.entity IN (" . getEntity('product') . ")";
816
817 dol_syslog(__METHOD__, LOG_DEBUG);
818 $resql = $this->db->query($sql);
819 if (!$resql) {
820 $this->errors[] = "Error " . $this->db->lasterror();
821 dol_syslog(__METHOD__ . ' ' . $this->errorsToString(), LOG_ERR);
822 return -1;
823 }
824
825 if ($obj = $this->db->fetch_object($resql)) {
826 $count = $obj->count;
827 }
828
829 return $count;
830 }
831
837 public function isUsed()
838 {
839 global $langs;
840 $error = 0;
841
842 // Clean parameters
843 $this->id = $this->id > 0 ? $this->id : 0;
844
845 // Check parameters
846 if (empty($this->id)) {
847 $this->errors[] = $langs->trans("ErrorFieldRequired", $langs->transnoentitiesnoconv("TechnicalID"));
848 $error++;
849 }
850 if ($error) {
851 dol_syslog(__METHOD__ . ' ' . $this->errorsToString(), LOG_ERR);
852 return -1;
853 }
854
855 $sql = "SELECT COUNT(*) AS nb FROM " . MAIN_DB_PREFIX . "product_attribute_combination2val WHERE fk_prod_attr = " . ((int) $this->id);
856
857 dol_syslog(__METHOD__, LOG_DEBUG);
858 $resql = $this->db->query($sql);
859 if (!$resql) {
860 $this->errors[] = "Error " . $this->db->lasterror();
861 return -1;
862 }
863
864 $used = 0;
865 if ($obj = $this->db->fetch_object($resql)) {
866 $used = $obj->nb;
867 }
868
869 return $used ? 1 : 0;
870 }
871
880 public function attributeOrder($renum = false, $rowidorder = 'ASC')
881 {
882 // Count number of attributes to reorder (according to choice $renum)
883 $nl = 0;
884 $sql = "SELECT count(rowid) FROM " . MAIN_DB_PREFIX . $this->table_element;
885 $sql .= " WHERE entity IN (" . getEntity('product') . ")";
886 if (!$renum) {
887 $sql .= " AND position = 0";
888 } else {
889 $sql .= " AND position <> 0";
890 }
891
892 dol_syslog(__METHOD__, LOG_DEBUG);
893 $resql = $this->db->query($sql);
894 if ($resql) {
895 $row = $this->db->fetch_row($resql);
896 $nl = $row[0];
897 } else {
898 dol_print_error($this->db);
899 }
900 if ($nl > 0) {
901 // The goal of this part is to reorder all attributes.
902 $rows = array();
903
904 // We first search all attributes
905 $sql = "SELECT rowid FROM " . MAIN_DB_PREFIX . $this->table_element;
906 $sql .= " WHERE entity IN (" . getEntity('product') . ")";
907 $sql .= " ORDER BY position ASC, rowid " . $rowidorder;
908
909 dol_syslog(__METHOD__ . " search all attributes", LOG_DEBUG);
910 $resql = $this->db->query($sql);
911 if ($resql) {
912 $i = 0;
913 $num = $this->db->num_rows($resql);
914 while ($i < $num) {
915 $row = $this->db->fetch_row($resql);
916 $rows[] = $row[0]; // Add attributes into array rows
917 $i++;
918 }
919
920 // Now we set a new number for each attributes
921 if (!empty($rows)) {
922 foreach ($rows as $key => $row) {
923 $this->updatePositionOfAttribute($row, ($key + 1));
924 }
925 }
926 } else {
927 dol_print_error($this->db);
928 }
929 }
930 return 1;
931 }
932
940 public function updatePositionOfAttribute($rowid, $position)
941 {
942 global $hookmanager;
943
944 $sql = "UPDATE " . MAIN_DB_PREFIX . $this->table_element . " SET position = " . ((int) $position);
945 $sql .= " WHERE rowid = " . ((int) $rowid);
946
947 dol_syslog(__METHOD__, LOG_DEBUG);
948 if (!$this->db->query($sql)) {
949 dol_print_error($this->db);
950 return -1;
951 } else {
952 $parameters = array('rowid' => $rowid, 'position' => $position);
953 $action = '';
954 $reshook = $hookmanager->executeHooks('afterPositionOfAttributeUpdate', $parameters, $this, $action);
955 return ($reshook >= 0 ? 1 : -1);
956 }
957 }
958
965 public function getPositionOfAttribute($rowid)
966 {
967 $sql = "SELECT position FROM " . MAIN_DB_PREFIX . $this->table_element;
968 $sql .= " WHERE entity IN (" . getEntity('product') . ")";
969
970 dol_syslog(__METHOD__, LOG_DEBUG);
971 $resql = $this->db->query($sql);
972 if ($resql) {
973 $row = $this->db->fetch_row($resql);
974 return $row[0];
975 }
976
977 return 0;
978 }
979
986 public function attributeMoveUp($rowid)
987 {
988 $this->attributeOrder(false, 'ASC');
989
990 // Get position of attribute
991 $position = $this->getPositionOfAttribute($rowid);
992
993 // Update position of attribute
994 $this->updateAttributePositionUp($rowid, $position);
995
996 return 1;
997 }
998
1005 public function attributeMoveDown($rowid)
1006 {
1007 $this->attributeOrder(false, 'ASC');
1008
1009 // Get position of line
1010 $position = $this->getPositionOfAttribute($rowid);
1011
1012 // Get max value for position
1013 $max = $this->getMaxAttributesPosition();
1014
1015 // Update position of attribute
1016 $this->updateAttributePositionDown($rowid, $position, $max);
1017
1018 return 1;
1019 }
1020
1028 public function updateAttributePositionUp($rowid, $position)
1029 {
1030 if ($position > 1) {
1031 $sql = "UPDATE " . MAIN_DB_PREFIX . $this->table_element . " SET position = " . ((int) $position);
1032 $sql .= " WHERE entity IN (" . getEntity('product') . ")";
1033 $sql .= " AND position = " . ((int) ($position - 1));
1034 if ($this->db->query($sql)) {
1035 $sql = "UPDATE " . MAIN_DB_PREFIX . $this->table_element . " SET position = " . ((int) ($position - 1));
1036 $sql .= " WHERE rowid = " . ((int) $rowid);
1037 if (!$this->db->query($sql)) {
1038 dol_print_error($this->db);
1039 }
1040 } else {
1041 dol_print_error($this->db);
1042 }
1043 }
1044 }
1045
1054 public function updateAttributePositionDown($rowid, $position, $max)
1055 {
1056 if ($position < $max) {
1057 $sql = "UPDATE " . MAIN_DB_PREFIX . $this->table_element . " SET position = " . ((int) $position);
1058 $sql .= " WHERE entity IN (" . getEntity('product') . ")";
1059 $sql .= " AND position = " . ((int) ($position + 1));
1060 if ($this->db->query($sql)) {
1061 $sql = "UPDATE " . MAIN_DB_PREFIX . $this->table_element . " SET position = " . ((int) ($position + 1));
1062 $sql .= " WHERE rowid = " . ((int) $rowid);
1063 if (!$this->db->query($sql)) {
1064 dol_print_error($this->db);
1065 }
1066 } else {
1067 dol_print_error($this->db);
1068 }
1069 }
1070 }
1071
1078 {
1079 // Search the last position of attributes
1080 $sql = "SELECT max(position) FROM " . MAIN_DB_PREFIX . $this->table_element;
1081 $sql .= " WHERE entity IN (" . getEntity('product') . ")";
1082
1083 dol_syslog(__METHOD__, LOG_DEBUG);
1084 $resql = $this->db->query($sql);
1085 if ($resql) {
1086 $row = $this->db->fetch_row($resql);
1087 return $row[0];
1088 }
1089
1090 return 0;
1091 }
1092
1099 public function attributesAjaxOrder($rows)
1100 {
1101 $num = count($rows);
1102 for ($i = 0; $i < $num; $i++) {
1103 $this->updatePositionOfAttribute($rows[$i], ($i + 1));
1104 }
1105 }
1106
1117 public function getNomUrl($withpicto = 0, $option = '', $notooltip = 0, $morecss = '', $save_lastsearch_value = -1)
1118 {
1119 global $conf, $langs, $hookmanager;
1120
1121 if (!empty($conf->dol_no_mouse_hover)) {
1122 $notooltip = 1; // Force disable tooltips
1123 }
1124
1125 $result = '';
1126
1127 $label = img_picto('', $this->picto) . ' <u>' . $langs->trans("ProductAttribute") . '</u>';
1128 if (isset($this->status)) {
1129 $label .= ' ' . $this->getLibStatut(5);
1130 }
1131 $label .= '<br>';
1132 $label .= '<b>' . $langs->trans('Ref') . ':</b> ' . $this->ref;
1133 if (!empty($this->label)) {
1134 $label .= '<br><b>' . $langs->trans('Label') . ':</b> ' . $this->label;
1135 }
1136
1137 $url = dol_buildpath('/variants/card.php', 1) . '?id=' . $this->id;
1138
1139 if ($option != 'nolink') {
1140 // Add param to save lastsearch_values or not
1141 $add_save_lastsearch_values = ($save_lastsearch_value == 1 ? 1 : 0);
1142 if ($save_lastsearch_value == -1 && isset($_SERVER["PHP_SELF"]) && preg_match('/list\.php/', $_SERVER["PHP_SELF"])) {
1143 $add_save_lastsearch_values = 1;
1144 }
1145 if ($url && $add_save_lastsearch_values) {
1146 $url .= '&save_lastsearch_values=1';
1147 }
1148 }
1149
1150 $linkclose = '';
1151 if (empty($notooltip)) {
1152 if (getDolGlobalString('MAIN_OPTIMIZEFORTEXTBROWSER')) {
1153 $label = $langs->trans("ShowProductAttribute");
1154 $linkclose .= ' alt="' . dol_escape_htmltag($label, 1) . '"';
1155 }
1156 $linkclose .= ' title="' . dol_escape_htmltag($label, 1) . '"';
1157 $linkclose .= ' class="classfortooltip' . ($morecss ? ' ' . $morecss : '') . '"';
1158 } else {
1159 $linkclose = ($morecss ? ' class="' . $morecss . '"' : '');
1160 }
1161
1162 if ($option == 'nolink' || empty($url)) {
1163 $linkstart = '<span';
1164 } else {
1165 $linkstart = '<a href="' . $url . '"';
1166 }
1167 $linkstart .= $linkclose . '>';
1168 if ($option == 'nolink' || empty($url)) {
1169 $linkend = '</span>';
1170 } else {
1171 $linkend = '</a>';
1172 }
1173
1174 $result .= $linkstart;
1175
1176 if (empty($this->showphoto_on_popup)) {
1177 if ($withpicto) {
1178 $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);
1179 }
1180 } else {
1181 if ($withpicto) {
1182 require_once DOL_DOCUMENT_ROOT . '/core/lib/files.lib.php';
1183
1184 list($class, $module) = explode('@', $this->picto);
1185 $upload_dir = $conf->$module->multidir_output[$conf->entity] . "/$class/" . dol_sanitizeFileName($this->ref);
1186 $filearray = dol_dir_list($upload_dir, "files");
1187 $filename = $filearray[0]['name'];
1188 if (!empty($filename)) {
1189 $pospoint = strpos($filearray[0]['name'], '.');
1190
1191 $pathtophoto = $class . '/' . $this->ref . '/thumbs/' . substr($filename, 0, $pospoint) . '_mini' . substr($filename, $pospoint);
1192 if (!getDolGlobalString(strtoupper($module . '_' . $class) . '_FORMATLISTPHOTOSASUSERS')) {
1193 $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>';
1194 } else {
1195 $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>';
1196 }
1197
1198 $result .= '</div>';
1199 } else {
1200 $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);
1201 }
1202 }
1203 }
1204
1205 if ($withpicto != 2) {
1206 $result .= $this->ref;
1207 }
1208
1209 $result .= $linkend;
1210 //if ($withpicto != 2) $result.=(($addlabel && $this->label) ? $sep . dol_trunc($this->label, ($addlabel > 1 ? $addlabel : 0)) : '');
1211
1212 global $action, $hookmanager;
1213 $hookmanager->initHooks(array('variantsdao'));
1214 $parameters = array('id' => $this->id, 'getnomurl' => $result);
1215 $reshook = $hookmanager->executeHooks('getNomUrl', $parameters, $this, $action); // Note that $action and $object may have been modified by some hooks
1216 if ($reshook > 0) {
1217 $result = $hookmanager->resPrint;
1218 } else {
1219 $result .= $hookmanager->resPrint;
1220 }
1221
1222 return $result;
1223 }
1224
1231 public function getLabelStatus($mode = 0)
1232 {
1233 return $this->LibStatut(0, $mode);
1234 }
1235
1242 public function getLibStatut($mode = 0)
1243 {
1244 return $this->LibStatut(0, $mode);
1245 }
1246
1247 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
1255 public function LibStatut($status, $mode = 1)
1256 {
1257 // phpcs:enable
1258 return '';
1259 }
1260
1261 // --------------------
1262 // TODO: All functions here must be redesigned and moved as they are not business functions but output functions
1263 // --------------------
1264
1265 /* This is to show add lines */
1266
1276 public function formAddObjectLine($dateSelector, $seller, $buyer, $defaulttpldir = '/variants/tpl')
1277 {
1278 global $conf, $user, $langs, $object, $hookmanager;
1279 global $form;
1280
1281 // Output template part (modules that overwrite templates must declare this into descriptor)
1282 // Use global variables + $dateSelector + $seller and $buyer
1283 // Note: This is deprecated. If you need to overwrite the tpl file, use instead the hook 'formAddObjectLine'.
1284 $dirtpls = array_merge($conf->modules_parts['tpl'], array($defaulttpldir));
1285 foreach ($dirtpls as $module => $reldir) {
1286 if (!empty($module)) {
1287 $tpl = dol_buildpath($reldir . '/productattributevalueline_create.tpl.php');
1288 } else {
1289 $tpl = DOL_DOCUMENT_ROOT . $reldir . '/productattributevalueline_create.tpl.php';
1290 }
1291
1292 if (empty($conf->file->strict_mode)) {
1293 $res = @include $tpl;
1294 } else {
1295 $res = include $tpl; // for debug
1296 }
1297 if ($res) {
1298 break;
1299 }
1300 }
1301 }
1302
1303 /* This is to show array of line of details */
1304
1320 public function printObjectLines($action, $seller, $buyer, $selected = 0, $dateSelector = 0, $defaulttpldir = '/variants/tpl', $addcreateline = 0)
1321 {
1322 global $conf, $hookmanager, $langs, $user, $form, $object;
1323 global $mysoc;
1324 // TODO We should not use global var for this
1325 global $disableedit, $disablemove, $disableremove;
1326
1327 $num = count($this->lines);
1328
1329 $parameters = array('num' => $num, 'selected' => $selected, 'table_element_line' => $this->table_element_line);
1330 $reshook = $hookmanager->executeHooks('printObjectLineTitle', $parameters, $this, $action); // Note that $action and $object may have been modified by some hooks
1331 if (empty($reshook)) {
1332 // Output template part (modules that overwrite templates must declare this into descriptor)
1333 // Use global variables + $dateSelector + $seller and $buyer
1334 // Note: This is deprecated. If you need to overwrite the tpl file, use instead the hook.
1335 $dirtpls = array_merge($conf->modules_parts['tpl'], array($defaulttpldir));
1336 foreach ($dirtpls as $module => $reldir) {
1337 if (!empty($module)) {
1338 $tpl = dol_buildpath($reldir . '/productattributevalueline_title.tpl.php');
1339 } else {
1340 $tpl = DOL_DOCUMENT_ROOT . $reldir . '/productattributevalueline_title.tpl.php';
1341 }
1342 if (empty($conf->file->strict_mode)) {
1343 $res = @include $tpl;
1344 } else {
1345 $res = include $tpl; // for debug
1346 }
1347 if ($res) {
1348 break;
1349 }
1350 }
1351 }
1352
1353
1354 if ($addcreateline) {
1355 // Form to add new line
1356 if ($action != 'selectlines') {
1357 if ($action != 'editline') {
1358 // Add products/services form
1359
1360 $parameters = array();
1361 $reshook = $hookmanager->executeHooks('formAddObjectLine', $parameters, $object, $action); // Note that $action and $object may have been modified by hook
1362 if ($reshook < 0) {
1363 setEventMessages($hookmanager->error, $hookmanager->errors, 'errors');
1364 }
1365 if (empty($reshook)) {
1366 $object->formAddObjectLine(1, $mysoc, $buyer);
1367 }
1368 }
1369 }
1370 }
1371
1372 $i = 0;
1373
1374 print "<!-- begin printObjectLines() -->\n";
1375 foreach ($this->lines as $line) {
1376 if (is_object($hookmanager)) { // Old code is commented on preceding line.
1377 $parameters = array('line' => $line, 'num' => $num, 'i' => $i, 'selected' => $selected, 'table_element_line' => $line->table_element);
1378 $reshook = $hookmanager->executeHooks('printObjectLine', $parameters, $this, $action); // Note that $action and $object may have been modified by some hooks
1379 }
1380 if (empty($reshook)) {
1381 $this->printObjectLine($action, $line, '', $num, $i, $dateSelector, $seller, $buyer, $selected, null, $defaulttpldir);
1382 }
1383
1384 $i++;
1385 }
1386 print "<!-- end printObjectLines() -->\n";
1387 }
1388
1406 public function printObjectLine($action, $line, $var, $num, $i, $dateSelector, $seller, $buyer, $selected = 0, $extrafields = null, $defaulttpldir = '/variants/tpl')
1407 {
1408 global $conf, $langs, $user, $object, $hookmanager;
1409 global $form;
1410 global $object_rights, $disableedit, $disablemove, $disableremove; // TODO We should not use global var for this !
1411
1412 $object_rights = $user->rights->variants;
1413
1414 // Line in view mode
1415 if ($action != 'editline' || $selected != $line->id) {
1416 // Output template part (modules that overwrite templates must declare this into descriptor)
1417 // Use global variables + $dateSelector + $seller and $buyer
1418 // Note: This is deprecated. If you need to overwrite the tpl file, use instead the hook printObjectLine and printObjectSubLine.
1419 $dirtpls = array_merge($conf->modules_parts['tpl'], array($defaulttpldir));
1420 foreach ($dirtpls as $module => $reldir) {
1421 if (!empty($module)) {
1422 $tpl = dol_buildpath($reldir . '/productattributevalueline_view.tpl.php');
1423 } else {
1424 $tpl = DOL_DOCUMENT_ROOT . $reldir . '/productattributevalueline_view.tpl.php';
1425 }
1426
1427 if (empty($conf->file->strict_mode)) {
1428 $res = @include $tpl;
1429 } else {
1430 $res = include $tpl; // for debug
1431 }
1432 if ($res) {
1433 break;
1434 }
1435 }
1436 }
1437
1438 // Line in update mode
1439 if ($action == 'editline' && $selected == $line->id) {
1440 // Output template part (modules that overwrite templates must declare this into descriptor)
1441 // Use global variables + $dateSelector + $seller and $buyer
1442 // Note: This is deprecated. If you need to overwrite the tpl file, use instead the hook printObjectLine and printObjectSubLine.
1443 $dirtpls = array_merge($conf->modules_parts['tpl'], array($defaulttpldir));
1444 foreach ($dirtpls as $module => $reldir) {
1445 if (!empty($module)) {
1446 $tpl = dol_buildpath($reldir . '/productattributevalueline_edit.tpl.php');
1447 } else {
1448 $tpl = DOL_DOCUMENT_ROOT . $reldir . '/productattributevalueline_edit.tpl.php';
1449 }
1450
1451 if (empty($conf->file->strict_mode)) {
1452 $res = @include $tpl;
1453 } else {
1454 $res = include $tpl; // for debug
1455 }
1456 if ($res) {
1457 break;
1458 }
1459 }
1460 }
1461 }
1462
1463 /* This is to show array of line of details of source object */
1464}
if( $user->socid > 0) if(! $user->hasRight('accounting', 'chartofaccount')) $object
Definition card.php:58
print $langs trans("AuditedSecurityEvents").'</strong >< span class="opacitymedium"></span >< br > status
Or an array listing all the potential status of the object: array: int of the status => translated la...
Definition security.php:626
print $object position
Definition edit.php:195
$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 Examples:
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 with all the ProductAttribute objects of a given entity.
updateAttributePositionUp($rowid, $position)
Update position of attribute (up)
isUsed()
Test if this attribute is 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()
Return the number of product variants 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 optionally 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($utf8_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:63
img_object($titlealt, $picto, $moreatt='', $pictoisfullpath=0, $srconly=0, $notitle=0)
Show a picto called object_picto (generic function)
img_picto($titlealt, $picto, $moreatt='', $pictoisfullpath=0, $srconly=0, $notitle=0, $alt='', $morecss='', $marginleftonlyshort=2)
Show picto whatever it's its name (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.
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.
dol_print_error($db=null, $error='', $errors=null)
Displays error message system with all the information to facilitate the diagnosis and the escalation...
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...