dolibarr 21.0.0-beta
product.php
Go to the documentation of this file.
1<?php
2/* Copyright (C) 2001-2007 Rodolphe Quiedeville <rodolphe@quiedeville.org>
3 * Copyright (C) 2004-2020 Laurent Destailleur <eldy@users.sourceforge.net>
4 * Copyright (C) 2004 Eric Seigne <eric.seigne@ryxeo.com>
5 * Copyright (C) 2005 Simon TOSSER <simon@kornog-computing.com>
6 * Copyright (C) 2005-2009 Regis Houssin <regis.houssin@inodbox.com>
7 * Copyright (C) 2013 Cédric Salvador <csalvador.gpcsolutions.fr>
8 * Copyright (C) 2013-2018 Juanjo Menent <jmenent@2byte.es>
9 * Copyright (C) 2014-2015 Cédric Gross <c.gross@kreiz-it.fr>
10 * Copyright (C) 2015 Marcos García <marcosgdf@gmail.com>
11 * Copyright (C) 2018-2024 Frédéric France <frederic.france@free.fr>
12 * Copyright (C) 2021 Gauthier VERDOL <gauthier.verdol@atm-consulting.fr>
13 * Copyright (C) 2024 MDW <mdeweerd@users.noreply.github.com>
14 *
15 * This program is free software; you can redistribute it and/or modify
16 * it under the terms of the GNU General Public License as published by
17 * the Free Software Foundation; either version 3 of the License, or
18 * (at your option) any later version.
19 *
20 * This program is distributed in the hope that it will be useful,
21 * but WITHOUT ANY WARRANTY; without even the implied warranty of
22 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
23 * GNU General Public License for more details.
24 *
25 * You should have received a copy of the GNU General Public License
26 * along with this program. If not, see <https://www.gnu.org/licenses/>.
27 */
28
35// Load Dolibarr environment
36require '../../main.inc.php';
37require_once DOL_DOCUMENT_ROOT.'/product/stock/class/entrepot.class.php';
38require_once DOL_DOCUMENT_ROOT.'/product/class/product.class.php';
39require_once DOL_DOCUMENT_ROOT.'/product/stock/class/productlot.class.php';
40require_once DOL_DOCUMENT_ROOT.'/fourn/class/fournisseur.product.class.php';
41require_once DOL_DOCUMENT_ROOT.'/core/lib/product.lib.php';
42require_once DOL_DOCUMENT_ROOT.'/product/class/html.formproduct.class.php';
43require_once DOL_DOCUMENT_ROOT.'/product/stock/class/productstockentrepot.class.php';
44if (isModEnabled('productbatch')) {
45 require_once DOL_DOCUMENT_ROOT.'/product/class/productbatch.class.php';
46}
47if (isModEnabled('project')) {
48 require_once DOL_DOCUMENT_ROOT.'/core/class/html.formprojet.class.php';
49 require_once DOL_DOCUMENT_ROOT.'/projet/class/project.class.php';
50}
51
52if (isModEnabled('variants')) {
53 require_once DOL_DOCUMENT_ROOT.'/variants/class/ProductAttribute.class.php';
54 require_once DOL_DOCUMENT_ROOT.'/variants/class/ProductAttributeValue.class.php';
55 require_once DOL_DOCUMENT_ROOT.'/variants/class/ProductCombination.class.php';
56 require_once DOL_DOCUMENT_ROOT.'/variants/class/ProductCombination2ValuePair.class.php';
57}
58
67// Load translation files required by the page
68$langs->loadlangs(array('products', 'suppliers', 'orders', 'bills', 'stocks', 'sendings', 'margins'));
69if (isModEnabled('productbatch')) {
70 $langs->load("productbatch");
71}
72
73$backtopage = GETPOST('backtopage', 'alpha');
74$action = GETPOST('action', 'aZ09');
75$cancel = GETPOST('cancel', 'alpha');
76
77$id = GETPOSTINT('id');
78$ref = GETPOST('ref', 'alpha');
79$stocklimit = GETPOSTFLOAT('seuil_stock_alerte');
80$desiredstock = GETPOSTFLOAT('desiredstock');
81$cancel = GETPOST('cancel', 'alpha');
82$fieldid = GETPOSTISSET("ref") ? 'ref' : 'rowid';
83$d_eatby = dol_mktime(0, 0, 0, GETPOSTINT('eatbymonth'), GETPOSTINT('eatbyday'), GETPOSTINT('eatbyyear'));
84$d_sellby = dol_mktime(0, 0, 0, GETPOSTINT('sellbymonth'), GETPOSTINT('sellbyday'), GETPOSTINT('sellbyyear'));
85$pdluoid = GETPOSTINT('pdluoid');
86$batchnumber = GETPOST('batch_number', 'san_alpha');
87if (!empty($batchnumber)) {
88 $batchnumber = trim($batchnumber);
89}
90$cost_price = GETPOST('cost_price', 'alpha');
91
92// Security check
93if ($user->socid) {
94 $socid = $user->socid;
95}
96
97$object = new Product($db);
98$extrafields = new ExtraFields($db);
99
100// fetch optionals attributes and labels
101$extrafields->fetch_name_optionals_label($object->table_element);
102
103if ($id > 0 || !empty($ref)) {
104 $result = $object->fetch($id, $ref);
105}
106
107if (empty($id) && !empty($object->id)) {
108 $id = $object->id;
109}
110
111$modulepart = 'product';
112
113// Get object canvas (By default, this is not defined, so standard usage of dolibarr)
114$canvas = !empty($object->canvas) ? $object->canvas : GETPOST("canvas");
115$objcanvas = null;
116if (!empty($canvas)) {
117 require_once DOL_DOCUMENT_ROOT.'/core/class/canvas.class.php';
118 $objcanvas = new Canvas($db, $action);
119 $objcanvas->getCanvas('stockproduct', 'card', $canvas);
120}
121
122// Initialize a technical object to manage hooks of page. Note that conf->hooks_modules contains an array of hook context
123$hookmanager->initHooks(array('stockproductcard', 'globalcard'));
124
125$error = 0;
126
127$usercanread = (($object->type == Product::TYPE_PRODUCT && $user->hasRight('produit', 'lire')) || ($object->type == Product::TYPE_SERVICE && $user->hasRight('service', 'lire')));
128$usercancreate = (($object->type == Product::TYPE_PRODUCT && $user->hasRight('produit', 'creer')) || ($object->type == Product::TYPE_SERVICE && $user->hasRight('service', 'creer')));
129$usercancreadprice = getDolGlobalString('MAIN_USE_ADVANCED_PERMS') ? $user->hasRight('product', 'product_advance', 'read_prices') : $user->hasRight('product', 'lire');
130$usercanupdatestock = $user->hasRight('stock', 'mouvement', 'creer');
131
132if ($object->isService()) {
133 $label = $langs->trans('Service');
134 $usercancreadprice = getDolGlobalString('MAIN_USE_ADVANCED_PERMS') ? $user->hasRight('service', 'service_advance', 'read_prices') : $user->hasRight('service', 'lire');
135}
136
137if ($object->id > 0) {
138 if ($object->type == $object::TYPE_PRODUCT) {
139 restrictedArea($user, 'produit', $object->id, 'product&product', '', '');
140 }
141 if ($object->type == $object::TYPE_SERVICE) {
142 restrictedArea($user, 'service', $object->id, 'product&product', '', '');
143 }
144} else {
145 restrictedArea($user, 'produit|service', $id, 'product&product', '', '', $fieldid);
146}
147
148
149/*
150 * Actions
151 */
152
153if ($cancel) {
154 $action = '';
155}
156
157$parameters = array('id' => $id, 'ref' => $ref, 'objcanvas' => $objcanvas);
158$reshook = $hookmanager->executeHooks('doActions', $parameters, $object, $action); // Note that $action and $object may have been modified by some hooks
159if ($reshook < 0) {
160 setEventMessages($hookmanager->error, $hookmanager->errors, 'errors');
161}
162
163if ($action == 'setcost_price' && $usercancreate) {
164 if ($id) {
165 $result = $object->fetch($id);
166 $object->cost_price = (float) price2num($cost_price);
167 $result = $object->update($object->id, $user);
168 if ($result > 0) {
169 setEventMessages($langs->trans("RecordSaved"), null, 'mesgs');
170 $action = '';
171 } else {
172 $error++;
173 setEventMessages($object->error, $object->errors, 'errors');
174 }
175 }
176}
177
178if ($action == 'addlimitstockwarehouse' && $usercancreate) {
179 $seuil_stock_alerte = GETPOST('seuil_stock_alerte');
180 $desiredstock = GETPOST('desiredstock');
181
182 $maj_ok = true;
183 if ($seuil_stock_alerte == '') {
184 setEventMessages($langs->trans("ErrorFieldRequired", $langs->transnoentitiesnoconv("StockLimit")), null, 'errors');
185 $maj_ok = false;
186 }
187 if ($desiredstock == '' || is_array($desiredstock)) {
188 setEventMessages($langs->trans("ErrorFieldRequired", $langs->transnoentitiesnoconv("DesiredStock")), null, 'errors');
189 $maj_ok = false;
190 }
191
192 $desiredstock = (float) $desiredstock;
193
194 if ($maj_ok) {
195 $pse = new ProductStockEntrepot($db);
196 // @phan-suppress-next-line PhanPluginSuspiciousParamPosition
197 if ($pse->fetch(0, $id, GETPOSTINT('fk_entrepot')) > 0) {
198 // Update
199 $pse->seuil_stock_alerte = $seuil_stock_alerte;
200 $pse->desiredstock = $desiredstock;
201 if ($pse->update($user) > 0) {
202 setEventMessages($langs->trans('ProductStockWarehouseUpdated'), null, 'mesgs');
203 }
204 } else {
205 // Create
206 $pse->fk_entrepot = GETPOSTINT('fk_entrepot');
207 $pse->fk_product = $id;
208 $pse->seuil_stock_alerte = GETPOST('seuil_stock_alerte');
209 $pse->desiredstock = GETPOSTFLOAT('desiredstock');
210 if ($pse->create($user) > 0) {
211 setEventMessages($langs->trans('ProductStockWarehouseCreated'), null, 'mesgs');
212 }
213 }
214 }
215
216 header("Location: ".$_SERVER["PHP_SELF"]."?id=".$id);
217 exit;
218}
219
220if ($action == 'delete_productstockwarehouse' && $usercancreate) {
221 $pse = new ProductStockEntrepot($db);
222
223 $pse->fetch(GETPOSTINT('fk_productstockwarehouse'));
224 if ($pse->delete($user) > 0) {
225 setEventMessages($langs->trans('ProductStockWarehouseDeleted'), null, 'mesgs');
226 }
227
228 $action = '';
229}
230
231// Set stock limit
232if ($action == 'setseuil_stock_alerte' && $usercancreate) {
233 $object = new Product($db);
234 $result = $object->fetch($id);
235 $object->seuil_stock_alerte = $stocklimit;
236 $result = $object->update($object->id, $user, 0, 'update');
237 if ($result < 0) {
238 setEventMessages($object->error, $object->errors, 'errors');
239 }
240 //else
241 // setEventMessages($lans->trans("SavedRecordSuccessfully"), null, 'mesgs');
242 $action = '';
243}
244
245// Set desired stock
246if ($action == 'setdesiredstock' && $usercancreate) {
247 $object = new Product($db);
248 $result = $object->fetch($id);
249 $object->desiredstock = $desiredstock;
250 $result = $object->update($object->id, $user, 0, 'update');
251 if ($result < 0) {
252 setEventMessages($object->error, $object->errors, 'errors');
253 }
254 $action = '';
255}
256
257
258// Correct stock
259if ($action == "correct_stock" && !$cancel && $usercanupdatestock) {
260 if (!(GETPOSTINT("id_entrepot") > 0)) {
261 setEventMessages($langs->trans("ErrorFieldRequired", $langs->transnoentitiesnoconv("Warehouse")), null, 'errors');
262 $error++;
263 $action = 'correction';
264 }
265 if (!GETPOST("nbpiece")) {
266 setEventMessages($langs->trans("ErrorFieldRequired", $langs->transnoentitiesnoconv("NumberOfUnit")), null, 'errors');
267 $error++;
268 $action = 'correction';
269 }
270
271 if (isModEnabled('productbatch')) {
272 $object = new Product($db);
273 $result = $object->fetch($id);
274
275 if ($object->hasbatch() && !$batchnumber) {
276 setEventMessages($langs->trans("ErrorFieldRequired", $langs->transnoentitiesnoconv("batch_number")), null, 'errors');
277 $error++;
278 $action = 'correction';
279 }
280 }
281
282 if (!$error) {
283 $priceunit = price2num(GETPOST("unitprice"));
284 $nbpiece = price2num(GETPOST("nbpiece", 'alphanohtml'));
285 if (is_numeric($nbpiece) && $nbpiece != 0 && $id) {
286 $origin_element = '';
287 $origin_id = null;
288
289 if (GETPOSTINT('projectid')) {
290 $origin_element = 'project';
291 $origin_id = GETPOSTINT('projectid');
292 }
293
294 if (empty($object)) {
295 $object = new Product($db);
296 $result = $object->fetch($id);
297 }
298
299 $disablestockchangeforsubproduct = 0;
300 if (GETPOST('disablesubproductstockchange')) {
301 $disablestockchangeforsubproduct = 1;
302 }
303
304 if ($object->hasbatch()) {
305 $result = $object->correct_stock_batch(
306 $user,
307 GETPOSTINT("id_entrepot"),
308 $nbpiece,
309 GETPOSTINT("mouvement"),
310 GETPOST("label", 'alphanohtml'), // label movement
311 $priceunit,
312 $d_eatby,
313 $d_sellby,
314 $batchnumber,
315 GETPOST('inventorycode', 'alphanohtml'),
316 $origin_element,
317 $origin_id,
318 $disablestockchangeforsubproduct
319 ); // We do not change value of stock for a correction
320 } else {
321 $result = $object->correct_stock(
322 $user,
323 GETPOSTINT("id_entrepot"),
324 $nbpiece,
325 GETPOSTINT("mouvement"),
326 GETPOST("label", 'alphanohtml'),
327 $priceunit,
328 GETPOST('inventorycode', 'alphanohtml'),
329 $origin_element,
330 $origin_id,
331 $disablestockchangeforsubproduct
332 ); // We do not change value of stock for a correction
333 }
334
335 if ($result > 0) {
336 if ($backtopage) {
337 header("Location: ".$backtopage);
338 exit;
339 } else {
340 header("Location: ".$_SERVER["PHP_SELF"]."?id=".$object->id);
341 exit;
342 }
343 } else {
344 setEventMessages($object->error, $object->errors, 'errors');
345 $action = 'correction';
346 }
347 }
348 }
349}
350
351// Transfer stock from a warehouse to another warehouse
352if ($action == "transfert_stock" && !$cancel && $usercanupdatestock) {
353 if (!(GETPOSTINT("id_entrepot") > 0) || !(GETPOSTINT("id_entrepot_destination") > 0)) {
354 setEventMessages($langs->trans("ErrorFieldRequired", $langs->transnoentitiesnoconv("Warehouse")), null, 'errors');
355 $error++;
356 $action = 'transfert';
357 }
358 if (!GETPOSTINT("nbpiece")) {
359 setEventMessages($langs->trans("ErrorFieldRequired", $langs->transnoentitiesnoconv("NumberOfUnit")), null, 'errors');
360 $error++;
361 $action = 'transfert';
362 }
363 if (GETPOSTINT("id_entrepot") == GETPOSTINT("id_entrepot_destination")) {
364 setEventMessages($langs->trans("ErrorSrcAndTargetWarehouseMustDiffers"), null, 'errors');
365 $error++;
366 $action = 'transfert';
367 }
368 if (isModEnabled('productbatch')) {
369 $object = new Product($db);
370 $result = $object->fetch($id);
371
372 if ($object->hasbatch() && !$batchnumber) {
373 setEventMessages($langs->trans("ErrorFieldRequired", $langs->transnoentitiesnoconv("batch_number")), null, 'errors');
374 $error++;
375 $action = 'transfert';
376 }
377 }
378
379 if (!$error) {
380 if ($id) {
381 $object = new Product($db);
382 $result = $object->fetch($id);
383
384 $db->begin();
385
386 $object->load_stock('novirtual'); // Load array product->stock_warehouse
387
388 // Define value of products moved
389 $pricesrc = 0;
390 if (isset($object->pmp)) {
391 $pricesrc = $object->pmp;
392 }
393 $pricedest = $pricesrc;
394
395 $nbpiece = price2num(GETPOST("nbpiece", 'alphanohtml'));
396
397 if ($object->hasbatch()) {
398 $pdluo = new Productbatch($db);
399
400 if ($pdluoid > 0) {
401 $result = $pdluo->fetch($pdluoid);
402 if ($result) {
403 $srcwarehouseid = $pdluo->warehouseid;
404 $batch = $pdluo->batch;
405 $eatby = $pdluo->eatby;
406 $sellby = $pdluo->sellby;
407 } else {
408 setEventMessages($pdluo->error, $pdluo->errors, 'errors');
409 $error++;
410 }
411 } else {
412 $srcwarehouseid = GETPOSTINT('id_entrepot');
413 $batch = $batchnumber;
414 $eatby = $d_eatby;
415 $sellby = $d_sellby;
416 }
417
418 $nbpiece = price2num(GETPOST("nbpiece", 'alphanohtml'));
419
420 if (!$error) {
421 // Remove stock
422 $result1 = $object->correct_stock_batch(
423 $user,
424 $srcwarehouseid,
425 $nbpiece,
426 1,
427 GETPOST("label", 'alphanohtml'),
428 $pricesrc,
429 $eatby,
430 $sellby,
431 $batch,
432 GETPOST('inventorycode', 'alphanohtml')
433 );
434 if ($result1 < 0) {
435 $error++;
436 }
437 }
438 if (!$error) {
439 // Add stock
440 $result2 = $object->correct_stock_batch(
441 $user,
442 GETPOSTINT("id_entrepot_destination"),
443 $nbpiece,
444 0,
445 GETPOST("label", 'alphanohtml'),
446 $pricedest,
447 $eatby,
448 $sellby,
449 $batch,
450 GETPOST('inventorycode', 'alphanohtml')
451 );
452 if ($result2 < 0) {
453 $error++;
454 }
455 }
456 } else {
457 if (!$error) {
458 // Remove stock
459 $result1 = $object->correct_stock(
460 $user,
461 GETPOSTINT("id_entrepot"),
462 $nbpiece,
463 1,
464 GETPOST("label", 'alphanohtml'),
465 $pricesrc,
466 GETPOST('inventorycode', 'alphanohtml')
467 );
468 if ($result1 < 0) {
469 $error++;
470 }
471 }
472 if (!$error) {
473 // Add stock
474 $result2 = $object->correct_stock(
475 $user,
476 GETPOSTINT("id_entrepot_destination"),
477 $nbpiece,
478 0,
479 GETPOST("label", 'alphanohtml'),
480 $pricedest,
481 GETPOST('inventorycode', 'alphanohtml')
482 );
483 if ($result2 < 0) {
484 $error++;
485 }
486 }
487 }
488
489
490 if (!$error && $result1 >= 0 && $result2 >= 0) {
491 $db->commit();
492
493 if ($backtopage) {
494 header("Location: ".$backtopage);
495 exit;
496 } else {
497 header("Location: product.php?id=".$object->id);
498 exit;
499 }
500 } else {
501 setEventMessages($object->error, $object->errors, 'errors');
502 $db->rollback();
503 $action = 'transfert';
504 }
505 }
506 }
507}
508
509// Update batch information
510if ($action == 'updateline' && GETPOST('save') == $langs->trans("Save") && $usercancreate) {
511 $pdluo = new Productbatch($db);
512 $result = $pdluo->fetch(GETPOSTINT('pdluoid'));
513
514 if ($result > 0) {
515 if ($pdluo->id) {
516 if ((!GETPOST("sellby")) && (!GETPOST("eatby")) && (!$batchnumber)) {
517 setEventMessages($langs->trans("ErrorFieldRequired", $langs->transnoentitiesnoconv("atleast1batchfield")), null, 'errors');
518 } else {
519 $d_eatby = dol_mktime(0, 0, 0, GETPOSTINT('eatbymonth'), GETPOSTINT('eatbyday'), GETPOSTINT('eatbyyear'));
520 $d_sellby = dol_mktime(0, 0, 0, GETPOSTINT('sellbymonth'), GETPOSTINT('sellbyday'), GETPOSTINT('sellbyyear'));
521 $pdluo->batch = $batchnumber;
522 $pdluo->eatby = $d_eatby;
523 $pdluo->sellby = $d_sellby;
524 $result = $pdluo->update($user);
525 if ($result < 0) {
526 setEventMessages($pdluo->error, $pdluo->errors, 'errors');
527 }
528 }
529 } else {
530 setEventMessages($langs->trans('BatchInformationNotfound'), null, 'errors');
531 }
532 } else {
533 setEventMessages($pdluo->error, null, 'errors');
534 }
535 header("Location: product.php?id=".$id);
536 exit;
537}
538
539
540/*
541 * View
542 */
543
544$form = new Form($db);
545$formproduct = new FormProduct($db);
546if (isModEnabled('project')) {
547 $formproject = new FormProjets($db);
548}
549
550if ($id > 0 || $ref) {
551 $object = new Product($db);
552 $result = $object->fetch($id, $ref);
553
554 $variants = $object->hasVariants();
555
556 $object->load_stock();
557
558 $title = $langs->trans('ProductServiceCard');
559 $helpurl = '';
560 $shortlabel = dol_trunc($object->label, 16);
561 if (GETPOST("type") == '0' || ($object->type == Product::TYPE_PRODUCT)) {
562 $title = $langs->trans('Product')." ".$shortlabel." - ".$langs->trans('Stock');
563 $helpurl = 'EN:Module_Products|FR:Module_Produits|ES:M&oacute;dulo_Productos';
564 }
565 if (GETPOST("type") == '1' || ($object->type == Product::TYPE_SERVICE)) {
566 $title = $langs->trans('Service')." ".$shortlabel." - ".$langs->trans('Stock');
567 $helpurl = 'EN:Module_Services_En|FR:Module_Services|ES:M&oacute;dulo_Servicios';
568 }
569
570 llxHeader('', $title, $helpurl, '', 0, 0, '', '', '', 'mod-product page-card_stock_product');
571
572 if (!empty($conf->use_javascript_ajax)) {
573 ?>
574 <script type="text/javascript">
575 $(document).ready(function() {
576 $(".collapse_batch").click(function() {
577 console.log("We click on collapse_batch");
578 var id_entrepot = $(this).attr('id').replace('ent', '');
579
580 if($(this).text().indexOf('+') > 0) {
581 $(".batch_warehouse" + id_entrepot).show();
582 $(this).html('(-)');
583 jQuery("#show_all").hide();
584 jQuery("#hide_all").show();
585 }
586 else {
587 $(".batch_warehouse" + id_entrepot).hide();
588 $(this).html('(+)');
589 }
590
591 return false;
592 });
593
594 $("#show_all").click(function() {
595 console.log("We click on show_all");
596 $("[class^=batch_warehouse]").show();
597 $("[class^=collapse_batch]").html('(-)');
598 jQuery("#show_all").hide();
599 jQuery("#hide_all").show();
600 return false;
601 });
602
603 $("#hide_all").click(function() {
604 console.log("We click on hide_all");
605 $("[class^=batch_warehouse]").hide();
606 $("[class^=collapse_batch]").html('(+)');
607 jQuery("#hide_all").hide();
608 jQuery("#show_all").show();
609 return false;
610 });
611
612 });
613 </script>
614 <?php
615 }
616
617 if ($result > 0) {
619 $titre = $langs->trans("CardProduct".$object->type);
620 $picto = ($object->type == Product::TYPE_SERVICE ? 'service' : 'product');
621
622 print dol_get_fiche_head($head, 'stock', $titre, -1, $picto);
623
625
626 $linkback = '<a href="'.DOL_URL_ROOT.'/product/list.php?restore_lastsearch_values=1&type='.$object->type.'">'.$langs->trans("BackToList").'</a>';
627
628 $shownav = 1;
629 if ($user->socid && !in_array('stock', explode(',', getDolGlobalString('MAIN_MODULES_FOR_EXTERNAL')))) {
630 $shownav = 0;
631 }
632
633 dol_banner_tab($object, 'ref', $linkback, $shownav, 'ref');
634
635 if (!$variants) {
636 print '<div class="fichecenter">';
637
638 print '<div class="fichehalfleft">';
639 print '<div class="underbanner clearboth"></div>';
640
641 print '<table class="border tableforfield centpercent">';
642
643 // Type
644 if (isModEnabled("product") && isModEnabled("service")) {
645 $typeformat = 'select;0:'.$langs->trans("Product").',1:'.$langs->trans("Service");
646 print '<tr><td class="">';
647 print (!getDolGlobalString('PRODUCT_DENY_CHANGE_PRODUCT_TYPE')) ? $form->editfieldkey("Type", 'fk_product_type', $object->type, $object, 0, $typeformat) : $langs->trans('Type');
648 print '</td><td>';
649 print $form->editfieldval("Type", 'fk_product_type', $object->type, $object, 0, $typeformat);
650 print '</td></tr>';
651 }
652
653 if (isModEnabled('productbatch')) {
654 print '<tr><td class="">'.$langs->trans("ManageLotSerial").'</td><td>';
655 print $object->getLibStatut(0, 2);
656 print '</td></tr>';
657 }
658
659 // Cost price. Can be used for margin module for option "calculate margin on explicit cost price
660 print '<tr><td>';
661 $textdesc = $langs->trans("CostPriceDescription");
662 $textdesc .= "<br>".$langs->trans("CostPriceUsage");
663 $text = $form->textwithpicto($langs->trans("CostPrice"), $textdesc, 1, 'help', '');
664 if (!$usercancreadprice) {
665 print $form->editfieldkey($text, 'cost_price', '', $object, 0, 'amount:6');
666 print '</td><td>';
667 print $form->editfieldval($text, 'cost_price', '', $object, 0, 'amount:6');
668 } else {
669 print $form->editfieldkey($text, 'cost_price', $object->cost_price, $object, $usercancreate, 'amount:6');
670 print '</td><td>';
671 print $form->editfieldval($text, 'cost_price', $object->cost_price, $object, $usercancreate, 'amount:6');
672 }
673 print '</td></tr>';
674
675
676
677 // AWP
678 print '<tr><td class="titlefield">';
679 print $form->textwithpicto($langs->trans("AverageUnitPricePMPShort"), $langs->trans("AverageUnitPricePMPDesc"));
680 print '</td>';
681 print '<td>';
682 if ($object->pmp > 0 && $usercancreadprice) {
683 print price($object->pmp).' '.$langs->trans("HT");
684 }
685 print '</td>';
686 print '</tr>';
687
688 // Minimum Price
689 print '<tr><td>'.$langs->trans("BuyingPriceMin").'</td>';
690 print '<td>';
691 $product_fourn = new ProductFournisseur($db);
692 if ($product_fourn->find_min_price_product_fournisseur($object->id) > 0) {
693 if ($product_fourn->product_fourn_price_id > 0 && $usercancreadprice) {
694 print $product_fourn->display_price_product_fournisseur();
695 } else {
696 print $langs->trans("NotDefined");
697 }
698 }
699 print '</td></tr>';
700
701 if (!getDolGlobalString('PRODUIT_MULTIPRICES')) {
702 // Price
703 print '<tr><td>'.$langs->trans("SellingPrice").'</td><td>';
704 if ($usercancreadprice) {
705 if ($object->price_base_type == 'TTC') {
706 print price($object->price_ttc).' '.$langs->trans($object->price_base_type);
707 } else {
708 print price($object->price).' '.$langs->trans($object->price_base_type);
709 }
710 }
711 print '</td></tr>';
712
713 // Price minimum
714 print '<tr><td>'.$langs->trans("MinPrice").'</td><td>';
715 if ($usercancreadprice) {
716 if ($object->price_base_type == 'TTC') {
717 print price($object->price_min_ttc).' '.$langs->trans($object->price_base_type);
718 } else {
719 print price($object->price_min).' '.$langs->trans($object->price_base_type);
720 }
721 }
722 print '</td></tr>';
723 } else {
724 // Price
725 print '<tr><td>'.$langs->trans("SellingPrice").'</td><td>';
726 print '<span class="opacitymedium">'.$langs->trans("Variable").'</span>';
727 print '</td></tr>';
728
729 // Price minimum
730 print '<tr><td>'.$langs->trans("MinPrice").'</td><td>';
731 print '<span class="opacitymedium">'.$langs->trans("Variable").'</span>';
732 print '</td></tr>';
733 }
734
735 // Hook formObject
736 $parameters = array();
737 $reshook = $hookmanager->executeHooks('formObjectOptions', $parameters, $object, $action); // Note that $action and $object may have been modified by hook
738 print $hookmanager->resPrint;
739
740 print '</table>';
741
742 print '</div>';
743 print '<div class="fichehalfright"><div class="underbanner clearboth"></div>';
744
745 print '<table class="border tableforfield centpercent">';
746
747 // Stock alert threshold
748 print '<tr><td>'.$form->editfieldkey($form->textwithpicto($langs->trans("StockLimit"), $langs->trans("StockLimitDesc"), 1), 'seuil_stock_alerte', $object->seuil_stock_alerte, $object, $user->hasRight('produit', 'creer')).'</td><td>';
749 print $form->editfieldval("StockLimit", 'seuil_stock_alerte', $object->seuil_stock_alerte, $object, $user->hasRight('produit', 'creer'), 'string');
750 print '</td></tr>';
751
752 // Desired stock
753 print '<tr><td>'.$form->editfieldkey($form->textwithpicto($langs->trans("DesiredStock"), $langs->trans("DesiredStockDesc"), 1), 'desiredstock', $object->desiredstock, $object, $user->hasRight('produit', 'creer'));
754 print '</td><td>';
755 print $form->editfieldval("DesiredStock", 'desiredstock', $object->desiredstock, $object, $user->hasRight('produit', 'creer'), 'string');
756 print '</td></tr>';
757
758 // Real stock
759 $text_stock_options = $langs->trans("RealStockDesc").'<br>';
760 $text_stock_options .= $langs->trans("RealStockWillAutomaticallyWhen").'<br>';
761 $text_stock_options .= (getDolGlobalString('STOCK_CALCULATE_ON_SHIPMENT') || getDolGlobalString('STOCK_CALCULATE_ON_SHIPMENT_CLOSE') ? '- '.$langs->trans("DeStockOnShipment").'<br>' : '');
762 $text_stock_options .= (getDolGlobalString('STOCK_CALCULATE_ON_VALIDATE_ORDER') ? '- '.$langs->trans("DeStockOnValidateOrder").'<br>' : '');
763 $text_stock_options .= (getDolGlobalString('STOCK_CALCULATE_ON_BILL') ? '- '.$langs->trans("DeStockOnBill").'<br>' : '');
764 $text_stock_options .= (getDolGlobalString('STOCK_CALCULATE_ON_SUPPLIER_BILL') ? '- '.$langs->trans("ReStockOnBill").'<br>' : '');
765 $text_stock_options .= (getDolGlobalString('STOCK_CALCULATE_ON_SUPPLIER_VALIDATE_ORDER') ? '- '.$langs->trans("ReStockOnValidateOrder").'<br>' : '');
766 $text_stock_options .= (getDolGlobalString('STOCK_CALCULATE_ON_SUPPLIER_DISPATCH_ORDER') ? '- '.$langs->trans("ReStockOnDispatchOrder").'<br>' : '');
767 $text_stock_options .= (getDolGlobalString('STOCK_CALCULATE_ON_RECEPTION') || getDolGlobalString('STOCK_CALCULATE_ON_RECEPTION_CLOSE') ? '- '.$langs->trans("StockOnReception").'<br>' : '');
768 $parameters = array();
769 $reshook = $hookmanager->executeHooks('physicalStockTextStockOptions', $parameters, $object, $action); // Note that $action and $object may have been modified by hook
770 if ($reshook > 0) {
771 $text_stock_options = $hookmanager->resPrint;
772 } elseif ($reshook == 0) {
773 $text_stock_options .= $hookmanager->resPrint;
774 } else {
775 setEventMessages($hookmanager->error, $hookmanager->errors, 'errors');
776 }
777
778 print '<tr><td>';
779 print $form->textwithpicto($langs->trans("PhysicalStock"), $text_stock_options, 1);
780 print '</td>';
781 print '<td>'.price2num($object->stock_reel, 'MS');
782 if ($object->seuil_stock_alerte != '' && ($object->stock_reel < $object->seuil_stock_alerte)) {
783 print ' '.img_warning($langs->trans("StockLowerThanLimit", $object->seuil_stock_alerte));
784 }
785
786 print ' &nbsp; &nbsp;<a href="'.DOL_URL_ROOT.'/product/stock/stockatdate.php?productid='.$object->id.'">'.$langs->trans("StockAtDate").'</a>';
787 print '</td>';
788 print '</tr>';
789
790 $stocktheo = price2num($object->stock_theorique, 'MS');
791
792 $found = 0;
793 $helpondiff = '<strong>'.$langs->trans("StockDiffPhysicTeoric").':</strong><br>';
794 // Number of sales orders running
795 if (isModEnabled('order')) {
796 if ($found) {
797 $helpondiff .= '<br>';
798 } else {
799 $found = 1;
800 }
801 $helpondiff .= $langs->trans("ProductQtyInCustomersOrdersRunning").': '.$object->stats_commande['qty'];
802 $result = $object->load_stats_commande(0, '0', 1);
803 if ($result < 0) {
804 dol_print_error($db, $object->error);
805 }
806 $helpondiff .= ' <span class="opacitymedium">('.$langs->trans("ProductQtyInDraft").': '.$object->stats_commande['qty'].')</span>';
807 }
808
809 // Number of product from sales order already sent (partial shipping)
810 if (isModEnabled("shipping")) {
811 require_once DOL_DOCUMENT_ROOT.'/expedition/class/expedition.class.php';
812 $filterShipmentStatus = '';
813 if (getDolGlobalString('STOCK_CALCULATE_ON_SHIPMENT')) {
815 } elseif (getDolGlobalString('STOCK_CALCULATE_ON_SHIPMENT_CLOSE')) {
816 $filterShipmentStatus = Expedition::STATUS_CLOSED;
817 }
818 if ($found) {
819 $helpondiff .= '<br>';
820 } else {
821 $found = 1;
822 }
823 $result = $object->load_stats_sending(0, '2', 1, $filterShipmentStatus);
824 $helpondiff .= $langs->trans("ProductQtyInShipmentAlreadySent").': '.$object->stats_expedition['qty'];
825 }
826
827 // Number of supplier order running
828 if (isModEnabled("supplier_order") || isModEnabled("supplier_invoice")) {
829 if ($found) {
830 $helpondiff .= '<br>';
831 } else {
832 $found = 1;
833 }
834 $result = $object->load_stats_commande_fournisseur(0, '3,4', 1);
835 $helpondiff .= $langs->trans("ProductQtyInSuppliersOrdersRunning").': '.$object->stats_commande_fournisseur['qty'];
836 $result = $object->load_stats_commande_fournisseur(0, '0,1,2', 1);
837 if ($result < 0) {
838 dol_print_error($db, $object->error);
839 }
840 $helpondiff .= ' <span class="opacitymedium">('.$langs->trans("ProductQtyInDraftOrWaitingApproved").': '.$object->stats_commande_fournisseur['qty'].')</span>';
841 }
842
843 // Number of product from supplier order already received (partial receipt)
844 if (isModEnabled("supplier_order") || isModEnabled("supplier_invoice")) {
845 if ($found) {
846 $helpondiff .= '<br>';
847 } else {
848 $found = 1;
849 }
850 $helpondiff .= $langs->trans("ProductQtyInSuppliersShipmentAlreadyRecevied").': '.$object->stats_reception['qty'];
851 }
852
853 // Number of product in production
854 if (isModEnabled('mrp')) {
855 if ($found) {
856 $helpondiff .= '<br>';
857 } else {
858 $found = 1;
859 }
860 $helpondiff .= $langs->trans("ProductQtyToConsumeByMO").': '.$object->stats_mrptoconsume['qty'].'<br>';
861 $helpondiff .= $langs->trans("ProductQtyToProduceByMO").': '.$object->stats_mrptoproduce['qty'];
862 }
863 $parameters = array('found' => &$found, 'id' => $object->id, 'includedraftpoforvirtual' => null);
864 $reshook = $hookmanager->executeHooks('virtualStockHelpOnDiff', $parameters, $object, $action); // Note that $action and $object may have been modified by hook
865 if ($reshook > 0) {
866 $helpondiff = $hookmanager->resPrint;
867 } elseif ($reshook == 0) {
868 $helpondiff .= $hookmanager->resPrint;
869 } else {
870 setEventMessages($hookmanager->error, $hookmanager->errors, 'errors');
871 }
872
873
874 // Calculating a theoretical value
875 print '<tr><td>';
876 print $form->textwithpicto($langs->trans("VirtualStock"), $langs->trans("VirtualStockDesc"));
877 print '</td>';
878 print "<td>";
879 //print (empty($stocktheo)?0:$stocktheo);
880 print $form->textwithpicto((empty($stocktheo) ? 0 : $stocktheo), $helpondiff);
881 if ($object->seuil_stock_alerte != '' && ($object->stock_theorique < $object->seuil_stock_alerte)) {
882 print ' '.img_warning($langs->trans("StockLowerThanLimit", $object->seuil_stock_alerte));
883 }
884 print ' &nbsp; &nbsp;<a href="'.DOL_URL_ROOT.'/product/stock/stockatdate.php?mode=future&productid='.$object->id.'">'.$langs->trans("VirtualStockAtDate").'</a>';
885 print '</td>';
886 print '</tr>';
887
888 // Last movement
889 if ($user->hasRight('stock', 'mouvement', 'lire')) {
890 $sql = "SELECT max(m.datem) as datem";
891 $sql .= " FROM ".MAIN_DB_PREFIX."stock_mouvement as m";
892 $sql .= " WHERE m.fk_product = ".((int) $object->id);
893 $resqlbis = $db->query($sql);
894 if ($resqlbis) {
895 $obj = $db->fetch_object($resqlbis);
896 $lastmovementdate = $db->jdate($obj->datem);
897 } else {
898 dol_print_error($db);
899 }
900 print '<tr><td class="tdtop">'.$langs->trans("LastMovement").'</td><td>';
901 if ($lastmovementdate) {
902 print dol_print_date($lastmovementdate, 'dayhour').' ';
903 print ' &nbsp; &nbsp; ';
904 print img_picto($langs->trans("StockMovement"), 'movement', 'class="pictofixedwidth"');
905 print '<a href="'.DOL_URL_ROOT.'/product/stock/movement_list.php?idproduct='.$object->id.'">'.$langs->trans("FullList").'</a>';
906 } else {
907 print img_picto($langs->trans("StockMovement"), 'movement', 'class="pictofixedwidth"');
908 print '<a href="'.DOL_URL_ROOT.'/product/stock/movement_list.php?idproduct='.$object->id.'">'.$langs->trans("None").'</a>';
909 }
910 print "</td></tr>";
911 }
912
913 print "</table>";
914
915 print '</div>';
916 print '</div>';
917
918 print '<div class="clearboth"></div>';
919 }
920
921 print dol_get_fiche_end();
922 }
923
924 // Correct stock
925 if ($action == "correction") {
926 include DOL_DOCUMENT_ROOT.'/product/stock/tpl/stockcorrection.tpl.php';
927 print '<br><br>';
928 }
929
930 // Transfer of units
931 if ($action == "transfert") {
932 include DOL_DOCUMENT_ROOT.'/product/stock/tpl/stocktransfer.tpl.php';
933 print '<br><br>';
934 }
935} else {
937}
938
939
940// Actions buttons
941
942$parameters = array();
943
944$reshook = $hookmanager->executeHooks('addMoreActionsButtons', $parameters, $object, $action); // Note that $action and $object may have been modified by hook
945if (empty($reshook)) {
946 if (empty($action) && $object->id) {
947 print "<div class=\"tabsAction\">\n";
948
949 if ($user->hasRight('stock', 'mouvement', 'creer')) {
950 if (!$variants || getDolGlobalString('VARIANT_ALLOW_STOCK_MOVEMENT_ON_VARIANT_PARENT')) {
951 print '<a class="butAction" href="'.$_SERVER["PHP_SELF"].'?id='.$object->id.'&action=transfert&token='.newToken().'">'.$langs->trans("TransferStock").'</a>';
952 } else {
953 print '<a class="butActionRefused classfortooltip" href="#" title="'.$langs->trans("ActionAvailableOnVariantProductOnly").'">'.$langs->trans("TransferStock").'</a>';
954 }
955 } else {
956 print '<a class="butActionRefused classfortooltip" href="#" title="'.$langs->trans("NotEnoughPermissions").'">'.$langs->trans("TransferStock").'</a>';
957 }
958
959 if ($user->hasRight('stock', 'mouvement', 'creer')) {
960 if (!$variants || getDolGlobalString('VARIANT_ALLOW_STOCK_MOVEMENT_ON_VARIANT_PARENT')) {
961 print '<a class="butAction" href="'.$_SERVER["PHP_SELF"].'?id='.$object->id.'&action=correction&token='.newToken().'">'.$langs->trans("CorrectStock").'</a>';
962 } else {
963 print '<a class="butActionRefused classfortooltip" href="#" title="'.$langs->trans("ActionAvailableOnVariantProductOnly").'">'.$langs->trans("CorrectStock").'</a>';
964 }
965 } else {
966 print '<a class="butActionRefused classfortooltip" href="#" title="'.$langs->trans("NotEnoughPermissions").'">'.$langs->trans("CorrectStock").'</a>';
967 }
968
969 print '</div>';
970 }
971}
972
973
974if (!$variants || getDolGlobalString('VARIANT_ALLOW_STOCK_MOVEMENT_ON_VARIANT_PARENT')) {
975 /*
976 * Stock detail (by warehouse). May go down into batch details.
977 */
978
979 print '<div class="div-table-responsive">';
980 print '<table class="noborder centpercent">';
981
982 print '<tr class="liste_titre">';
983 print '<td colspan="4">'.$langs->trans("Warehouse").'</td>';
984 print '<td class="right">'.$langs->trans("NumberOfUnit").'</td>';
985 print '<td class="right">'.$form->textwithpicto($langs->trans("AverageUnitPricePMPShort"), $langs->trans("AverageUnitPricePMPDesc")).'</td>';
986 print '<td class="right">'.$langs->trans("EstimatedStockValueShort").'</td>';
987 print '<td class="right">'.$langs->trans("SellPriceMin").'</td>';
988 print '<td class="right">'.$langs->trans("EstimatedStockValueSellShort").'</td>';
989 print '<td></td>';
990 print '<td></td>';
991 print '</tr>';
992
993 if ((isModEnabled('productbatch')) && $object->hasbatch()) {
994 $colspan = 3;
995 print '<tr class="liste_titre"><td class="minwidth200">';
996 if (!empty($conf->use_javascript_ajax)) {
997 print '<a id="show_all" href="#" class="hideobject">'.img_picto('', 'folder-open', 'class="paddingright"').$langs->trans("ShowAllLots").'</a>';
998 //print ' &nbsp; ';
999 print '<a id="hide_all" href="#">'.img_picto('', 'folder', 'class="paddingright"').$langs->trans("HideLots").'</a>';
1000 //print '&nbsp;'.$form->textwithpicto('', $langs->trans('CollapseBatchDetailHelp'), 1, 'help', '');
1001 }
1002 print '</td>';
1003 print '<td class="right">'.$langs->trans("batch_number").'</td>';
1004 if (!getDolGlobalString('PRODUCT_DISABLE_SELLBY')) {
1005 $colspan--;
1006 print '<td class="center width100">'.$langs->trans("SellByDate").'</td>';
1007 }
1008 if (!getDolGlobalString('PRODUCT_DISABLE_EATBY')) {
1009 $colspan--;
1010 print '<td class="center width100">'.$langs->trans("EatByDate").'</td>';
1011 }
1012 print '<td colspan="'.$colspan.'"></td>';
1013 print '<td></td>';
1014 print '<td></td>';
1015 print '<td></td>';
1016 print '<td></td>';
1017 print '<td></td>';
1018 print '<td></td>';
1019 print '</tr>';
1020 }
1021
1022 $sql = "SELECT e.rowid, e.ref, e.lieu, e.fk_parent, e.statut as status, ps.reel, ps.rowid as product_stock_id, p.pmp";
1023 $sql .= " FROM ".MAIN_DB_PREFIX."entrepot as e,";
1024 $sql .= " ".MAIN_DB_PREFIX."product_stock as ps";
1025 $sql .= " LEFT JOIN ".MAIN_DB_PREFIX."product as p ON p.rowid = ps.fk_product";
1026 $sql .= " WHERE ps.reel != 0";
1027 $sql .= " AND ps.fk_entrepot = e.rowid";
1028 $sql .= " AND e.entity IN (".getEntity('stock').")";
1029 $sql .= " AND ps.fk_product = ".((int) $object->id);
1030 $sql .= " ORDER BY e.ref";
1031
1032 $entrepotstatic = new Entrepot($db);
1033 $product_lot_static = new Productlot($db);
1034
1035 $num = 0;
1036 $total = 0;
1037 $totalvalue = $totalvaluesell = 0;
1038 $totalwithpmp = 0;
1039
1040 $resql = $db->query($sql);
1041 if ($resql) {
1042 $num = $db->num_rows($resql);
1043 $total = $totalwithpmp;
1044 $i = 0;
1045 $var = false;
1046 while ($i < $num) {
1047 $obj = $db->fetch_object($resql);
1048
1049 $entrepotstatic->id = $obj->rowid;
1050 $entrepotstatic->ref = $obj->ref;
1051 $entrepotstatic->label = $obj->ref;
1052 $entrepotstatic->lieu = $obj->lieu;
1053 $entrepotstatic->fk_parent = $obj->fk_parent;
1054 $entrepotstatic->statut = $obj->status;
1055 $entrepotstatic->status = $obj->status;
1056
1057 $stock_real = price2num($obj->reel, 'MS');
1058 print '<tr class="oddeven">';
1059
1060 // Warehouse
1061 print '<td colspan="4">';
1062 print $entrepotstatic->getNomUrl(1);
1063 if (!empty($conf->use_javascript_ajax) && isModEnabled('productbatch') && $object->hasbatch()) {
1064 print '<a class="collapse_batch marginleftonly" id="ent' . $entrepotstatic->id . '" href="#">';
1065 print(!getDolGlobalString('STOCK_SHOW_ALL_BATCH_BY_DEFAULT') ? '(+)' : '(-)');
1066 print '</a>';
1067 }
1068 print '</td>';
1069
1070 print '<td class="right">'.$stock_real.($stock_real < 0 ? ' '.img_warning() : '').'</td>';
1071
1072 // PMP
1073 print '<td class="right nowraponall">'.(price2num($object->pmp) ? price2num($object->pmp, 'MU') : '').'</td>';
1074
1075 // Value purchase
1076 if ($usercancreadprice) {
1077 print '<td class="right amount nowraponall">'.(price2num($object->pmp) ? price(price2num($object->pmp * $obj->reel, 'MT')) : '').'</td>';
1078 } else {
1079 print '<td class="right amount nowraponall"></td>';
1080 }
1081
1082 // Sell price
1083 $minsellprice = null;
1084 $maxsellprice = null;
1085 print '<td class="right nowraponall">';
1086 if (getDolGlobalString('PRODUIT_MULTIPRICES')) {
1087 foreach ($object->multiprices as $priceforlevel) {
1088 if (is_numeric($priceforlevel)) {
1089 if (is_null($maxsellprice) || $priceforlevel > $maxsellprice) {
1090 $maxsellprice = $priceforlevel;
1091 }
1092 if (is_null($minsellprice) || $priceforlevel < $minsellprice) {
1093 $minsellprice = $priceforlevel;
1094 }
1095 }
1096 }
1097 print '<span class="valignmiddle">';
1098 if ($usercancreadprice) {
1099 if ($minsellprice != $maxsellprice) {
1100 print price(price2num($minsellprice, 'MU'), 1).' - '.price(price2num($maxsellprice, 'MU'), 1);
1101 } else {
1102 print price(price2num($minsellprice, 'MU'), 1);
1103 }
1104 }
1105 print '</span>';
1106 print $form->textwithpicto('', $langs->trans("Variable"));
1107 } elseif ($usercancreadprice) {
1108 print price(price2num($object->price, 'MU'), 1);
1109 }
1110 print '</td>';
1111
1112 // Value sell
1113 print '<td class="right amount nowraponall">';
1114 if (getDolGlobalString('PRODUIT_MULTIPRICES')) {
1115 print '<span class="valignmiddle">';
1116 if ($usercancreadprice) {
1117 if ($minsellprice != $maxsellprice) {
1118 print price(price2num($minsellprice * $obj->reel, 'MT'), 1).' - '.price(price2num($maxsellprice * $obj->reel, 'MT'), 1);
1119 } else {
1120 print price(price2num($minsellprice * $obj->reel, 'MT'), 1);
1121 }
1122 }
1123 print '</span>';
1124 print $form->textwithpicto('', $langs->trans("Variable"));
1125 } else {
1126 if ($usercancreadprice) {
1127 print price(price2num($object->price * $obj->reel, 'MT'), 1);
1128 }
1129 }
1130 print '</td>';
1131 print '<td></td>';
1132 print '<td></td>';
1133 print '</tr>';
1134 $total += $obj->reel;
1135 if (price2num($object->pmp)) {
1136 $totalwithpmp += $obj->reel;
1137 }
1138 $totalvalue += ($object->pmp * $obj->reel);
1139 $totalvaluesell += ($object->price * $obj->reel);
1140 // Batch Detail
1141 if ((isModEnabled('productbatch')) && $object->hasbatch()) {
1142 $details = Productbatch::findAll($db, $obj->product_stock_id, 0, $object->id);
1143 if ($details < 0) {
1144 dol_print_error($db);
1145 }
1146 foreach ($details as $pdluo) {
1147 $product_lot_static->id = $pdluo->lotid;
1148 $product_lot_static->batch = $pdluo->batch;
1149 $product_lot_static->eatby = $pdluo->eatby;
1150 $product_lot_static->sellby = $pdluo->sellby;
1151
1152 if ($action == 'editline' && GETPOSTINT('lineid') == $pdluo->id) { //Current line edit
1153 print "\n".'<tr>';
1154 print '<td colspan="9">';
1155 print '<form action="'.$_SERVER["PHP_SELF"].'" method="POST">';
1156 print '<input type="hidden" name="token" value="'.newToken().'">';
1157 print '<input type="hidden" name="pdluoid" value="'.$pdluo->id.'"><input type="hidden" name="action" value="updateline"><input type="hidden" name="id" value="'.$id.'"><table class="noborder centpercent"><tr><td width="10%"></td>';
1158 print '<td class="right" width="10%"><input type="text" name="batch_number" value="'.$pdluo->batch.'"></td>';
1159 if (!getDolGlobalString('PRODUCT_DISABLE_SELLBY')) {
1160 print '<td class="center" width="10%">';
1161 print $form->selectDate($pdluo->sellby, 'sellby', 0, 0, 1, '', 1, 0);
1162 print '</td>';
1163 }
1164 if (!getDolGlobalString('PRODUCT_DISABLE_EATBY')) {
1165 print '<td class="center" width="10%">';
1166 print $form->selectDate($pdluo->eatby, 'eatby', 0, 0, 1, '', 1, 0);
1167 print '</td>';
1168 }
1169 print '<td class="right" colspan="3">'.$pdluo->qty.($pdluo->qty < 0 ? ' '.img_warning() : '').'</td>';
1170 print '<td colspan="4"><input type="submit" class="button button-save" id="savelinebutton marginbottomonly" name="save" value="'.$langs->trans("Save").'">';
1171 print '<input type="submit" class="button button-cancel" id="cancellinebutton" name="Cancel" value="'.$langs->trans("Cancel").'"></td></tr>';
1172 print '</table>';
1173 print '</form>';
1174 print '</td>';
1175 print '<td></td>';
1176 print '<td></td>';
1177 print '</tr>';
1178 } else {
1179 print "\n".'<tr style="display:'.(!getDolGlobalString('STOCK_SHOW_ALL_BATCH_BY_DEFAULT') ? 'none' : 'visible').';" class="batch_warehouse'.$entrepotstatic->id.'"><td class="left">';
1180 print '</td>';
1181 print '<td class="right nowraponall">';
1182 if ($product_lot_static->id > 0) {
1183 print $product_lot_static->getNomUrl(1);
1184 } else {
1185 print $product_lot_static->getNomUrl(1, 'nolink');
1186 }
1187 print '</td>';
1188 $colspan = 3;
1189 if (!getDolGlobalString('PRODUCT_DISABLE_SELLBY')) {
1190 $colspan--;
1191 print '<td class="center">'.dol_print_date($pdluo->sellby, 'day').'</td>';
1192 }
1193 if (!getDolGlobalString('PRODUCT_DISABLE_EATBY')) {
1194 $colspan--;
1195 print '<td class="center">'.dol_print_date($pdluo->eatby, 'day').'</td>';
1196 }
1197 print '<td class="right" colspan="'.$colspan.'">'.$pdluo->qty.($pdluo->qty < 0 ? ' '.img_warning() : (($pdluo->qty > 1 && $object->status_batch == 2) ? ' '.img_warning($langs->trans('IlligalQtyForSerialNumbers')) : '')).'</td>';
1198 print '<td colspan="4"></td>';
1199 print '<td class="center tdoverflowmax125" title="'.dol_escape_htmltag($langs->trans("TransferStock")).'">';
1200 if ($entrepotstatic->status != $entrepotstatic::STATUS_CLOSED) {
1201 print '<a href="'.$_SERVER["PHP_SELF"].'?id='.$object->id.'&id_entrepot='.$entrepotstatic->id.'&action=transfert&pdluoid='.$pdluo->id.'&token='.newToken().'">';
1202 print img_picto($langs->trans("TransferStock"), 'add', 'class="hideonsmartphone paddingright" style="color: #a69944"');
1203 print $langs->trans("TransferStock");
1204 print '</a>';
1205 // Disabled, because edition of stock content must use the "Correct stock menu".
1206 // Do not use this, or data will be wrong (bad tracking of movement label, inventory code, ...
1207 //print '<a href="'.$_SERVER["PHP_SELF"].'?id='.$id.'&action=editline&token='.newToken().'&lineid='.$pdluo->id.'#'.$pdluo->id.'">';
1208 //print img_edit().'</a>';
1209 }
1210 print '</td>';
1211 print '<td class="center tdoverflowmax125" title="'.dol_escape_htmltag($langs->trans("CorrectStock")).'">';
1212 if ($entrepotstatic->status != $entrepotstatic::STATUS_CLOSED) {
1213 print '<a href="'.$_SERVER["PHP_SELF"].'?id='.$object->id.'&id_entrepot='.$entrepotstatic->id.'&action=correction&pdluoid='.$pdluo->id.'&token='.newToken().'">';
1214 print img_picto($langs->trans("CorrectStock"), 'add', 'class="hideonsmartphone paddingright" style="color: #a69944"');
1215 print $langs->trans("CorrectStock");
1216 print '</a>';
1217 // Disabled, because edition of stock content must use the "Correct stock menu".
1218 // Do not use this, or data will be wrong (bad tracking of movement label, inventory code, ...
1219 //print '<a href="'.$_SERVER["PHP_SELF"].'?id='.$id.'&action=editline&token='.newToken().'&lineid='.$pdluo->id.'#'.$pdluo->id.'">';
1220 //print img_edit().'</a>';
1221 }
1222 print '</td>';
1223 print '</tr>';
1224 }
1225 }
1226 }
1227 $i++;
1228 }
1229 } else {
1230 dol_print_error($db);
1231 }
1232
1233 // Total line
1234 print '<tr class="liste_total"><td class="right liste_total" colspan="4">'.$langs->trans("Total").':</td>';
1235 print '<td class="liste_total right">'.price2num($total, 'MS').'</td>';
1236 print '<td class="liste_total right">';
1237 if ($usercancreadprice) {
1238 print($totalwithpmp ? price(price2num($totalvalue / $totalwithpmp, 'MU')) : '&nbsp;'); // This value may have rounding errors
1239 }
1240 print '</td>';
1241 // Value purchase
1242 print '<td class="liste_total right">';
1243 if ($usercancreadprice) {
1244 print $totalvalue ? price(price2num($totalvalue, 'MT'), 1) : '&nbsp;';
1245 }
1246 print '</td>';
1247 print '<td class="liste_total right">';
1248 if ($num) {
1249 if ($total) {
1250 print '<span class="valignmiddle">';
1251 if (getDolGlobalString('PRODUIT_MULTIPRICES')) {
1252 print $form->textwithpicto('', $langs->trans("Variable"));
1253 } elseif ($usercancreadprice) {
1254 print price($totalvaluesell / $total, 1);
1255 }
1256 print '</span>';
1257 }
1258 }
1259 print '</td>';
1260 // Value to sell
1261 print '<td class="liste_total right amount">';
1262 if ($num) {
1263 print '<span class="valignmiddle">';
1264 if (!getDolGlobalString('PRODUIT_MULTIPRICES') && $usercancreadprice) {
1265 print price(price2num($totalvaluesell, 'MT'), 1);
1266 } else {
1267 print $form->textwithpicto('', $langs->trans("Variable"));
1268 }
1269 print '</span>';
1270 }
1271 print '</td>';
1272 print '<td></td>';
1273 print '<td></td>';
1274 print "</tr>";
1275
1276 print "</table>";
1277 print '</div>';
1278
1279 if (getDolGlobalString('STOCK_ALLOW_ADD_LIMIT_STOCK_BY_WAREHOUSE')) {
1280 print '<br><br>';
1281 print load_fiche_titre($langs->trans('AddNewProductStockWarehouse'));
1282
1283 if ($user->hasRight('produit', 'creer')) {
1284 print '<form action="'.$_SERVER["PHP_SELF"].'" method="POST">';
1285 print '<input type="hidden" name="token" value="'.newToken().'">';
1286 print '<input type="hidden" name="action" value="addlimitstockwarehouse">';
1287 print '<input type="hidden" name="id" value="'.$id.'">';
1288 }
1289 print '<table class="noborder centpercent">';
1290 if ($user->hasRight('produit', 'creer')) {
1291 print '<tr class="liste_titre"><td>'.$formproduct->selectWarehouses('', 'fk_entrepot').'</td>';
1292 print '<td class="right"><input name="seuil_stock_alerte" type="text" placeholder="'.$langs->trans("StockLimit").'" /></td>';
1293 print '<td class="right"><input name="desiredstock" type="text" placeholder="'.$langs->trans("DesiredStock").'" /></td>';
1294 print '<td class="right"><input type="submit" value="'.$langs->trans("Save").'" class="button button-save" /></td>';
1295 print '</tr>';
1296 } else {
1297 print '<tr class="liste_titre"><td>'.$langs->trans("Warehouse").'</td>';
1298 print '<td class="right">'.$langs->trans("StockLimit").'</td>';
1299 print '<td class="right">'.$langs->trans("DesiredStock").'</td>';
1300 print '</tr>';
1301 }
1302
1303 $pse = new ProductStockEntrepot($db);
1304 $lines = $pse->fetchAll($id);
1305
1306 $visibleWarehouseEntities = explode(',', getEntity('stock')); // For MultiCompany compatibility
1307
1308 if (!empty($lines)) {
1309 $var = false;
1310 foreach ($lines as $line) {
1311 $ent = new Entrepot($db);
1312 $ent->fetch($line['fk_entrepot']);
1313
1314 if (!isModEnabled("multicompany") || in_array($ent->entity, $visibleWarehouseEntities)) {
1315 // Display only warehouses from our entity and entities sharing stock with actual entity
1316 print '<tr class="oddeven"><td>'.$ent->getNomUrl(3).'</td>';
1317 print '<td class="right">'.$line['seuil_stock_alerte'].'</td>';
1318 print '<td class="right">'.$line['desiredstock'].'</td>';
1319 if ($user->hasRight('produit', 'creer')) {
1320 print '<td class="right"><a href="'.$_SERVER['PHP_SELF'].'?id='.$id.'&fk_productstockwarehouse='.$line['id'].'&action=delete_productstockwarehouse&token='.newToken().'">'.img_delete().'</a></td>';
1321 }
1322 print '</tr>';
1323 }
1324 }
1325 }
1326
1327 print "</table>";
1328
1329 if ($user->hasRight('produit', 'creer')) {
1330 print '</form>';
1331 }
1332 }
1333} else {
1334 // List of variants
1335 include_once DOL_DOCUMENT_ROOT.'/variants/class/ProductCombination.class.php';
1336 include_once DOL_DOCUMENT_ROOT.'/variants/class/ProductCombination2ValuePair.class.php';
1337 $prodstatic = new Product($db);
1338 $prodcomb = new ProductCombination($db);
1339 $comb2val = new ProductCombination2ValuePair($db);
1340 $productCombinations = $prodcomb->fetchAllByFkProductParent($object->id);
1341
1342 print '<form method="POST" action="'.$_SERVER["PHP_SELF"].'">';
1343 print '<input type="hidden" name="token" value="'.newToken().'">';
1344 print '<input type="hidden" name="action" value="massaction">';
1345 print '<input type="hidden" name="id" value="'.$id.'">';
1346 print '<input type="hidden" name="backtopage" value="'.$backtopage.'">';
1347
1348 // load variants
1349 $title = $langs->trans("ProductCombinations");
1350
1351 print_barre_liste($title, 0, $_SERVER["PHP_SELF"], '', $sortfield, $sortorder, '', 0);
1352
1353 print '<div class="div-table-responsive">'; ?>
1354 <table class="liste">
1355 <tr class="liste_titre">
1356 <td class="liste_titre"><?php echo $langs->trans('Product') ?></td>
1357 <td class="liste_titre"><?php echo $langs->trans('Combination') ?></td>
1358 <td class="liste_titre center"><?php echo $langs->trans('OnSell') ?></td>
1359 <td class="liste_titre center"><?php echo $langs->trans('OnBuy') ?></td>
1360 <td class="liste_titre right"><?php echo $langs->trans('Stock') ?></td>
1361 <td class="liste_titre"></td>
1362 </tr>
1363 <?php
1364
1365 if (count($productCombinations)) {
1366 $stock_total = 0;
1367 foreach ($productCombinations as $currcomb) {
1368 $prodstatic->fetch($currcomb->fk_product_child);
1369 $prodstatic->load_stock();
1370 $stock_total += $prodstatic->stock_reel; ?>
1371 <tr class="oddeven">
1372 <td><?php echo $prodstatic->getNomUrl(1) ?></td>
1373 <td>
1374 <?php
1375
1376 $productCombination2ValuePairs = $comb2val->fetchByFkCombination($currcomb->id);
1377 $iMax = count($productCombination2ValuePairs);
1378
1379 for ($i = 0; $i < $iMax; $i++) {
1380 echo dol_htmlentities($productCombination2ValuePairs[$i]);
1381
1382 if ($i !== ($iMax - 1)) {
1383 echo ', ';
1384 }
1385 } ?>
1386 </td>
1387 <td style="text-align: center;"><?php echo $prodstatic->getLibStatut(2, 0) ?></td>
1388 <td style="text-align: center;"><?php echo $prodstatic->getLibStatut(2, 1) ?></td>
1389 <td class="right"><?php echo $prodstatic->stock_reel ?></td>
1390 <td class="right">
1391 <a class="paddingleft paddingright editfielda" href="<?php echo dol_buildpath('/product/stock/product.php?id='.$currcomb->fk_product_child, 2) ?>"><?php echo img_edit() ?></a>
1392 </td>
1393 <?php
1394 ?>
1395 </tr>
1396 <?php
1397 }
1398
1399 print '<tr class="liste_total">';
1400 print '<td colspan="4" class="left">'.$langs->trans("Total").'</td>';
1401 print '<td class="right">'.$stock_total.'</td>';
1402 print '<td></td>';
1403 print '</tr>';
1404 } else {
1405 print '<tr><td colspan="8"><span class="opacitymedium">'.$langs->trans("None").'</span></td></tr>';
1406 } ?>
1407 </table>
1408
1409 <?php
1410 print '</div>';
1411
1412 print '</form>';
1413}
1414
1415// End of page
1416llxFooter();
1417$db->close();
$id
Definition account.php:48
if( $user->socid > 0) if(! $user->hasRight('accounting', 'chartofaccount')) $object
Definition card.php:66
if(!defined('NOREQUIRESOC')) if(!defined( 'NOREQUIRETRAN')) if(!defined('NOTOKENRENEWAL')) if(!defined( 'NOREQUIREMENU')) if(!defined('NOREQUIREHTML')) if(!defined( 'NOREQUIREAJAX')) llxHeader($head='', $title='', $help_url='', $target='', $disablejs=0, $disablehead=0, $arrayofjs='', $arrayofcss='', $morequerystring='', $morecssonbody='', $replacemainareaby='', $disablenofollow=0, $disablenoindex=0)
Empty header.
Definition wrapper.php:71
Class to manage canvas.
Class to manage warehouses.
const STATUS_CLOSED
Closed status -> parcel was received by customer / end of process prev status : validated or shipment...
const STATUS_VALIDATED
Validated status -> parcel is ready to be sent prev status : draft next status : closed or shipment_i...
Class to manage standard extra fields.
Class to manage generation of HTML components Only common components must be here.
Class with static methods for building HTML components related to products Only components common to ...
Class to manage building of HTML components.
Class ProductCombination2ValuePair Used to represent the relation between a variant and its attribute...
Class ProductCombination Used to represent the relation between a product and one of its variants.
Class to manage predefined suppliers products.
Class to manage products or services.
const TYPE_PRODUCT
Regular product.
const TYPE_SERVICE
Service.
Class ProductStockEntrepot.
Manage record for batch number management.
static findAll($dbs, $fk_product_stock, $with_qty=0, $fk_product=0)
Return all batch detail records for a given product and warehouse.
Class with list of lots and properties.
llxFooter()
Footer empty.
Definition document.php:107
dol_mktime($hour, $minute, $second, $month, $day, $year, $gm='auto', $check=1)
Return a timestamp date built from detailed information (by default a local PHP server timestamp) Rep...
load_fiche_titre($title, $morehtmlright='', $picto='generic', $pictoisfullpath=0, $id='', $morecssontable='', $morehtmlcenter='')
Load a title with picto.
setEventMessages($mesg, $mesgs, $style='mesgs', $messagekey='', $noduplicate=0, $attop=0)
Set event messages in dol_events session object.
print_barre_liste($title, $page, $file, $options='', $sortfield='', $sortorder='', $morehtmlcenter='', $num=-1, $totalnboflines='', $picto='generic', $pictoisfullpath=0, $morehtmlright='', $morecss='', $limit=-1, $selectlimitsuffix=0, $hidenavigation=0, $pagenavastextinput=0, $morehtmlrightbeforearrow='')
Print a title with navigation controls for pagination.
img_warning($titlealt='default', $moreatt='', $morecss='pictowarning')
Show warning logo.
img_delete($titlealt='default', $other='class="pictodelete"', $morecss='')
Show delete logo.
img_picto($titlealt, $picto, $moreatt='', $pictoisfullpath=0, $srconly=0, $notitle=0, $alt='', $morecss='', $marginleftonlyshort=2)
Show picto whatever it's its name (generic function)
GETPOSTINT($paramname, $method=0)
Return the value of a $_GET or $_POST supervariable, converted into integer.
dol_get_fiche_head($links=array(), $active='', $title='', $notab=0, $picto='', $pictoisfullpath=0, $morehtmlright='', $morecss='', $limittoshow=0, $moretabssuffix='', $dragdropfile=0)
Show tabs of a record.
price2num($amount, $rounding='', $option=0)
Function that return a number with universal decimal format (decimal separator is '.
dol_get_fiche_end($notab=0)
Return tab footer of a card.
price($amount, $form=0, $outlangs='', $trunc=1, $rounding=-1, $forcerounding=-1, $currency_code='')
Function to format a value into an amount for visual output Function used into PDF and HTML pages.
dol_print_date($time, $format='', $tzoutput='auto', $outputlangs=null, $encodetooutput=false)
Output date in a string format according to outputlangs (or langs if not defined).
newToken()
Return the value of token currently saved into session with name 'newtoken'.
dol_htmlentities($string, $flags=ENT_QUOTES|ENT_SUBSTITUTE, $encoding='UTF-8', $double_encode=false)
Replace htmlentities functions.
dol_htmloutput_events($disabledoutputofmessages=0)
Print formatted messages to output (Used to show messages on html output).
GETPOST($paramname, $check='alphanohtml', $method=0, $filter=null, $options=null, $noreplace=0)
Return value of a param into GET or POST supervariable.
GETPOSTFLOAT($paramname, $rounding='')
Return the value of a $_GET or $_POST supervariable, converted into float.
dol_print_error($db=null, $error='', $errors=null)
Displays error message system with all the information to facilitate the diagnosis and the escalation...
dol_trunc($string, $size=40, $trunc='right', $stringencoding='UTF-8', $nodot=0, $display=0)
Truncate a string to a particular length adding '…' if string larger than length.
getDolGlobalString($key, $default='')
Return a Dolibarr global constant string value.
img_edit($titlealt='default', $float=0, $other='')
Show logo edit/modify fiche.
getEntity($element, $shared=1, $currentobject=null)
Get list of entity id to use.
treeview li table
No Email.
global $conf
The following vars must be defined: $type2label $form $conf, $lang, The following vars may also be de...
Definition member.php:79
product_prepare_head($object)
Prepare array with list of tabs.
if(preg_match('/crypted:/i', $dolibarr_main_db_pass)||!empty($dolibarr_main_db_encrypted_pass)) $conf db type
Definition repair.php:149
restrictedArea(User $user, $features, $object=0, $tableandshare='', $feature2='', $dbt_keyfield='fk_soc', $dbt_select='rowid', $isdraft=0, $mode=0)
Check permissions of a user to show a page and an object.