dolibarr 21.0.0-alpha
html.formproduct.class.php
Go to the documentation of this file.
1<?php
2/* Copyright (C) 2008-2009 Laurent Destailleur <eldy@users.sourceforge.net>
3 * Copyright (C) 2015-2017 Francis Appels <francis.appels@yahoo.com>
4 * Copyright (C) 2024 Frédéric France <frederic.france@free.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
25require_once DOL_DOCUMENT_ROOT.'/product/stock/class/entrepot.class.php';
26
32{
36 public $db;
37
41 public $error = '';
42
43 // Cache arrays
44 public $cache_warehouses = array();
45 public $cache_lot = array();
46 public $cache_workstations = array();
47
48
54 public function __construct($db)
55 {
56 $this->db = $db;
57 }
58
59
77 public function loadWarehouses($fk_product = 0, $batch = '', $status = '', $sumStock = true, $exclude = array(), $stockMin = false, $orderBy = 'e.ref')
78 {
79 global $conf, $langs;
80
81 if (empty($fk_product) && count($this->cache_warehouses)) {
82 return 0; // Cache already loaded and we do not want a list with information specific to a product
83 }
84
85 $warehouseStatus = array();
86
87 if (preg_match('/warehouseclosed/', $status)) {
88 $warehouseStatus[] = Entrepot::STATUS_CLOSED;
89 }
90 if (preg_match('/warehouseopen/', $status)) {
91 $warehouseStatus[] = Entrepot::STATUS_OPEN_ALL;
92 }
93 if (preg_match('/warehouseinternal/', $status)) {
94 $warehouseStatus[] = Entrepot::STATUS_OPEN_INTERNAL;
95 }
96
97 $sql = "SELECT e.rowid, e.ref as label, e.description, e.fk_parent";
98 if (!empty($fk_product) && $fk_product > 0) {
99 if (!empty($batch)) {
100 $sql .= ", pb.qty as stock";
101 } else {
102 $sql .= ", ps.reel as stock";
103 }
104 } elseif ($sumStock) {
105 $sql .= ", sum(ps.reel) as stock";
106 }
107 $sql .= " FROM ".$this->db->prefix()."entrepot as e";
108 $sql .= " LEFT JOIN ".$this->db->prefix()."product_stock as ps on ps.fk_entrepot = e.rowid";
109 if (!empty($fk_product) && $fk_product > 0) {
110 $sql .= " AND ps.fk_product = ".((int) $fk_product);
111 if (!empty($batch)) {
112 $sql .= " LEFT JOIN ".$this->db->prefix()."product_batch as pb on pb.fk_product_stock = ps.rowid AND pb.batch = '".$this->db->escape($batch)."'";
113 }
114 }
115 $sql .= " WHERE e.entity IN (".getEntity('stock').")";
116 if (count($warehouseStatus)) {
117 $sql .= " AND e.statut IN (".$this->db->sanitize(implode(',', $warehouseStatus)).")";
118 } else {
119 $sql .= " AND e.statut = 1";
120 }
121
122 if (is_array($exclude) && !empty($exclude)) {
123 $sql .= ' AND e.rowid NOT IN('.$this->db->sanitize(implode(',', $exclude)).')';
124 }
125
126 // minimum stock
127 if ($stockMin !== false) {
128 if (!empty($fk_product) && $fk_product > 0) {
129 if (!empty($batch)) {
130 $sql .= " AND pb.qty > ".((float) $stockMin);
131 } else {
132 $sql .= " AND ps.reel > ".((float) $stockMin);
133 }
134 }
135 }
136
137 if ($sumStock && empty($fk_product)) {
138 $sql .= " GROUP BY e.rowid, e.ref, e.description, e.fk_parent";
139
140 // minimum stock
141 if ($stockMin !== false) {
142 $sql .= " HAVING sum(ps.reel) > ".((float) $stockMin);
143 }
144 }
145 $sql .= " ORDER BY ".$orderBy;
146
147 dol_syslog(get_class($this).'::loadWarehouses', LOG_DEBUG);
148 $resql = $this->db->query($sql);
149 if ($resql) {
150 $num = $this->db->num_rows($resql);
151 $i = 0;
152 while ($i < $num) {
153 $obj = $this->db->fetch_object($resql);
154 if ($sumStock) {
155 $obj->stock = price2num($obj->stock, 5);
156 }
157 $this->cache_warehouses[$obj->rowid]['id'] = $obj->rowid;
158 $this->cache_warehouses[$obj->rowid]['label'] = $obj->label;
159 $this->cache_warehouses[$obj->rowid]['parent_id'] = $obj->fk_parent;
160 $this->cache_warehouses[$obj->rowid]['description'] = $obj->description;
161 $this->cache_warehouses[$obj->rowid]['stock'] = $obj->stock;
162 $i++;
163 }
164
165 // Full label init
166 foreach ($this->cache_warehouses as $obj_rowid => $tab) {
167 $this->cache_warehouses[$obj_rowid]['full_label'] = $this->get_parent_path($tab);
168 }
169
170 return $num;
171 } else {
172 dol_print_error($this->db);
173 return -1;
174 }
175 }
176
187 public function loadWorkstations($fk_product = 0, $exclude = array(), $orderBy = 'w.ref')
188 {
189 global $conf, $langs;
190
191 if (empty($fk_product) && count($this->cache_workstations)) {
192 return 0; // Cache already loaded and we do not want a list with information specific to a product
193 }
194
195 $sql = "SELECT w.rowid, w.ref as ref, w.label as label, w.type, w.nb_operators_required,w.thm_operator_estimated,w.thm_machine_estimated";
196 $sql .= " FROM ".$this->db->prefix()."workstation_workstation as w";
197 $sql .= " WHERE 1 = 1";
198 if (!empty($fk_product) && $fk_product > 0) {
199 $sql .= " AND w.fk_product = ".((int) $fk_product);
200 }
201 $sql .= " AND w.entity IN (".getEntity('workstation').")";
202
203 if (is_array($exclude) && !empty($exclude)) {
204 $sql .= ' AND w.rowid NOT IN('.$this->db->sanitize(implode(',', $exclude)).')';
205 }
206
207 $sql .= " ORDER BY ".$orderBy;
208
209 dol_syslog(get_class($this).'::loadWorkstations', LOG_DEBUG);
210 $resql = $this->db->query($sql);
211 if ($resql) {
212 $num = $this->db->num_rows($resql);
213 $i = 0;
214 while ($i < $num) {
215 $obj = $this->db->fetch_object($resql);
216
217 $this->cache_workstations[$obj->rowid]['id'] = $obj->rowid;
218 $this->cache_workstations[$obj->rowid]['ref'] = $obj->ref;
219 $this->cache_workstations[$obj->rowid]['label'] = $obj->label;
220 $this->cache_workstations[$obj->rowid]['type'] = $obj->type;
221 $this->cache_workstations[$obj->rowid]['nb_operators_required'] = $obj->nb_operators_required;
222 $this->cache_workstations[$obj->rowid]['thm_operator_estimated'] = $obj->thm_operator_estimated;
223 $this->cache_workstations[$obj->rowid]['thm_machine_estimated'] = $obj->thm_machine_estimated;
224 $i++;
225 }
226
227 return $num;
228 } else {
229 dol_print_error($this->db);
230 return -1;
231 }
232 }
233
234 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
242 private function get_parent_path($tab, $final_label = '')
243 {
244 //phpcs:enable
245 if (empty($final_label)) {
246 $final_label = $tab['label'];
247 }
248
249 if (empty($tab['parent_id'])) {
250 return $final_label;
251 } else {
252 if (!empty($this->cache_warehouses[$tab['parent_id']])) {
253 $final_label = $this->cache_warehouses[$tab['parent_id']]['label'].' >> '.$final_label;
254 return $this->get_parent_path($this->cache_warehouses[$tab['parent_id']], $final_label);
255 }
256 }
257
258 return $final_label;
259 }
260
287 public function selectWarehouses($selected = '', $htmlname = 'idwarehouse', $filterstatus = '', $empty = 0, $disabled = 0, $fk_product = 0, $empty_label = '', $showstock = 0, $forcecombo = 0, $events = array(), $morecss = 'minwidth200', $exclude = array(), $showfullpath = 1, $stockMin = false, $orderBy = 'e.ref', $multiselect = 0)
288 {
289 global $conf, $langs, $user, $hookmanager;
290
291 dol_syslog(get_class($this)."::selectWarehouses " . (is_array($selected) ? 'selected is array' : $selected) . ", $htmlname, $filterstatus, $empty, $disabled, $fk_product, $empty_label, $showstock, $forcecombo, $morecss", LOG_DEBUG);
292
293 $out = '';
294 if (!getDolGlobalString('ENTREPOT_EXTRA_STATUS')) {
295 $filterstatus = '';
296 }
297 if (!empty($fk_product) && $fk_product > 0) {
298 $this->cache_warehouses = array();
299 }
300
301 $this->loadWarehouses($fk_product, '', $filterstatus, true, $exclude, $stockMin, $orderBy);
302 $nbofwarehouses = count($this->cache_warehouses);
303
304 if ($conf->use_javascript_ajax && !$forcecombo) {
305 include_once DOL_DOCUMENT_ROOT.'/core/lib/ajax.lib.php';
306 $comboenhancement = ajax_combobox($htmlname, $events);
307 $out .= $comboenhancement;
308 }
309
310 if (strpos($htmlname, 'search_') !== 0) {
311 if (empty($user->fk_warehouse) || $user->fk_warehouse == -1) {
312 if (is_scalar($selected) && ($selected == '-2' || $selected == 'ifone') && getDolGlobalString('MAIN_DEFAULT_WAREHOUSE')) {
313 $selected = getDolGlobalString('MAIN_DEFAULT_WAREHOUSE');
314 }
315 } else {
316 if (is_scalar($selected) && ($selected == '-2' || $selected == 'ifone') && getDolGlobalString('MAIN_DEFAULT_WAREHOUSE_USER')) {
317 $selected = $user->fk_warehouse;
318 }
319 }
320 }
321
322 $out .= '<select '.($multiselect ? 'multiple ' : '').'class="flat'.($morecss ? ' '.$morecss : '').'"'.($disabled ? ' disabled' : '');
323 $out .= ' id="'.$htmlname.'" name="'.($htmlname.($multiselect ? '[]' : '').($disabled ? '_disabled' : '')).'"';
324 //$out .= ' placeholder="todo"'; // placeholder for select2 must be added by setting the id+placeholder js param when calling select2
325 $out .= '>';
326 if ($empty) {
327 $out .= '<option value="-1">'.($empty_label ? $empty_label : '&nbsp;').'</option>';
328 }
329 foreach ($this->cache_warehouses as $id => $arraytypes) {
330 $label = '';
331 if ($showfullpath) {
332 $label .= $arraytypes['full_label'];
333 } else {
334 $label .= $arraytypes['label'];
335 }
336 if (($fk_product || ($showstock > 0)) && ($arraytypes['stock'] != 0 || ($showstock > 0))) {
337 if ($arraytypes['stock'] <= 0) {
338 $label .= ' <span class="text-warning">('.$langs->trans("Stock").':'.$arraytypes['stock'].')</span>';
339 } else {
340 $label .= ' <span class="opacitymedium">('.$langs->trans("Stock").':'.$arraytypes['stock'].')</span>';
341 }
342 }
343
344 $out .= '<option value="'.$id.'"';
345 if (is_array($selected)) {
346 if (in_array($id, $selected)) {
347 $out .= ' selected';
348 }
349 } else {
350 if ($selected == $id || (!empty($selected) && preg_match('/^ifone/', $selected) && $nbofwarehouses == 1)) {
351 $out .= ' selected';
352 }
353 }
354 $out .= ' data-html="'.dol_escape_htmltag($label).'"';
355 $out .= '>';
356 $out .= $label;
357 $out .= '</option>';
358 }
359 $out .= '</select>';
360 if ($disabled) {
361 $out .= '<input type="hidden" name="'.$htmlname.'" value="'.(($selected > 0) ? $selected : '').'">';
362 }
363
364 $parameters = array(
365 'selected' => $selected,
366 'htmlname' => $htmlname,
367 'filterstatus' => $filterstatus,
368 'empty' => $empty,
369 'disabled ' => $disabled,
370 'fk_product' => $fk_product,
371 'empty_label' => $empty_label,
372 'showstock' => $showstock,
373 'forcecombo' => $forcecombo,
374 'events' => $events,
375 'morecss' => $morecss,
376 'exclude' => $exclude,
377 'showfullpath' => $showfullpath,
378 'stockMin' => $stockMin,
379 'orderBy' => $orderBy
380 );
381
382 $reshook = $hookmanager->executeHooks('selectWarehouses', $parameters, $this);
383 if ($reshook > 0) {
384 $out = $hookmanager->resPrint;
385 } elseif ($reshook == 0) {
386 $out .= $hookmanager->resPrint;
387 }
388
389 return $out;
390 }
391
411 public function selectWorkstations($selected = '', $htmlname = 'idworkstations', $empty = 0, $disabled = 0, $fk_product = 0, $empty_label = '', $forcecombo = 0, $events = array(), $morecss = 'minwidth200', $exclude = array(), $showfullpath = 1, $orderBy = 'e.ref')
412 {
413 global $conf, $langs, $user, $hookmanager;
414
415 dol_syslog(get_class($this)."::selectWorkstations $selected, $htmlname, $empty, $disabled, $fk_product, $empty_label, $forcecombo, $morecss", LOG_DEBUG);
416
417 $filterstatus='';
418 $out = '';
419 if (!empty($fk_product) && $fk_product > 0) {
420 $this->cache_workstations = array();
421 }
422
423 $this->loadWorkstations($fk_product);
424 $nbofworkstations = count($this->cache_workstations);
425
426 if ($conf->use_javascript_ajax && !$forcecombo) {
427 include_once DOL_DOCUMENT_ROOT.'/core/lib/ajax.lib.php';
428 $comboenhancement = ajax_combobox($htmlname, $events);
429 $out .= $comboenhancement;
430 }
431
432 if (strpos($htmlname, 'search_') !== 0) {
433 if (empty($user->fk_workstation) || $user->fk_workstation == -1) {
434 if (($selected == '-2' || $selected == 'ifone') && getDolGlobalString('MAIN_DEFAULT_WORKSTATION')) {
435 $selected = getDolGlobalString('MAIN_DEFAULT_WORKSTATION');
436 }
437 } else {
438 if (($selected == '-2' || $selected == 'ifone') && getDolGlobalString('MAIN_DEFAULT_WORKSTATION')) {
439 $selected = $user->fk_workstation;
440 }
441 }
442 }
443
444 $out .= '<select class="flat'.($morecss ? ' '.$morecss : '').'"'.($disabled ? ' disabled' : '').' id="'.$htmlname.'" name="'.($htmlname.($disabled ? '_disabled' : '')).'">';
445 if ($empty) {
446 $out .= '<option value="-1">'.($empty_label ? $empty_label : '&nbsp;').'</option>';
447 }
448 foreach ($this->cache_workstations as $id => $arraytypes) {
449 $label = $arraytypes['label'];
450
451 $out .= '<option value="'.$id.'"';
452 if ($selected == $id || (preg_match('/^ifone/', $selected) && $nbofworkstations == 1)) {
453 $out .= ' selected';
454 }
455 $out .= ' data-html="'.dol_escape_htmltag($label).'"';
456 $out .= '>';
457 $out .= $label;
458 $out .= '</option>';
459 }
460 $out .= '</select>';
461 if ($disabled) {
462 $out .= '<input type="hidden" name="'.$htmlname.'" value="'.(($selected > 0) ? $selected : '').'">';
463 }
464
465 $parameters = array(
466 'selected' => $selected,
467 'htmlname' => $htmlname,
468 'filterstatus' => $filterstatus,
469 'empty' => $empty,
470 'disabled ' => $disabled,
471 'fk_product' => $fk_product,
472 'empty_label' => $empty_label,
473 'forcecombo' => $forcecombo,
474 'events' => $events,
475 'morecss' => $morecss,
476 'exclude' => $exclude,
477 'showfullpath' => $showfullpath,
478 'orderBy' => $orderBy
479 );
480
481 $reshook = $hookmanager->executeHooks('selectWorkstations', $parameters, $this);
482 if ($reshook > 0) {
483 $out = $hookmanager->resPrint;
484 } elseif ($reshook == 0) {
485 $out .= $hookmanager->resPrint;
486 }
487
488 return $out;
489 }
490
500 public function formSelectWarehouses($page, $selected = '', $htmlname = 'warehouse_id', $addempty = 0)
501 {
502 global $langs;
503 if ($htmlname != "none") {
504 print '<form method="POST" action="'.$page.'">';
505 print '<input type="hidden" name="action" value="setwarehouse">';
506 print '<input type="hidden" name="token" value="'.newToken().'">';
507 print '<table class="nobordernopadding">';
508 print '<tr><td>';
509 print $this->selectWarehouses($selected, $htmlname, '', $addempty);
510 print '</td>';
511 print '<td class="left"><input type="submit" class="button smallpaddingimp" value="'.$langs->trans("Modify").'"></td>';
512 print '</tr></table></form>';
513 } else {
514 if ($selected) {
515 require_once DOL_DOCUMENT_ROOT.'/product/stock/class/entrepot.class.php';
516 $warehousestatic = new Entrepot($this->db);
517 $warehousestatic->fetch($selected);
518 print $warehousestatic->getNomUrl();
519 } else {
520 print "&nbsp;";
521 }
522 }
523 }
524
525 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
538 public function select_measuring_units($name = 'measuring_units', $measuring_style = '', $selected = '0', $adddefault = 0, $mode = 0)
539 {
540 //phpcs:enable
541 print $this->selectMeasuringUnits($name, $measuring_style, $selected, $adddefault, $mode);
542 }
543
556 public function selectMeasuringUnits($name = 'measuring_units', $measuring_style = '', $selected = '0', $adddefault = 0, $mode = 0, $morecss = 'minwidth75 maxwidth125')
557 {
558 global $langs, $db;
559
560 $langs->load("other");
561
562 $return = '';
563
564 // TODO Use a cache
565 require_once DOL_DOCUMENT_ROOT.'/core/class/cunits.class.php';
566 $measuringUnits = new CUnits($db);
567
568 $filter = array();
569 $filter['t.active'] = 1;
570 if ($measuring_style) {
571 $filter['t.unit_type'] = $measuring_style;
572 }
573
574 $result = $measuringUnits->fetchAll(
575 '',
576 '',
577 0,
578 0,
579 $filter
580 );
581 if ($result < 0) {
582 dol_print_error($db);
583 return -1;
584 } else {
585 $return .= '<select class="flat'.($morecss ? ' '.$morecss : '').'" name="'.$name.'" id="'.$name.'">';
586 if ($adddefault || $adddefault === '') {
587 $return .= '<option value="0"'.($selected === '0' ? ' selected' : '').'>'.($adddefault ? '('.$langs->trans("Default").')' : '').'</option>';
588 }
589
590 foreach ($measuringUnits->records as $lines) {
591 $return .= '<option value="';
592 if ($mode == 1) {
593 $return .= $lines->short_label;
594 } elseif ($mode == 2) {
595 $return .= $lines->scale;
596 } else {
597 $return .= $lines->id;
598 }
599 $return .= '"';
600 if ($mode == 1 && $lines->short_label == $selected) {
601 $return .= ' selected';
602 } elseif ($mode == 2 && $lines->scale == $selected) {
603 $return .= ' selected';
604 } elseif ($mode == 0 && $lines->id == $selected) {
605 $return .= ' selected';
606 }
607 $return .= '>';
608 if ($measuring_style == 'time') {
609 $return .= $langs->trans(ucfirst($lines->label));
610 } else {
611 $return .= $langs->trans($lines->label);
612 }
613 $return .= '</option>';
614 }
615 $return .= '</select>';
616 }
617
618 $return .= ajax_combobox($name);
619
620 return $return;
621 }
622
633 public function selectProductNature($name = 'finished', $selected = '', $mode = 0, $showempty = 1)
634 {
635 global $langs, $db;
636
637 $langs->load('products');
638
639 $return = '';
640
641 // TODO Use a cache
642 require_once DOL_DOCUMENT_ROOT.'/core/class/cproductnature.class.php';
643 $productNature = new CProductNature($db);
644
645 $filter = array();
646 $filter['t.active'] = 1;
647
648 $result = $productNature->fetchAll('', '', 0, 0, $filter);
649
650 if ($result < 0) {
651 dol_print_error($db);
652 return -1;
653 } else {
654 $return .= '<select class="flat" name="'.$name.'" id="'.$name.'">';
655 if ($showempty || ($selected == '' || $selected == '-1')) {
656 $return .= '<option value="-1"';
657 if ($selected == '' || $selected == '-1') {
658 $return .= ' selected';
659 }
660 $return .= '></option>';
661 }
662 if (!empty($productNature->records) && is_array($productNature->records)) {
663 foreach ($productNature->records as $lines) {
664 $return .= '<option value="';
665 if ($mode == 1) {
666 $return .= $lines->label;
667 } else {
668 $return .= $lines->code;
669 }
670
671 $return .= '"';
672
673 if ($mode == 1 && $lines->label == $selected) {
674 $return .= ' selected';
675 } elseif ($lines->code == $selected) {
676 $return .= ' selected';
677 }
678
679 $return .= '>';
680 $return .= $langs->trans($lines->label);
681 $return .= '</option>';
682 }
683 }
684 $return .= '</select>';
685 }
686
687 $return .= ajax_combobox($name);
688
689 return $return;
690 }
691
710 public function selectLotStock($selected = '', $htmlname = 'batch_id', $filterstatus = '', $empty = 0, $disabled = 0, $fk_product = 0, $fk_entrepot = 0, $objectLines = array(), $empty_label = '', $forcecombo = 0, $events = array(), $morecss = 'minwidth200')
711 {
712 global $conf, $langs;
713
714 dol_syslog(get_class($this)."::selectLotStock $selected, $htmlname, $filterstatus, $empty, $disabled, $fk_product, $fk_entrepot, $empty_label, $forcecombo, $morecss", LOG_DEBUG);
715
716 $out = '';
717 $productIdArray = array();
718 if (!is_array($objectLines) || !count($objectLines)) {
719 if (!empty($fk_product) && $fk_product > 0) {
720 $productIdArray[] = (int) $fk_product;
721 }
722 } else {
723 foreach ($objectLines as $line) {
724 if ($line->fk_product) {
725 $productIdArray[] = $line->fk_product;
726 }
727 }
728 }
729
730 $nboflot = $this->loadLotStock($productIdArray);
731
732 if ($conf->use_javascript_ajax && !$forcecombo) {
733 include_once DOL_DOCUMENT_ROOT.'/core/lib/ajax.lib.php';
734 $comboenhancement = ajax_combobox($htmlname, $events);
735 $out .= $comboenhancement;
736 }
737
738 $out .= '<select class="flat'.($morecss ? ' '.$morecss : '').'"'.($disabled ? ' disabled' : '').' id="'.$htmlname.'" name="'.($htmlname.($disabled ? '_disabled' : '')).'">';
739 if ($empty) {
740 $out .= '<option value="-1">'.($empty_label ? $empty_label : '&nbsp;').'</option>';
741 }
742 if (!empty($fk_product) && $fk_product > 0) {
743 $productIdArray = array((int) $fk_product); // only show lot stock for product
744 } else {
745 foreach ($this->cache_lot as $key => $value) {
746 $productIdArray[] = $key;
747 }
748 }
749
750 foreach ($productIdArray as $productId) {
751 foreach ($this->cache_lot[$productId] as $id => $arraytypes) {
752 if (empty($fk_entrepot) || $fk_entrepot == $arraytypes['entrepot_id']) {
753 $label = $arraytypes['entrepot_label'].' - ';
754 $label .= $arraytypes['batch'];
755 if ($arraytypes['qty'] <= 0) {
756 $label .= ' <span class=\'text-warning\'>('.$langs->trans("Stock").' '.$arraytypes['qty'].')</span>';
757 } else {
758 $label .= ' <span class=\'opacitymedium\'>('.$langs->trans("Stock").' '.$arraytypes['qty'].')</span>';
759 }
760
761 $out .= '<option value="'.$id.'"';
762
763 if ($selected == $id || ($selected == 'ifone' && $nboflot == 1)) {
764 $out .= ' selected';
765 }
766 $out .= ' data-html="'.dol_escape_htmltag($label).'"';
767 $out .= '>';
768 $out .= $label;
769 $out .= '</option>';
770 }
771 }
772 }
773 $out .= '</select>';
774 if ($disabled) {
775 $out .= '<input type="hidden" name="'.$htmlname.'" value="'.(($selected > 0) ? $selected : '').'">';
776 }
777
778 return $out;
779 }
780
781
782
793 public function selectLotDataList($htmlname = 'batch_id', $empty = 0, $fk_product = 0, $fk_entrepot = 0, $objectLines = array())
794 {
795 global $langs, $hookmanager;
796
797 dol_syslog(get_class($this)."::selectLotDataList $htmlname, $empty, $fk_product, $fk_entrepot", LOG_DEBUG);
798
799 $out = '';
800 $productIdArray = array();
801 if (!is_array($objectLines) || !count($objectLines)) {
802 if (!empty($fk_product) && $fk_product > 0) {
803 $productIdArray[] = (int) $fk_product;
804 }
805 } else {
806 foreach ($objectLines as $line) {
807 if ($line->fk_product) {
808 $productIdArray[] = $line->fk_product;
809 }
810 }
811 }
812
813 $nboflot = $this->loadLotStock($productIdArray);
814
815 if (!empty($fk_product) && $fk_product > 0) {
816 $productIdArray = array((int) $fk_product); // only show lot stock for product
817 } else {
818 foreach ($this->cache_lot as $key => $value) {
819 $productIdArray[] = $key;
820 }
821 }
822
823 if (empty($hookmanager)) {
824 include_once DOL_DOCUMENT_ROOT . '/core/class/hookmanager.class.php';
825 $hookmanager = new HookManager($this->db);
826 }
827 $hookmanager->initHooks(array('productdao'));
828 $parameters = array('productIdArray' => $productIdArray, 'htmlname' => $htmlname);
829 $reshook = $hookmanager->executeHooks('selectLotDataList', $parameters, $this);
830 if ($reshook < 0) {
831 return $hookmanager->error;
832 } elseif ($reshook > 0) {
833 return $hookmanager->resPrint;
834 } else {
835 $out .= $hookmanager->resPrint;
836 }
837
838 $out .= '<datalist id="'.$htmlname.'" >';
839 foreach ($productIdArray as $productId) {
840 if (array_key_exists($productId, $this->cache_lot)) {
841 foreach ($this->cache_lot[$productId] as $id => $arraytypes) {
842 if (empty($fk_entrepot) || $fk_entrepot == $arraytypes['entrepot_id']) {
843 $label = $arraytypes['entrepot_label'] . ' - ';
844 $label .= $arraytypes['batch'];
845 $out .= '<option data-warehouse="'.dol_escape_htmltag($label).'" value="' . $arraytypes['batch'] . '">(' . $langs->trans('Stock Total') . ': ' . $arraytypes['qty'] . ')</option>';
846 }
847 }
848 }
849 }
850 $out .= '</datalist>';
851
852 return $out;
853 }
854
855
863 private function loadLotStock($productIdArray = array())
864 {
865 global $conf, $langs;
866
867 $cacheLoaded = false;
868 if (empty($productIdArray)) {
869 // only Load lot stock for given products
870 $this->cache_lot = array();
871 return 0;
872 }
873 if (count($productIdArray) && count($this->cache_lot)) {
874 // check cache already loaded for product id's
875 foreach ($productIdArray as $productId) {
876 $cacheLoaded = !empty($this->cache_lot[$productId]) ? true : false;
877 }
878 }
879 if ($cacheLoaded) {
880 return count($this->cache_lot);
881 } else {
882 // clear cache
883 $this->cache_lot = array();
884 $productIdList = implode(',', $productIdArray);
885
886 $batch_count = 0;
887 global $hookmanager;
888 if (empty($hookmanager)) {
889 include_once DOL_DOCUMENT_ROOT . '/core/class/hookmanager.class.php';
890 $hookmanager = new HookManager($this->db);
891 }
892 $hookmanager->initHooks(array('productdao'));
893 $parameters = array('productIdList' => $productIdList);
894 $reshook = $hookmanager->executeHooks('loadLotStock', $parameters, $this);
895 if ($reshook < 0) {
896 $this->error = $hookmanager->error;
897 return -1;
898 }
899 if (!empty($hookmanager->resArray['batch_list']) && is_array($hookmanager->resArray['batch_list'])) {
900 $this->cache_lot = $hookmanager->resArray['batch_list'];
901 $batch_count = (int) $hookmanager->resArray['batch_count'];
902 }
903 if ($reshook > 0) {
904 return $batch_count;
905 }
906
907 $sql = "SELECT pb.batch, pb.rowid, ps.fk_entrepot, pb.qty, e.ref as label, ps.fk_product";
908 $sql .= " FROM ".$this->db->prefix()."product_batch as pb";
909 $sql .= " LEFT JOIN ".$this->db->prefix()."product_stock as ps on ps.rowid = pb.fk_product_stock";
910 $sql .= " LEFT JOIN ".$this->db->prefix()."entrepot as e on e.rowid = ps.fk_entrepot AND e.entity IN (".getEntity('stock').")";
911 if (!empty($productIdList)) {
912 $sql .= " WHERE ps.fk_product IN (".$this->db->sanitize($productIdList).")";
913 }
914 $sql .= " ORDER BY e.ref, pb.batch";
915
916 dol_syslog(get_class($this).'::loadLotStock', LOG_DEBUG);
917 $resql = $this->db->query($sql);
918 if ($resql) {
919 $num = $this->db->num_rows($resql);
920 $i = 0;
921 while ($i < $num) {
922 $obj = $this->db->fetch_object($resql);
923 $this->cache_lot[$obj->fk_product][$obj->rowid]['id'] = $obj->rowid;
924 $this->cache_lot[$obj->fk_product][$obj->rowid]['batch'] = $obj->batch;
925 $this->cache_lot[$obj->fk_product][$obj->rowid]['entrepot_id'] = $obj->fk_entrepot;
926 $this->cache_lot[$obj->fk_product][$obj->rowid]['entrepot_label'] = $obj->label;
927 $this->cache_lot[$obj->fk_product][$obj->rowid]['qty'] = $obj->qty;
928 $i++;
929 }
930
931 return $batch_count + $num;
932 } else {
933 dol_print_error($this->db);
934 return -1;
935 }
936 }
937 }
938}
ajax_combobox($htmlname, $events=array(), $minLengthToAutocomplete=0, $forcefocus=0, $widthTypeOfAutocomplete='resolve', $idforemptyvalue='-1', $morecss='')
Convert a html select field into an ajax combobox.
Definition ajax.lib.php:456
Class of dictionary of nature of product (used by imports)
Class of dictionary type of thirdparty (used by imports)
Class to manage warehouses.
const STATUS_OPEN_INTERNAL
Warehouse open and only operations for stock transfers/corrections allowed (not for customer shipping...
const STATUS_OPEN_ALL
Warehouse open and any operations are allowed (customer shipping, supplier dispatch,...
const STATUS_CLOSED
Warehouse closed, inactive.
Class with static methods for building HTML components related to products Only components common to ...
selectWarehouses($selected='', $htmlname='idwarehouse', $filterstatus='', $empty=0, $disabled=0, $fk_product=0, $empty_label='', $showstock=0, $forcecombo=0, $events=array(), $morecss='minwidth200', $exclude=array(), $showfullpath=1, $stockMin=false, $orderBy='e.ref', $multiselect=0)
Return list of warehouses.
loadWorkstations($fk_product=0, $exclude=array(), $orderBy='w.ref')
Load in cache array list of workstations If fk_product is not 0, we do not use cache.
selectWorkstations($selected='', $htmlname='idworkstations', $empty=0, $disabled=0, $fk_product=0, $empty_label='', $forcecombo=0, $events=array(), $morecss='minwidth200', $exclude=array(), $showfullpath=1, $orderBy='e.ref')
Return list of workstations.
selectLotDataList($htmlname='batch_id', $empty=0, $fk_product=0, $fk_entrepot=0, $objectLines=array())
Return list of lot numbers (stock from product_batch) for product and warehouse.
selectProductNature($name='finished', $selected='', $mode=0, $showempty=1)
Return a combo box with list of units NAture of product labels are defined in llx_c_product_nature.
selectLotStock($selected='', $htmlname='batch_id', $filterstatus='', $empty=0, $disabled=0, $fk_product=0, $fk_entrepot=0, $objectLines=array(), $empty_label='', $forcecombo=0, $events=array(), $morecss='minwidth200')
Return list of lot numbers (stock from product_batch) with stock location and stock qty.
loadLotStock($productIdArray=array())
Load in cache array list of lot available in stock from a given list of products.
get_parent_path($tab, $final_label='')
Return full path to current warehouse in $tab (recursive function)
select_measuring_units($name='measuring_units', $measuring_style='', $selected='0', $adddefault=0, $mode=0)
Output a combo box with list of units Currently the units are not define in the DB.
selectMeasuringUnits($name='measuring_units', $measuring_style='', $selected='0', $adddefault=0, $mode=0, $morecss='minwidth75 maxwidth125')
Return a combo box with list of units Units labels are defined in llx_c_units.
formSelectWarehouses($page, $selected='', $htmlname='warehouse_id', $addempty=0)
Display form to select warehouse.
__construct($db)
Constructor.
loadWarehouses($fk_product=0, $batch='', $status='', $sumStock=true, $exclude=array(), $stockMin=false, $orderBy='e.ref')
Load in cache array list of warehouses If fk_product is not 0, we do not use cache.
Class to manage hooks.
price2num($amount, $rounding='', $option=0)
Function that return a number with universal decimal format (decimal separator is '.
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.