dolibarr 21.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 * 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
26require_once DOL_DOCUMENT_ROOT.'/product/stock/class/entrepot.class.php';
27
33{
37 public $db;
38
42 public $error = '';
43
44 // Cache arrays
48 public $cache_warehouses = array();
52 public $cache_lot = array();
56 public $cache_workstations = array();
57
58
64 public function __construct($db)
65 {
66 $this->db = $db;
67 }
68
69
87 public function loadWarehouses($fk_product = 0, $batch = '', $status = '', $sumStock = true, $exclude = array(), $stockMin = false, $orderBy = 'e.ref')
88 {
89 global $conf, $langs;
90
91 if (empty($fk_product) && count($this->cache_warehouses)) {
92 return 0; // Cache already loaded and we do not want a list with information specific to a product
93 }
94
95 $warehouseStatus = array();
96
97 if (preg_match('/warehouseclosed/', $status)) {
98 $warehouseStatus[] = Entrepot::STATUS_CLOSED;
99 }
100 if (preg_match('/warehouseopen/', $status)) {
101 $warehouseStatus[] = Entrepot::STATUS_OPEN_ALL;
102 }
103 if (preg_match('/warehouseinternal/', $status)) {
104 $warehouseStatus[] = Entrepot::STATUS_OPEN_INTERNAL;
105 }
106
107 $sql = "SELECT e.rowid, e.ref as label, e.description, e.fk_parent";
108 if (!empty($fk_product) && $fk_product > 0) {
109 if (!empty($batch)) {
110 $sql .= ", pb.qty as stock";
111 } else {
112 $sql .= ", ps.reel as stock";
113 }
114 } elseif ($sumStock) {
115 $sql .= ", sum(ps.reel) as stock";
116 }
117 $sql .= " FROM ".$this->db->prefix()."entrepot as e";
118 $sql .= " LEFT JOIN ".$this->db->prefix()."product_stock as ps on ps.fk_entrepot = e.rowid";
119 if (!empty($fk_product) && $fk_product > 0) {
120 $sql .= " AND ps.fk_product = ".((int) $fk_product);
121 if (!empty($batch)) {
122 $sql .= " LEFT JOIN ".$this->db->prefix()."product_batch as pb on pb.fk_product_stock = ps.rowid AND pb.batch = '".$this->db->escape($batch)."'";
123 }
124 }
125 $sql .= " WHERE e.entity IN (".getEntity('stock').")";
126 if (count($warehouseStatus)) {
127 $sql .= " AND e.statut IN (".$this->db->sanitize(implode(',', $warehouseStatus)).")";
128 } else {
129 $sql .= " AND e.statut = 1";
130 }
131
132 if (is_array($exclude) && !empty($exclude)) {
133 $sql .= ' AND e.rowid NOT IN('.$this->db->sanitize(implode(',', $exclude)).')';
134 }
135
136 // minimum stock
137 if ($stockMin !== false) {
138 if (!empty($fk_product) && $fk_product > 0) {
139 if (!empty($batch)) {
140 $sql .= " AND pb.qty > ".((float) $stockMin);
141 } else {
142 $sql .= " AND ps.reel > ".((float) $stockMin);
143 }
144 }
145 }
146
147 if ($sumStock && empty($fk_product)) {
148 $sql .= " GROUP BY e.rowid, e.ref, e.description, e.fk_parent";
149
150 // minimum stock
151 if ($stockMin !== false) {
152 $sql .= " HAVING sum(ps.reel) > ".((float) $stockMin);
153 }
154 }
155 $sql .= " ORDER BY ".$orderBy;
156
157 dol_syslog(get_class($this).'::loadWarehouses', LOG_DEBUG);
158 $resql = $this->db->query($sql);
159 if ($resql) {
160 $num = $this->db->num_rows($resql);
161 $i = 0;
162 while ($i < $num) {
163 $obj = $this->db->fetch_object($resql);
164 if ($sumStock) {
165 $obj->stock = price2num($obj->stock, 5);
166 }
167 $this->cache_warehouses[$obj->rowid]['id'] = $obj->rowid;
168 $this->cache_warehouses[$obj->rowid]['label'] = $obj->label;
169 $this->cache_warehouses[$obj->rowid]['parent_id'] = $obj->fk_parent;
170 $this->cache_warehouses[$obj->rowid]['description'] = $obj->description;
171 $this->cache_warehouses[$obj->rowid]['stock'] = $obj->stock;
172 $i++;
173 }
174
175 // Full label init
176 foreach ($this->cache_warehouses as $obj_rowid => $tab) {
177 $this->cache_warehouses[$obj_rowid]['full_label'] = $this->get_parent_path($tab);
178 }
179
180 return $num;
181 } else {
182 dol_print_error($this->db);
183 return -1;
184 }
185 }
186
197 public function loadWorkstations($fk_product = 0, $exclude = array(), $orderBy = 'w.ref')
198 {
199 global $conf, $langs;
200
201 if (empty($fk_product) && count($this->cache_workstations)) {
202 return 0; // Cache already loaded and we do not want a list with information specific to a product
203 }
204
205 $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";
206 $sql .= " FROM ".$this->db->prefix()."workstation_workstation as w";
207 $sql .= " WHERE 1 = 1";
208 if (!empty($fk_product) && $fk_product > 0) {
209 $sql .= " AND w.fk_product = ".((int) $fk_product);
210 }
211 $sql .= " AND w.entity IN (".getEntity('workstation').")";
212
213 if (is_array($exclude) && !empty($exclude)) {
214 $sql .= ' AND w.rowid NOT IN('.$this->db->sanitize(implode(',', $exclude)).')';
215 }
216
217 $sql .= " ORDER BY ".$orderBy;
218
219 dol_syslog(get_class($this).'::loadWorkstations', LOG_DEBUG);
220 $resql = $this->db->query($sql);
221 if ($resql) {
222 $num = $this->db->num_rows($resql);
223 $i = 0;
224 while ($i < $num) {
225 $obj = $this->db->fetch_object($resql);
226
227 $this->cache_workstations[$obj->rowid]['id'] = $obj->rowid;
228 $this->cache_workstations[$obj->rowid]['ref'] = $obj->ref;
229 $this->cache_workstations[$obj->rowid]['label'] = $obj->label;
230 $this->cache_workstations[$obj->rowid]['type'] = $obj->type;
231 $this->cache_workstations[$obj->rowid]['nb_operators_required'] = $obj->nb_operators_required;
232 $this->cache_workstations[$obj->rowid]['thm_operator_estimated'] = $obj->thm_operator_estimated;
233 $this->cache_workstations[$obj->rowid]['thm_machine_estimated'] = $obj->thm_machine_estimated;
234 $i++;
235 }
236
237 return $num;
238 } else {
239 dol_print_error($this->db);
240 return -1;
241 }
242 }
243
244 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
255 private function get_parent_path($tab, $final_label = '')
256 {
257 //phpcs:enable
258 if (empty($final_label)) {
259 $final_label = $tab['label'];
260 }
261
262 if (empty($tab['parent_id']) || getDolGlobalInt('MAIN_WAREHOUSE_LIST_DISPLAY_MODE') === 1) {
263 return $final_label;
264 } else {
265 if (!empty($this->cache_warehouses[$tab['parent_id']])) {
266 if (getDolGlobalInt('MAIN_WAREHOUSE_LIST_DISPLAY_MODE') !== 2 || (getDolGlobalInt('MAIN_WAREHOUSE_LIST_DISPLAY_MODE') === 2 && empty($this->cache_warehouses[$tab['parent_id']]['parent_id']))) {
267 $final_label = $this->cache_warehouses[$tab['parent_id']]['label'] . ' >> ' . $final_label;
268 }
269
270 return $this->get_parent_path($this->cache_warehouses[$tab['parent_id']], $final_label);
271 }
272 }
273
274 return $final_label;
275 }
276
303 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)
304 {
305 global $conf, $langs, $user, $hookmanager;
306
307 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);
308
309 $out = '';
310 if ((!getDolGlobalString('ENTREPOT_EXTRA_STATUS')) && ($filterstatus==="warehouseinternal")) {
311 $filterstatus = '';
312 }
313 if (!empty($fk_product) && $fk_product > 0) {
314 $this->cache_warehouses = array();
315 }
316
317 $this->loadWarehouses($fk_product, '', $filterstatus, true, $exclude, $stockMin, $orderBy);
318 $nbofwarehouses = count($this->cache_warehouses);
319
320 if ($conf->use_javascript_ajax && !$forcecombo) {
321 include_once DOL_DOCUMENT_ROOT.'/core/lib/ajax.lib.php';
322 $comboenhancement = ajax_combobox($htmlname, $events);
323 $out .= $comboenhancement;
324 }
325
326 if (strpos($htmlname, 'search_') !== 0) {
327 if (empty($user->fk_warehouse) || $user->fk_warehouse == -1) {
328 if (is_scalar($selected) && ($selected == '-2' || $selected == 'ifone') && getDolGlobalString('MAIN_DEFAULT_WAREHOUSE')) {
329 $selected = getDolGlobalString('MAIN_DEFAULT_WAREHOUSE');
330 }
331 } else {
332 if (is_scalar($selected) && ($selected == '-2' || $selected == 'ifone') && getDolGlobalString('MAIN_DEFAULT_WAREHOUSE_USER')) {
333 $selected = $user->fk_warehouse;
334 }
335 }
336 }
337
338 $out .= '<!-- selectWarehouses -->';
339 $out .= '<select '.($multiselect ? 'multiple ' : '').'class="flat'.($morecss ? ' '.$morecss : '').'"'.($disabled ? ' disabled' : '');
340 $out .= ' id="'.$htmlname.'" name="'.($htmlname.($multiselect ? '[]' : '').($disabled ? '_disabled' : '')).'"';
341 //$out .= ' placeholder="todo"'; // placeholder for select2 must be added by setting the id+placeholder js param when calling select2
342 $out .= '>';
343 if ($empty) {
344 $out .= '<option value="-1">'.($empty_label ? $empty_label : '&nbsp;').'</option>';
345 }
346 foreach ($this->cache_warehouses as $id => $arraytypes) {
347 $label = '';
348 if ($showfullpath) {
349 $label .= $arraytypes['full_label'];
350 } else {
351 $label .= $arraytypes['label'];
352 }
353 if (($fk_product || ($showstock > 0)) && ($arraytypes['stock'] != 0 || ($showstock > 0))) {
354 if ($arraytypes['stock'] <= 0) {
355 $label .= ' <span class="text-warning">('.$langs->trans("Stock").':'.$arraytypes['stock'].')</span>';
356 } else {
357 $label .= ' <span class="opacitymedium">('.$langs->trans("Stock").':'.$arraytypes['stock'].')</span>';
358 }
359 }
360
361 $out .= '<option value="'.$id.'"';
362 if (is_array($selected)) {
363 if (in_array($id, $selected)) {
364 $out .= ' selected';
365 }
366 } else {
367 if ($selected == $id || (!empty($selected) && preg_match('/^ifone/', $selected) && $nbofwarehouses == 1)) {
368 $out .= ' selected';
369 }
370 }
371 $out .= ' data-html="'.dol_escape_htmltag($label).'"';
372 $out .= '>';
373 $out .= $label;
374 $out .= '</option>';
375 }
376 $out .= '</select>';
377 if ($disabled) {
378 $out .= '<input type="hidden" name="'.$htmlname.'" value="'.(($selected > 0) ? $selected : '').'">';
379 }
380
381 $parameters = array(
382 'selected' => $selected,
383 'htmlname' => $htmlname,
384 'filterstatus' => $filterstatus,
385 'empty' => $empty,
386 'disabled ' => $disabled,
387 'fk_product' => $fk_product,
388 'empty_label' => $empty_label,
389 'showstock' => $showstock,
390 'forcecombo' => $forcecombo,
391 'events' => $events,
392 'morecss' => $morecss,
393 'exclude' => $exclude,
394 'showfullpath' => $showfullpath,
395 'stockMin' => $stockMin,
396 'orderBy' => $orderBy
397 );
398
399 $reshook = $hookmanager->executeHooks('selectWarehouses', $parameters, $this);
400 if ($reshook > 0) {
401 $out = $hookmanager->resPrint;
402 } elseif ($reshook == 0) {
403 $out .= $hookmanager->resPrint;
404 }
405
406 return $out;
407 }
408
428 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')
429 {
430 global $conf, $langs, $user, $hookmanager;
431
432 dol_syslog(get_class($this)."::selectWorkstations $selected, $htmlname, $empty, $disabled, $fk_product, $empty_label, $forcecombo, $morecss", LOG_DEBUG);
433
434 $filterstatus = '';
435 $out = '';
436 if (!empty($fk_product) && $fk_product > 0) {
437 $this->cache_workstations = array();
438 }
439
440 $this->loadWorkstations($fk_product);
441 $nbofworkstations = count($this->cache_workstations);
442
443 if ($conf->use_javascript_ajax && !$forcecombo) {
444 include_once DOL_DOCUMENT_ROOT.'/core/lib/ajax.lib.php';
445 $comboenhancement = ajax_combobox($htmlname, $events);
446 $out .= $comboenhancement;
447 }
448
449 if (strpos($htmlname, 'search_') !== 0) {
450 if (empty($user->fk_workstation) || $user->fk_workstation == -1) {
451 if (($selected == '-2' || $selected == 'ifone') && getDolGlobalString('MAIN_DEFAULT_WORKSTATION')) {
452 $selected = getDolGlobalString('MAIN_DEFAULT_WORKSTATION');
453 }
454 } else {
455 if (($selected == '-2' || $selected == 'ifone') && getDolGlobalString('MAIN_DEFAULT_WORKSTATION')) {
456 $selected = $user->fk_workstation;
457 }
458 }
459 }
460
461 $out .= '<select class="flat'.($morecss ? ' '.$morecss : '').'"'.($disabled ? ' disabled' : '').' id="'.$htmlname.'" name="'.($htmlname.($disabled ? '_disabled' : '')).'">';
462 if ($empty) {
463 $out .= '<option value="-1">'.($empty_label ? $empty_label : '&nbsp;').'</option>';
464 }
465 foreach ($this->cache_workstations as $id => $arraytypes) {
466 $label = $arraytypes['label'];
467
468 $out .= '<option value="'.$id.'"';
469 if ($selected == $id || (preg_match('/^ifone/', $selected) && $nbofworkstations == 1)) {
470 $out .= ' selected';
471 }
472 $out .= ' data-html="'.dol_escape_htmltag($label).'"';
473 $out .= '>';
474 $out .= $label;
475 $out .= '</option>';
476 }
477 $out .= '</select>';
478 if ($disabled) {
479 $out .= '<input type="hidden" name="'.$htmlname.'" value="'.(($selected > 0) ? $selected : '').'">';
480 }
481
482 $parameters = array(
483 'selected' => $selected,
484 'htmlname' => $htmlname,
485 'filterstatus' => $filterstatus,
486 'empty' => $empty,
487 'disabled ' => $disabled,
488 'fk_product' => $fk_product,
489 'empty_label' => $empty_label,
490 'forcecombo' => $forcecombo,
491 'events' => $events,
492 'morecss' => $morecss,
493 'exclude' => $exclude,
494 'showfullpath' => $showfullpath,
495 'orderBy' => $orderBy
496 );
497
498 $reshook = $hookmanager->executeHooks('selectWorkstations', $parameters, $this);
499 if ($reshook > 0) {
500 $out = $hookmanager->resPrint;
501 } elseif ($reshook == 0) {
502 $out .= $hookmanager->resPrint;
503 }
504
505 return $out;
506 }
507
517 public function formSelectWarehouses($page, $selected = '', $htmlname = 'warehouse_id', $addempty = 0)
518 {
519 global $langs;
520 if ($htmlname != "none") {
521 print '<form method="POST" action="'.$page.'">';
522 print '<input type="hidden" name="action" value="setwarehouse">';
523 print '<input type="hidden" name="token" value="'.newToken().'">';
524 print '<table class="nobordernopadding">';
525 print '<tr><td>';
526 print $this->selectWarehouses($selected, $htmlname, '', $addempty);
527 print '</td>';
528 print '<td class="left"><input type="submit" class="button smallpaddingimp" value="'.$langs->trans("Modify").'"></td>';
529 print '</tr></table></form>';
530 } else {
531 if ($selected) {
532 require_once DOL_DOCUMENT_ROOT.'/product/stock/class/entrepot.class.php';
533 $warehousestatic = new Entrepot($this->db);
534 $warehousestatic->fetch($selected);
535 print $warehousestatic->getNomUrl();
536 } else {
537 print "&nbsp;";
538 }
539 }
540 }
541
542 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
555 public function select_measuring_units($name = 'measuring_units', $measuring_style = '', $selected = '0', $adddefault = 0, $mode = 0)
556 {
557 //phpcs:enable
558 print $this->selectMeasuringUnits($name, $measuring_style, $selected, $adddefault, $mode);
559 }
560
573 public function selectMeasuringUnits($name = 'measuring_units', $measuring_style = '', $selected = '0', $adddefault = 0, $mode = 0, $morecss = 'minwidth75 maxwidth125')
574 {
575 global $langs, $db;
576
577 $langs->load("other");
578
579 $return = '';
580 $placeholderID = ($mode == 2 ? '99999999' : '-1'); // we don't want ajaxcombobox replace clearing option in mode 2
581
582 // TODO Use a cache
583 require_once DOL_DOCUMENT_ROOT.'/core/class/cunits.class.php';
584 $measuringUnits = new CUnits($db);
585
586 $filter = array();
587 $filter['t.active'] = 1;
588 if ($measuring_style) {
589 $filter['t.unit_type'] = $measuring_style;
590 }
591
592 $result = $measuringUnits->fetchAll(
593 '',
594 'scale',
595 0,
596 0,
597 $filter
598 );
599 if ($result < 0) {
600 dol_print_error($db);
601 return -1;
602 } else {
603 $return .= '<select class="flat'.($morecss ? ' '.$morecss : '').'" name="'.$name.'" id="'.$name.'">';
604 if ($adddefault || $adddefault === '') {
605 $return .= '<option value="0"'.($selected === '0' ? ' selected' : '').'>'.($adddefault ? '('.$langs->trans("Default").')' : '').'</option>';
606 }
607
608 foreach ($measuringUnits->records as $lines) {
609 $return .= '<option value="';
610 if ($mode == 1) {
611 $return .= $lines->short_label;
612 } elseif ($mode == 2) {
613 $return .= $lines->scale;
614 } else {
615 $return .= $lines->id;
616 }
617 $return .= '"';
618 if ($mode == 1 && $lines->short_label == $selected) {
619 $return .= ' selected';
620 } elseif ($mode == 2 && $lines->scale == $selected) {
621 $return .= ' selected';
622 } elseif ($mode == 0 && $lines->id == $selected) {
623 $return .= ' selected';
624 }
625 $return .= '>';
626 if ($measuring_style == 'time') {
627 $return .= $langs->trans(ucfirst($lines->label));
628 } else {
629 $return .= $langs->trans($lines->label);
630 }
631 $return .= '</option>';
632 }
633 $return .= '</select>';
634 }
635
636 $return .= ajax_combobox($name, [], 0, 0, 'resolve', $placeholderID);
637
638 return $return;
639 }
640
651 public function selectProductNature($name = 'finished', $selected = '', $mode = 0, $showempty = 1)
652 {
653 global $langs, $db;
654
655 $langs->load('products');
656
657 $return = '';
658
659 // TODO Use a cache
660 require_once DOL_DOCUMENT_ROOT.'/core/class/cproductnature.class.php';
661 $productNature = new CProductNature($db);
662
663 $filter = array();
664 $filter['t.active'] = 1;
665
666 $result = $productNature->fetchAll('', '', 0, 0, $filter);
667
668 if ($result < 0) {
669 dol_print_error($db);
670 return -1;
671 } else {
672 $return .= '<select class="flat" name="'.$name.'" id="'.$name.'">';
673 if ($showempty || ($selected == '' || $selected == '-1')) {
674 $return .= '<option value="-1"';
675 if ($selected == '' || $selected == '-1') {
676 $return .= ' selected';
677 }
678 $return .= '></option>';
679 }
680 if (!empty($productNature->records) && is_array($productNature->records)) {
681 foreach ($productNature->records as $lines) {
682 $return .= '<option value="';
683 if ($mode == 1) {
684 $return .= $lines->label;
685 } else {
686 $return .= $lines->code;
687 }
688
689 $return .= '"';
690
691 if ($mode == 1 && $lines->label == $selected) {
692 $return .= ' selected';
693 } elseif ($lines->code == $selected) {
694 $return .= ' selected';
695 }
696
697 $return .= '>';
698 $return .= $langs->trans($lines->label);
699 $return .= '</option>';
700 }
701 }
702 $return .= '</select>';
703 }
704
705 $return .= ajax_combobox($name);
706
707 return $return;
708 }
709
728 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')
729 {
730 global $conf, $langs;
731
732 dol_syslog(get_class($this)."::selectLotStock $selected, $htmlname, $filterstatus, $empty, $disabled, $fk_product, $fk_entrepot, $empty_label, $forcecombo, $morecss", LOG_DEBUG);
733
734 $out = '';
735 $productIdArray = array();
736 if (!is_array($objectLines) || !count($objectLines)) {
737 if (!empty($fk_product) && $fk_product > 0) {
738 $productIdArray[] = (int) $fk_product;
739 }
740 } else {
741 foreach ($objectLines as $line) {
742 if ($line->fk_product) {
743 $productIdArray[] = $line->fk_product;
744 }
745 }
746 }
747
748 $nboflot = $this->loadLotStock($productIdArray);
749
750 if ($conf->use_javascript_ajax && !$forcecombo) {
751 include_once DOL_DOCUMENT_ROOT.'/core/lib/ajax.lib.php';
752 $comboenhancement = ajax_combobox($htmlname, $events);
753 $out .= $comboenhancement;
754 }
755
756 $out .= '<select class="flat'.($morecss ? ' '.$morecss : '').'"'.($disabled ? ' disabled' : '').' id="'.$htmlname.'" name="'.($htmlname.($disabled ? '_disabled' : '')).'">';
757 if ($empty) {
758 $out .= '<option value="-1">'.($empty_label ? $empty_label : '&nbsp;').'</option>';
759 }
760 if (!empty($fk_product) && $fk_product > 0) {
761 $productIdArray = array((int) $fk_product); // only show lot stock for product
762 } else {
763 foreach ($this->cache_lot as $key => $value) {
764 $productIdArray[] = $key;
765 }
766 }
767
768 foreach ($productIdArray as $productId) {
769 foreach ($this->cache_lot[$productId] as $id => $arraytypes) {
770 if (empty($fk_entrepot) || $fk_entrepot == $arraytypes['entrepot_id']) {
771 $label = $arraytypes['entrepot_label'].' - ';
772 $label .= $arraytypes['batch'];
773 if ($arraytypes['qty'] <= 0) {
774 $label .= ' <span class=\'text-warning\'>('.$langs->trans("Stock").' '.$arraytypes['qty'].')</span>';
775 } else {
776 $label .= ' <span class=\'opacitymedium\'>('.$langs->trans("Stock").' '.$arraytypes['qty'].')</span>';
777 }
778
779 $out .= '<option value="'.$id.'"';
780
781 if ($selected == $id || ($selected == 'ifone' && $nboflot == 1)) {
782 $out .= ' selected';
783 }
784 $out .= ' data-html="'.dol_escape_htmltag($label).'"';
785 $out .= '>';
786 $out .= $label;
787 $out .= '</option>';
788 }
789 }
790 }
791 $out .= '</select>';
792 if ($disabled) {
793 $out .= '<input type="hidden" name="'.$htmlname.'" value="'.(($selected > 0) ? $selected : '').'">';
794 }
795
796 return $out;
797 }
798
799
800
811 public function selectLotDataList($htmlname = 'batch_id', $empty = 0, $fk_product = 0, $fk_entrepot = 0, $objectLines = array())
812 {
813 global $conf, $langs, $hookmanager;
814
815 dol_syslog(get_class($this)."::selectLotDataList $htmlname, $empty, $fk_product, $fk_entrepot", LOG_DEBUG);
816
817 $out = '';
818 $productIdArray = array();
819 if (!is_array($objectLines) || !count($objectLines)) {
820 if (!empty($fk_product) && $fk_product > 0) {
821 $productIdArray[] = (int) $fk_product;
822 }
823 } else {
824 foreach ($objectLines as $line) {
825 if ($line->fk_product) {
826 $productIdArray[] = $line->fk_product;
827 }
828 }
829 }
830
831 $nboflot = $this->loadLotStock($productIdArray);
832
833 if (!empty($fk_product) && $fk_product > 0) {
834 $productIdArray = array((int) $fk_product); // only show lot stock for product
835 } else {
836 foreach ($this->cache_lot as $key => $value) {
837 $productIdArray[] = $key;
838 }
839 }
840
841 if (empty($hookmanager)) {
842 include_once DOL_DOCUMENT_ROOT . '/core/class/hookmanager.class.php';
843 $hookmanager = new HookManager($this->db);
844 }
845 $hookmanager->initHooks(array('productdao'));
846 $parameters = array('productIdArray' => $productIdArray, 'htmlname' => $htmlname);
847 $reshook = $hookmanager->executeHooks('selectLotDataList', $parameters, $this);
848 if ($reshook < 0) {
849 return $hookmanager->error;
850 } elseif ($reshook > 0) {
851 return $hookmanager->resPrint;
852 } else {
853 $out .= $hookmanager->resPrint;
854 }
855
856 $out .= '<datalist id="'.$htmlname.'" >';
857 foreach ($productIdArray as $productId) {
858 if (array_key_exists($productId, $this->cache_lot)) {
859 foreach ($this->cache_lot[$productId] as $id => $arraytypes) {
860 if (empty($fk_entrepot) || $fk_entrepot == $arraytypes['entrepot_id']) {
861 $label = $arraytypes['entrepot_label'] . ' - ';
862 $label .= $arraytypes['batch'];
863 // Notice: Chrome show 1 line with value and 1 for label. Firefox show only 1 line with label
864 $out .= '<option data-warehouse="'.dol_escape_htmltag($label).'" value="' . $arraytypes['batch'] . '">' . ($conf->browser->name === 'chrome' ? '' : $arraytypes['batch']) . ' (' . $langs->trans('Stock Total') . ': ' . $arraytypes['qty'] . ')</option>';
865 }
866 }
867 }
868 }
869 $out .= '</datalist>';
870
871 return $out;
872 }
873
874
882 private function loadLotStock($productIdArray = array())
883 {
884 global $conf, $langs;
885
886 $cacheLoaded = false;
887 if (empty($productIdArray)) {
888 // only Load lot stock for given products
889 $this->cache_lot = array();
890 return 0;
891 }
892 if (count($productIdArray) && count($this->cache_lot)) {
893 // check cache already loaded for product id's
894 foreach ($productIdArray as $productId) {
895 $cacheLoaded = !empty($this->cache_lot[$productId]);
896 }
897 }
898 if ($cacheLoaded) {
899 return count($this->cache_lot);
900 } else {
901 // clear cache
902 $this->cache_lot = array();
903 $productIdList = implode(',', $productIdArray);
904
905 $batch_count = 0;
906 global $hookmanager;
907 if (empty($hookmanager)) {
908 include_once DOL_DOCUMENT_ROOT . '/core/class/hookmanager.class.php';
909 $hookmanager = new HookManager($this->db);
910 }
911 $hookmanager->initHooks(array('productdao'));
912 $parameters = array('productIdList' => $productIdList);
913 $reshook = $hookmanager->executeHooks('loadLotStock', $parameters, $this);
914 if ($reshook < 0) {
915 $this->error = $hookmanager->error;
916 return -1;
917 }
918 if (!empty($hookmanager->resArray['batch_list']) && is_array($hookmanager->resArray['batch_list'])) {
919 $this->cache_lot = $hookmanager->resArray['batch_list'];
920 $batch_count = (int) $hookmanager->resArray['batch_count'];
921 }
922 if ($reshook > 0) {
923 return $batch_count;
924 }
925
926 $sql = "SELECT pb.batch, pb.rowid, ps.fk_entrepot, pb.qty, e.ref as label, ps.fk_product";
927 $sql .= " FROM ".$this->db->prefix()."product_batch as pb";
928 $sql .= " LEFT JOIN ".$this->db->prefix()."product_stock as ps on ps.rowid = pb.fk_product_stock";
929 $sql .= " LEFT JOIN ".$this->db->prefix()."entrepot as e on e.rowid = ps.fk_entrepot AND e.entity IN (".getEntity('stock').")";
930 if (!empty($productIdList)) {
931 $sql .= " WHERE ps.fk_product IN (".$this->db->sanitize($productIdList).")";
932 }
933 $sql .= " ORDER BY e.ref, pb.batch";
934
935 dol_syslog(get_class($this).'::loadLotStock', LOG_DEBUG);
936 $resql = $this->db->query($sql);
937 if ($resql) {
938 $num = $this->db->num_rows($resql);
939 $i = 0;
940 while ($i < $num) {
941 $obj = $this->db->fetch_object($resql);
942 $this->cache_lot[$obj->fk_product][$obj->rowid]['id'] = $obj->rowid;
943 $this->cache_lot[$obj->fk_product][$obj->rowid]['batch'] = $obj->batch;
944 $this->cache_lot[$obj->fk_product][$obj->rowid]['entrepot_id'] = $obj->fk_entrepot;
945 $this->cache_lot[$obj->fk_product][$obj->rowid]['entrepot_label'] = $obj->label;
946 $this->cache_lot[$obj->fk_product][$obj->rowid]['qty'] = $obj->qty;
947 $i++;
948 }
949
950 return $batch_count + $num;
951 } else {
952 dol_print_error($this->db);
953 return -1;
954 }
955 }
956 }
957}
$id
Definition account.php:48
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:459
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) Set Hidden conf MAIN_WAREHOUSE_LIS...
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 '.
getDolGlobalInt($key, $default=0)
Return a Dolibarr global constant int value.
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 a 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.
global $conf
The following vars must be defined: $type2label $form $conf, $lang, The following vars may also be de...
Definition member.php:79