dolibarr  20.0.0-beta
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 
25 require_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 = '', $default = '0', $adddefault = 0, $mode = 0)
539  {
540  //phpcs:enable
541  print $this->selectMeasuringUnits($name, $measuring_style, $default, $adddefault, $mode);
542  }
543 
556  public function selectMeasuringUnits($name = 'measuring_units', $measuring_style = '', $default = '0', $adddefault = 0, $mode = 0, $morecss = 'maxwidth125')
557  {
558  global $langs, $conf, $mysoc, $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">'.($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 == $default) {
601  $return .= ' selected';
602  } elseif ($mode == 2 && $lines->scale == $default) {
603  $return .= ' selected';
604  } elseif ($mode == 0 && $lines->id == $default) {
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.
selectMeasuringUnits($name='measuring_units', $measuring_style='', $default='0', $adddefault=0, $mode=0, $morecss='maxwidth125')
Return a combo box with list of units Units labels are defined in llx_c_units.
loadLotStock($productIdArray=array())
Load in cache array list of lot available in stock from a given list of products.
select_measuring_units($name='measuring_units', $measuring_style='', $default='0', $adddefault=0, $mode=0)
Output a combo box with list of units Currently the units are not define in the DB.
get_parent_path($tab, $final_label='')
Return full path to current warehouse in $tab (recursive function)
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.
if(isModEnabled('invoice') && $user->hasRight('facture', 'lire')) if((isModEnabled('fournisseur') &&!getDolGlobalString('MAIN_USE_NEW_SUPPLIERMOD') && $user->hasRight("fournisseur", "facture", "lire"))||(isModEnabled('supplier_invoice') && $user->hasRight("supplier_invoice", "lire"))) if(isModEnabled('don') && $user->hasRight('don', 'lire')) if(isModEnabled('tax') && $user->hasRight('tax', 'charges', 'lire')) if(isModEnabled('invoice') &&isModEnabled('order') && $user->hasRight("commande", "lire") &&!getDolGlobalString('WORKFLOW_DISABLE_CREATE_INVOICE_FROM_ORDER')) $sql
Social contributions to pay.
Definition: index.php:745
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.
if(!defined( 'CSRFCHECK_WITH_TOKEN'))