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