34 require
'../../main.inc.php';
35 require_once DOL_DOCUMENT_ROOT.
'/product/stock/class/entrepot.class.php';
36 require_once DOL_DOCUMENT_ROOT.
'/product/class/product.class.php';
37 require_once DOL_DOCUMENT_ROOT.
'/product/stock/class/productlot.class.php';
38 require_once DOL_DOCUMENT_ROOT.
'/fourn/class/fournisseur.product.class.php';
39 require_once DOL_DOCUMENT_ROOT.
'/core/lib/product.lib.php';
40 require_once DOL_DOCUMENT_ROOT.
'/product/class/html.formproduct.class.php';
41 require_once DOL_DOCUMENT_ROOT.
'/product/stock/class/productstockentrepot.class.php';
42 if (!empty($conf->productbatch->enabled)) {
43 require_once DOL_DOCUMENT_ROOT.
'/product/class/productbatch.class.php';
45 if (!empty($conf->project->enabled)) {
46 require_once DOL_DOCUMENT_ROOT.
'/core/class/html.formprojet.class.php';
47 require_once DOL_DOCUMENT_ROOT.
'/projet/class/project.class.php';
50 if (!empty($conf->variants->enabled)) {
51 require_once DOL_DOCUMENT_ROOT.
'/variants/class/ProductAttribute.class.php';
52 require_once DOL_DOCUMENT_ROOT.
'/variants/class/ProductAttributeValue.class.php';
53 require_once DOL_DOCUMENT_ROOT.
'/variants/class/ProductCombination.class.php';
54 require_once DOL_DOCUMENT_ROOT.
'/variants/class/ProductCombination2ValuePair.class.php';
58 $langs->loadlangs(array(
'products',
'suppliers',
'orders',
'bills',
'stocks',
'sendings',
'margins'));
59 if (!empty($conf->productbatch->enabled)) {
60 $langs->load(
"productbatch");
63 $backtopage =
GETPOST(
'backtopage',
'alpha');
64 $action =
GETPOST(
'action',
'aZ09');
65 $cancel =
GETPOST(
'cancel',
'alpha');
69 $stocklimit =
GETPOST(
'seuil_stock_alerte');
70 $desiredstock =
GETPOST(
'desiredstock');
71 $cancel =
GETPOST(
'cancel',
'alpha');
72 $fieldid = isset($_GET[
"ref"]) ?
'ref' :
'rowid';
75 $pdluoid =
GETPOST(
'pdluoid',
'int');
76 $batchnumber =
GETPOST(
'batch_number',
'san_alpha');
77 if (!empty($batchnumber)) {
78 $batchnumber = trim($batchnumber);
80 $cost_price =
GETPOST(
'cost_price',
'alpha');
84 $socid = $user->socid;
91 $extrafields->fetch_name_optionals_label($object->table_element);
93 if ($id > 0 || !empty($ref)) {
94 $result = $object->fetch($id, $ref);
97 if (empty($id) && !empty($object->id)) {
101 $modulepart =
'product';
104 $canvas = !empty($object->canvas) ? $object->canvas :
GETPOST(
"canvas");
106 if (!empty($canvas)) {
107 require_once DOL_DOCUMENT_ROOT.
'/core/class/canvas.class.php';
108 $objcanvas =
new Canvas($db, $action);
109 $objcanvas->getCanvas(
'stockproduct',
'card', $canvas);
113 $hookmanager->initHooks(array(
'stockproductcard',
'globalcard'));
120 if ($object->id > 0) {
121 if ($object->type == $object::TYPE_PRODUCT) {
122 restrictedArea($user,
'produit', $object->id,
'product&product',
'',
'');
124 if ($object->type == $object::TYPE_SERVICE) {
125 restrictedArea($user,
'service', $object->id,
'product&product',
'',
'');
128 restrictedArea($user,
'produit|service', $id,
'product&product',
'',
'', $fieldid);
140 $parameters = array(
'id'=>$id,
'ref'=>$ref,
'objcanvas'=>$objcanvas);
141 $reshook = $hookmanager->executeHooks(
'doActions', $parameters, $object, $action);
146 if ($action ==
'setcost_price') {
148 $result = $object->fetch($id);
149 $object->cost_price =
price2num($cost_price);
150 $result = $object->update($object->id, $user);
161 if ($action ==
'addlimitstockwarehouse' && !empty($user->rights->produit->creer)) {
162 $seuil_stock_alerte =
GETPOST(
'seuil_stock_alerte');
163 $desiredstock =
GETPOST(
'desiredstock');
166 if ($seuil_stock_alerte ==
'') {
167 setEventMessages($langs->trans(
"ErrorFieldRequired", $langs->transnoentitiesnoconv(
"StockLimit")),
null,
'errors');
170 if ($desiredstock ==
'') {
171 setEventMessages($langs->trans(
"ErrorFieldRequired", $langs->transnoentitiesnoconv(
"DesiredStock")),
null,
'errors');
177 if ($pse->fetch(0, $id,
GETPOST(
'fk_entrepot',
'int')) > 0) {
179 $pse->seuil_stock_alerte = $seuil_stock_alerte;
180 $pse->desiredstock = $desiredstock;
181 if ($pse->update($user) > 0) {
182 setEventMessages($langs->trans(
'ProductStockWarehouseUpdated'),
null,
'mesgs');
186 $pse->fk_entrepot =
GETPOST(
'fk_entrepot',
'int');
187 $pse->fk_product = $id;
188 $pse->seuil_stock_alerte =
GETPOST(
'seuil_stock_alerte');
189 $pse->desiredstock =
GETPOST(
'desiredstock');
190 if ($pse->create($user) > 0) {
191 setEventMessages($langs->trans(
'ProductStockWarehouseCreated'),
null,
'mesgs');
196 header(
"Location: ".$_SERVER[
"PHP_SELF"].
"?id=".$id);
200 if ($action ==
'delete_productstockwarehouse' && !empty($user->rights->produit->creer)) {
203 $pse->fetch(
GETPOST(
'fk_productstockwarehouse',
'int'));
204 if ($pse->delete($user) > 0) {
205 setEventMessages($langs->trans(
'ProductStockWarehouseDeleted'),
null,
'mesgs');
212 if ($action ==
'setseuil_stock_alerte' && !empty($user->rights->produit->creer)) {
214 $result = $object->fetch($id);
215 $object->seuil_stock_alerte = $stocklimit;
216 $result = $object->update($object->id, $user, 0,
'update');
226 if ($action ==
'setdesiredstock' && !empty($user->rights->produit->creer)) {
228 $result = $object->fetch($id);
229 $object->desiredstock = $desiredstock;
230 $result = $object->update($object->id, $user, 0,
'update');
239 if ($action ==
"correct_stock" && !$cancel) {
240 if (!(
GETPOST(
"id_entrepot",
'int') > 0)) {
241 setEventMessages($langs->trans(
"ErrorFieldRequired", $langs->transnoentitiesnoconv(
"Warehouse")),
null,
'errors');
243 $action =
'correction';
246 setEventMessages($langs->trans(
"ErrorFieldRequired", $langs->transnoentitiesnoconv(
"NumberOfUnit")),
null,
'errors');
248 $action =
'correction';
251 if (!empty($conf->productbatch->enabled)) {
253 $result = $object->fetch($id);
255 if ($object->hasbatch() && !$batchnumber) {
256 setEventMessages($langs->trans(
"ErrorFieldRequired", $langs->transnoentitiesnoconv(
"batch_number")),
null,
'errors');
258 $action =
'correction';
265 if (is_numeric($nbpiece) && $nbpiece != 0 && $id) {
266 $origin_element =
'';
269 if (
GETPOST(
'projectid',
'int')) {
270 $origin_element =
'project';
271 $origin_id =
GETPOST(
'projectid',
'int');
274 if (empty($object)) {
276 $result = $object->fetch($id);
279 $disablestockchangeforsubproduct = 0;
280 if (
GETPOST(
'disablesubproductstockchange')) {
281 $disablestockchangeforsubproduct = 1;
284 if ($object->hasbatch()) {
285 $result = $object->correct_stock_batch(
290 GETPOST(
"label",
'alphanohtml'),
295 GETPOST(
'inventorycode',
'alphanohtml'),
298 $disablestockchangeforsubproduct
301 $result = $object->correct_stock(
306 GETPOST(
"label",
'alphanohtml'),
308 GETPOST(
'inventorycode',
'alphanohtml'),
311 $disablestockchangeforsubproduct
317 header(
"Location: ".$backtopage);
320 header(
"Location: ".$_SERVER[
"PHP_SELF"].
"?id=".$object->id);
325 $action =
'correction';
332 if ($action ==
"transfert_stock" && !$cancel) {
333 if (!(
GETPOST(
"id_entrepot",
'int') > 0) || !(
GETPOST(
"id_entrepot_destination",
'int') > 0)) {
334 setEventMessages($langs->trans(
"ErrorFieldRequired", $langs->transnoentitiesnoconv(
"Warehouse")),
null,
'errors');
336 $action =
'transfert';
338 if (!
GETPOST(
"nbpiece",
'int')) {
339 setEventMessages($langs->trans(
"ErrorFieldRequired", $langs->transnoentitiesnoconv(
"NumberOfUnit")),
null,
'errors');
341 $action =
'transfert';
343 if (
GETPOST(
"id_entrepot",
'int') ==
GETPOST(
"id_entrepot_destination",
'int')) {
344 setEventMessages($langs->trans(
"ErrorSrcAndTargetWarehouseMustDiffers"),
null,
'errors');
346 $action =
'transfert';
348 if (!empty($conf->productbatch->enabled)) {
350 $result = $object->fetch($id);
352 if ($object->hasbatch() && !$batchnumber) {
353 setEventMessages($langs->trans(
"ErrorFieldRequired", $langs->transnoentitiesnoconv(
"batch_number")),
null,
'errors');
355 $action =
'transfert';
362 $result = $object->fetch($id);
366 $object->load_stock(
'novirtual');
370 if (isset($object->pmp)) {
371 $pricesrc = $object->pmp;
373 $pricedest = $pricesrc;
377 if ($object->hasbatch()) {
381 $result = $pdluo->fetch($pdluoid);
383 $srcwarehouseid = $pdluo->warehouseid;
384 $batch = $pdluo->batch;
385 $eatby = $pdluo->eatby;
386 $sellby = $pdluo->sellby;
392 $srcwarehouseid =
GETPOST(
'id_entrepot',
'int');
393 $batch = $batchnumber;
402 $result1 = $object->correct_stock_batch(
407 GETPOST(
"label",
'alphanohtml'),
412 GETPOST(
'inventorycode',
'alphanohtml')
420 $result2 = $object->correct_stock_batch(
422 GETPOST(
"id_entrepot_destination",
'int'),
425 GETPOST(
"label",
'alphanohtml'),
430 GETPOST(
'inventorycode',
'alphanohtml')
439 $result1 = $object->correct_stock(
444 GETPOST(
"label",
'alphanohtml'),
446 GETPOST(
'inventorycode',
'alphanohtml')
454 $result2 = $object->correct_stock(
456 GETPOST(
"id_entrepot_destination",
'int'),
459 GETPOST(
"label",
'alphanohtml'),
461 GETPOST(
'inventorycode',
'alphanohtml')
470 if (!$error && $result1 >= 0 && $result2 >= 0) {
474 header(
"Location: ".$backtopage);
477 header(
"Location: product.php?id=".$object->id);
483 $action =
'transfert';
490 if ($action ==
'updateline' &&
GETPOST(
'save') == $langs->trans(
"Save")) {
492 $result = $pdluo->fetch(
GETPOST(
'pdluoid',
'int'));
496 if ((!
GETPOST(
"sellby")) && (!
GETPOST(
"eatby")) && (!$batchnumber)) {
497 setEventMessages($langs->trans(
"ErrorFieldRequired", $langs->transnoentitiesnoconv(
"atleast1batchfield")),
null,
'errors');
501 $pdluo->batch = $batchnumber;
502 $pdluo->eatby = $d_eatby;
503 $pdluo->sellby = $d_sellby;
504 $result = $pdluo->update($user);
510 setEventMessages($langs->trans(
'BatchInformationNotfound'),
null,
'errors');
515 header(
"Location: product.php?id=".$id);
527 if (!empty($conf->project->enabled)) {
531 if ($id > 0 || $ref) {
533 $result = $object->fetch($id, $ref);
535 $variants = $object->hasVariants();
537 $object->load_stock();
539 $title = $langs->trans(
'ProductServiceCard');
541 $shortlabel =
dol_trunc($object->label, 16);
543 $title = $langs->trans(
'Product').
" ".$shortlabel.
" - ".$langs->trans(
'Stock');
544 $helpurl =
'EN:Module_Products|FR:Module_Produits|ES:Módulo_Productos';
547 $title = $langs->trans(
'Service').
" ".$shortlabel.
" - ".$langs->trans(
'Stock');
548 $helpurl =
'EN:Module_Services_En|FR:Module_Services|ES:Módulo_Servicios';
553 if (!empty($conf->use_javascript_ajax)) {
555 <script
type=
"text/javascript">
556 $(document).ready(
function() {
557 $(
".collapse_batch").click(
function() {
558 console.log(
"We click on collapse_batch");
559 var id_entrepot = $(
this).attr(
'id').replace(
'ent',
'');
561 if($(
this).text().indexOf(
'+') > 0) {
562 $(
".batch_warehouse" + id_entrepot).show();
564 jQuery(
"#show_all").hide();
565 jQuery(
"#hide_all").show();
568 $(
".batch_warehouse" + id_entrepot).hide();
575 $(
"#show_all").click(
function() {
576 console.log(
"We click on show_all");
577 $(
"[class^=batch_warehouse]").show();
578 $(
"[class^=collapse_batch]").html(
'(-)');
579 jQuery(
"#show_all").hide();
580 jQuery(
"#hide_all").show();
584 $(
"#hide_all").click(
function() {
585 console.log(
"We click on hide_all");
586 $(
"[class^=batch_warehouse]").hide();
587 $(
"[class^=collapse_batch]").html(
'(+)');
588 jQuery(
"#hide_all").hide();
589 jQuery(
"#show_all").show();
600 $titre = $langs->trans(
"CardProduct".$object->type);
607 $linkback =
'<a href="'.DOL_URL_ROOT.
'/product/list.php?restore_lastsearch_values=1">'.$langs->trans(
"BackToList").
'</a>';
610 if ($user->socid && !in_array(
'stock', explode(
',', $conf->global->MAIN_MODULES_FOR_EXTERNAL))) {
617 print
'<div class="fichecenter">';
619 print
'<div class="fichehalfleft">';
620 print
'<div class="underbanner clearboth"></div>';
622 print
'<table class="border tableforfield centpercent">';
625 if (!empty($conf->product->enabled) && !empty($conf->service->enabled)) {
626 $typeformat =
'select;0:'.$langs->trans(
"Product").
',1:'.$langs->trans(
"Service");
627 print
'<tr><td class="">';
628 print (empty($conf->global->PRODUCT_DENY_CHANGE_PRODUCT_TYPE)) ?
$form->editfieldkey(
"Type",
'fk_product_type', $object->type, $object, 0, $typeformat) : $langs->trans(
'Type');
630 print
$form->editfieldval(
"Type",
'fk_product_type', $object->type, $object, 0, $typeformat);
635 print
'<tr><td class="">'.$langs->trans(
"ManageLotSerial").
'</td><td>';
636 print $object->getLibStatut(0, 2);
642 $textdesc = $langs->trans(
"CostPriceDescription");
643 $textdesc .=
"<br>".$langs->trans(
"CostPriceUsage");
644 $text =
$form->textwithpicto($langs->trans(
"CostPrice"), $textdesc, 1,
'help',
'');
645 print
$form->editfieldkey($text,
'cost_price', $object->cost_price, $object, $usercancreate,
'amount:6');
647 print
$form->editfieldval($text,
'cost_price', $object->cost_price, $object, $usercancreate,
'amount:6');
651 print
'<tr><td class="titlefield">';
652 print
$form->textwithpicto($langs->trans(
"AverageUnitPricePMPShort"), $langs->trans(
"AverageUnitPricePMPDesc"));
655 if ($object->pmp > 0) {
656 print
price($object->pmp).
' '.$langs->trans(
"HT");
662 print
'<tr><td>'.$langs->trans(
"BuyingPriceMin").
'</td>';
665 if ($product_fourn->find_min_price_product_fournisseur($object->id) > 0) {
666 if ($product_fourn->product_fourn_price_id > 0) {
667 print $product_fourn->display_price_product_fournisseur();
669 print $langs->trans(
"NotDefined");
674 if (empty($conf->global->PRODUIT_MULTIPRICES)) {
676 print
'<tr><td>'.$langs->trans(
"SellingPrice").
'</td><td>';
677 if ($object->price_base_type ==
'TTC') {
678 print
price($object->price_ttc).
' '.$langs->trans($object->price_base_type);
680 print
price($object->price).
' '.$langs->trans($object->price_base_type);
685 print
'<tr><td>'.$langs->trans(
"MinPrice").
'</td><td>';
686 if ($object->price_base_type ==
'TTC') {
687 print
price($object->price_min_ttc).
' '.$langs->trans($object->price_base_type);
689 print
price($object->price_min).
' '.$langs->trans($object->price_base_type);
694 print
'<tr><td>'.$langs->trans(
"SellingPrice").
'</td><td>';
695 print
'<span class="opacitymedium">'.$langs->trans(
"Variable").
'</span>';
699 print
'<tr><td>'.$langs->trans(
"MinPrice").
'</td><td>';
700 print
'<span class="opacitymedium">'.$langs->trans(
"Variable").
'</span>';
705 $parameters = array();
706 $reshook = $hookmanager->executeHooks(
'formObjectOptions', $parameters, $object, $action);
707 print $hookmanager->resPrint;
712 print
'<div class="fichehalfright"><div class="underbanner clearboth"></div>';
714 print
'<table class="border tableforfield centpercent">';
717 print
'<tr><td>'.$form->editfieldkey(
$form->textwithpicto($langs->trans(
"StockLimit"), $langs->trans(
"StockLimitDesc"), 1),
'seuil_stock_alerte', $object->seuil_stock_alerte, $object, $user->rights->produit->creer).
'</td><td>';
718 print
$form->editfieldval(
"StockLimit",
'seuil_stock_alerte', $object->seuil_stock_alerte, $object, $user->rights->produit->creer,
'string');
722 print
'<tr><td>'.$form->editfieldkey(
$form->textwithpicto($langs->trans(
"DesiredStock"), $langs->trans(
"DesiredStockDesc"), 1),
'desiredstock', $object->desiredstock, $object, $user->rights->produit->creer);
724 print
$form->editfieldval(
"DesiredStock",
'desiredstock', $object->desiredstock, $object, $user->rights->produit->creer,
'string');
728 $text_stock_options = $langs->trans(
"RealStockDesc").
'<br>';
729 $text_stock_options .= $langs->trans(
"RealStockWillAutomaticallyWhen").
'<br>';
730 $text_stock_options .= (!empty($conf->global->STOCK_CALCULATE_ON_SHIPMENT) || !empty($conf->global->STOCK_CALCULATE_ON_SHIPMENT_CLOSE) ?
'- '.$langs->trans(
"DeStockOnShipment").
'<br>' :
'');
731 $text_stock_options .= (!empty($conf->global->STOCK_CALCULATE_ON_VALIDATE_ORDER) ?
'- '.$langs->trans(
"DeStockOnValidateOrder").
'<br>' :
'');
732 $text_stock_options .= (!empty($conf->global->STOCK_CALCULATE_ON_BILL) ?
'- '.$langs->trans(
"DeStockOnBill").
'<br>' :
'');
733 $text_stock_options .= (!empty($conf->global->STOCK_CALCULATE_ON_SUPPLIER_BILL) ?
'- '.$langs->trans(
"ReStockOnBill").
'<br>' :
'');
734 $text_stock_options .= (!empty($conf->global->STOCK_CALCULATE_ON_SUPPLIER_VALIDATE_ORDER) ?
'- '.$langs->trans(
"ReStockOnValidateOrder").
'<br>' :
'');
735 $text_stock_options .= (!empty($conf->global->STOCK_CALCULATE_ON_SUPPLIER_DISPATCH_ORDER) ?
'- '.$langs->trans(
"ReStockOnDispatchOrder").
'<br>' :
'');
736 $text_stock_options .= (!empty($conf->global->STOCK_CALCULATE_ON_RECEPTION) || !empty($conf->global->STOCK_CALCULATE_ON_RECEPTION_CLOSE) ?
'- '.$langs->trans(
"StockOnReception").
'<br>' :
'');
739 print
$form->textwithpicto($langs->trans(
"PhysicalStock"), $text_stock_options, 1);
741 print
'<td>'.price2num($object->stock_reel,
'MS');
742 if ($object->seuil_stock_alerte !=
'' && ($object->stock_reel < $object->seuil_stock_alerte)) {
743 print
' '.img_warning($langs->trans(
"StockLowerThanLimit", $object->seuil_stock_alerte));
746 print
' <a href="'.DOL_URL_ROOT.
'/product/stock/stockatdate.php?productid='.$object->id.
'">'.$langs->trans(
"StockAtDate").
'</a>';
750 $stocktheo =
price2num($object->stock_theorique,
'MS');
753 $helpondiff =
'<strong>'.$langs->trans(
"StockDiffPhysicTeoric").
':</strong><br>';
755 if (!empty($conf->commande->enabled)) {
757 $helpondiff .=
'<br>';
761 $helpondiff .= $langs->trans(
"ProductQtyInCustomersOrdersRunning").
': '.$object->stats_commande[
'qty'];
762 $result = $object->load_stats_commande(0,
'0', 1);
766 $helpondiff .=
' <span class="opacitymedium">('.$langs->trans(
"ProductQtyInDraft").
': '.$object->stats_commande[
'qty'].
')</span>';
770 if (!empty($conf->expedition->enabled)) {
771 require_once DOL_DOCUMENT_ROOT.
'/expedition/class/expedition.class.php';
772 $filterShipmentStatus =
'';
773 if (!empty($conf->global->STOCK_CALCULATE_ON_SHIPMENT)) {
775 } elseif (!empty($conf->global->STOCK_CALCULATE_ON_SHIPMENT_CLOSE)) {
779 $helpondiff .=
'<br>';
783 $result = $object->load_stats_sending(0,
'2', 1, $filterShipmentStatus);
784 $helpondiff .= $langs->trans(
"ProductQtyInShipmentAlreadySent").
': '.$object->stats_expedition[
'qty'];
788 if ((!empty($conf->fournisseur->enabled) && empty($conf->global->MAIN_USE_NEW_SUPPLIERMOD)) || !empty($conf->supplier_order->enabled) || !empty($conf->supplier_invoice->enabled)) {
790 $helpondiff .=
'<br>';
794 $result = $object->load_stats_commande_fournisseur(0,
'3,4', 1);
795 $helpondiff .= $langs->trans(
"ProductQtyInSuppliersOrdersRunning").
': '.$object->stats_commande_fournisseur[
'qty'];
796 $result = $object->load_stats_commande_fournisseur(0,
'0,1,2', 1);
800 $helpondiff .=
' <span class="opacitymedium">('.$langs->trans(
"ProductQtyInDraftOrWaitingApproved").
': '.$object->stats_commande_fournisseur[
'qty'].
')</span>';
804 if ((!empty($conf->fournisseur->enabled) && empty($conf->global->MAIN_USE_NEW_SUPPLIERMOD)) || !empty($conf->supplier_order->enabled) || !empty($conf->supplier_invoice->enabled)) {
806 $helpondiff .=
'<br>';
810 $helpondiff .= $langs->trans(
"ProductQtyInSuppliersShipmentAlreadyRecevied").
': '.$object->stats_reception[
'qty'];
814 if (!empty($conf->mrp->enabled)) {
816 $helpondiff .=
'<br>';
820 $helpondiff .= $langs->trans(
"ProductQtyToConsumeByMO").
': '.$object->stats_mrptoconsume[
'qty'].
'<br>';
821 $helpondiff .= $langs->trans(
"ProductQtyToProduceByMO").
': '.$object->stats_mrptoproduce[
'qty'];
827 print
$form->textwithpicto($langs->trans(
"VirtualStock"), $langs->trans(
"VirtualStockDesc"));
831 print
$form->textwithpicto((empty($stocktheo) ? 0 : $stocktheo), $helpondiff);
832 if ($object->seuil_stock_alerte !=
'' && ($object->stock_theorique < $object->seuil_stock_alerte)) {
833 print
' '.img_warning($langs->trans(
"StockLowerThanLimit", $object->seuil_stock_alerte));
835 print
' <a href="'.DOL_URL_ROOT.
'/product/stock/stockatdate.php?mode=future&productid='.$object->id.
'">'.$langs->trans(
"VirtualStockAtDate").
'</a>';
840 if (!empty($user->rights->stock->mouvement->lire)) {
841 $sql =
"SELECT max(m.datem) as datem";
842 $sql .=
" FROM ".MAIN_DB_PREFIX.
"stock_mouvement as m";
843 $sql .=
" WHERE m.fk_product = ".((int) $object->id);
844 $resqlbis = $db->query($sql);
846 $obj = $db->fetch_object($resqlbis);
847 $lastmovementdate = $db->jdate($obj->datem);
851 print
'<tr><td class="tdtop">'.$langs->trans(
"LastMovement").
'</td><td>';
852 if ($lastmovementdate) {
854 print
' ';
855 print
img_picto($langs->trans(
"StockMovement"),
'movement',
'class="pictofixedwidth"');
856 print
'<a href="'.DOL_URL_ROOT.
'/product/stock/movement_list.php?idproduct='.$object->id.
'">'.$langs->trans(
"FullList").
'</a>';
858 print
img_picto($langs->trans(
"StockMovement"),
'movement',
'class="pictofixedwidth"');
859 print
'<a href="'.DOL_URL_ROOT.
'/product/stock/movement_list.php?idproduct='.$object->id.
'">'.$langs->trans(
"None").
'</a>';
869 print
'<div style="clear:both"></div>';
876 if ($action ==
"correction") {
877 include DOL_DOCUMENT_ROOT.
'/product/stock/tpl/stockcorrection.tpl.php';
882 if ($action ==
"transfert") {
883 include DOL_DOCUMENT_ROOT.
'/product/stock/tpl/stocktransfer.tpl.php';
893 $parameters = array();
895 $reshook = $hookmanager->executeHooks(
'addMoreActionsButtons', $parameters, $object, $action);
896 if (empty($reshook)) {
897 if (empty($action) && $object->id) {
898 print
"<div class=\"tabsAction\">\n";
900 if ($user->rights->stock->mouvement->creer) {
901 if (!$variants || !empty($conf->global->VARIANT_ALLOW_STOCK_MOVEMENT_ON_VARIANT_PARENT)) {
902 print
'<a class="butAction" href="'.$_SERVER[
"PHP_SELF"].
'?id='.$object->id.
'&action=transfert">'.$langs->trans(
"TransferStock").
'</a>';
904 print
'<a class="butActionRefused classfortooltip" href="#" title="'.$langs->trans(
"ActionAvailableOnVariantProductOnly").
'">'.$langs->trans(
"TransferStock").
'</a>';
907 print
'<a class="butActionRefused classfortooltip" href="#" title="'.$langs->trans(
"NotEnoughPermissions").
'">'.$langs->trans(
"CorrectStock").
'</a>';
910 if ($user->rights->stock->mouvement->creer) {
911 if (!$variants || !empty($conf->global->VARIANT_ALLOW_STOCK_MOVEMENT_ON_VARIANT_PARENT)) {
912 print
'<a class="butAction" href="'.$_SERVER[
"PHP_SELF"].
'?id='.$object->id.
'&action=correction">'.$langs->trans(
"CorrectStock").
'</a>';
914 print
'<a class="butActionRefused classfortooltip" href="#" title="'.$langs->trans(
"ActionAvailableOnVariantProductOnly").
'">'.$langs->trans(
"CorrectStock").
'</a>';
917 print
'<a class="butActionRefused classfortooltip" href="#" title="'.$langs->trans(
"NotEnoughPermissions").
'">'.$langs->trans(
"CorrectStock").
'</a>';
930 print
'<div class="div-table-responsive">';
931 print
'<table class="noborder centpercent">';
933 print
'<tr class="liste_titre">';
934 print
'<td colspan="4">'.$langs->trans(
"Warehouse").
'</td>';
935 print
'<td class="right">'.$langs->trans(
"NumberOfUnit").
'</td>';
936 print
'<td class="right">'.$form->textwithpicto($langs->trans(
"AverageUnitPricePMPShort"), $langs->trans(
"AverageUnitPricePMPDesc")).
'</td>';
937 print
'<td class="right">'.$langs->trans(
"EstimatedStockValueShort").
'</td>';
938 print
'<td class="right">'.$langs->trans(
"SellPriceMin").
'</td>';
939 print
'<td class="right">'.$langs->trans(
"EstimatedStockValueSellShort").
'</td>';
944 if ((!empty($conf->productbatch->enabled)) && $object->hasbatch()) {
946 print
'<tr class="liste_titre"><td class="minwidth200">';
947 if (!empty($conf->use_javascript_ajax)) {
948 print
'<a id="show_all" href="#" class="hideobject">'.img_picto(
'',
'folder-open',
'class="paddingright"').$langs->trans(
"ShowAllLots").
'</a>';
950 print
'<a id="hide_all" href="#">'.img_picto(
'',
'folder',
'class="paddingright"').$langs->trans(
"HideLots").
'</a>';
954 print
'<td class="right">'.$langs->trans(
"batch_number").
'</td>';
955 if (empty($conf->global->PRODUCT_DISABLE_SELLBY)) {
957 print
'<td class="center width100">'.$langs->trans(
"SellByDate").
'</td>';
959 if (empty($conf->global->PRODUCT_DISABLE_EATBY)) {
961 print
'<td class="center width100">'.$langs->trans(
"EatByDate").
'</td>';
963 print
'<td colspan="'.$colspan.
'"></td>';
973 $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";
974 $sql .=
" FROM ".MAIN_DB_PREFIX.
"entrepot as e,";
975 $sql .=
" ".MAIN_DB_PREFIX.
"product_stock as ps";
976 $sql .=
" LEFT JOIN ".MAIN_DB_PREFIX.
"product as p ON p.rowid = ps.fk_product";
977 $sql .=
" WHERE ps.reel != 0";
978 $sql .=
" AND ps.fk_entrepot = e.rowid";
979 $sql .=
" AND e.entity IN (".getEntity(
'stock').
")";
980 $sql .=
" AND ps.fk_product = ".((int) $object->id);
981 $sql .=
" ORDER BY e.ref";
983 $entrepotstatic =
new Entrepot($db);
988 $totalvalue = $totalvaluesell = 0;
991 $resql = $db->query($sql);
993 $num = $db->num_rows(
$resql);
994 $total = $totalwithpmp;
998 $obj = $db->fetch_object(
$resql);
1000 $entrepotstatic->id = $obj->rowid;
1001 $entrepotstatic->ref = $obj->ref;
1002 $entrepotstatic->label = $obj->ref;
1003 $entrepotstatic->lieu = $obj->lieu;
1004 $entrepotstatic->fk_parent = $obj->fk_parent;
1005 $entrepotstatic->statut = $obj->status;
1006 $entrepotstatic->status = $obj->status;
1008 $stock_real =
price2num($obj->reel,
'MS');
1009 print
'<tr class="oddeven">';
1012 print
'<td colspan="4">';
1013 print $entrepotstatic->getNomUrl(1);
1014 if (!empty($conf->use_javascript_ajax) && !empty($conf->productbatch->enabled) && $object->hasbatch()) {
1015 print
'<a class="collapse_batch marginleftonly" id="ent' . $entrepotstatic->id .
'" href="#">';
1016 print (empty($conf->global->STOCK_SHOW_ALL_BATCH_BY_DEFAULT) ?
'(+)' :
'(-)');
1021 print
'<td class="right">'.$stock_real.($stock_real < 0 ?
' '.img_warning() :
'').
'</td>';
1024 print
'<td class="right nowraponall">'.(price2num($object->pmp) ?
price2num($object->pmp,
'MU') :
'').
'</td>';
1027 print
'<td class="right amount nowraponall">'.(price2num($object->pmp) ?
price(
price2num($object->pmp * $obj->reel,
'MT')) :
'').
'</td>';
1030 $minsellprice =
null; $maxsellprice =
null;
1031 print
'<td class="right">';
1032 if (!empty($conf->global->PRODUIT_MULTIPRICES)) {
1033 foreach ($object->multiprices as $priceforlevel) {
1034 if (is_numeric($priceforlevel)) {
1035 if (is_null($maxsellprice) || $priceforlevel > $maxsellprice) {
1036 $maxsellprice = $priceforlevel;
1038 if (is_null($minsellprice) || $priceforlevel < $minsellprice) {
1039 $minsellprice = $priceforlevel;
1043 print
'<span class="valignmiddle">';
1044 if ($minsellprice != $maxsellprice) {
1050 print
$form->textwithpicto(
'', $langs->trans(
"Variable"));
1057 print
'<td class="right amount nowraponall">';
1058 if (!empty($conf->global->PRODUIT_MULTIPRICES)) {
1059 print
'<span class="valignmiddle">';
1060 if ($minsellprice != $maxsellprice) {
1066 print
$form->textwithpicto(
'', $langs->trans(
"Variable"));
1074 $total += $obj->reel;
1076 $totalwithpmp += $obj->reel;
1078 $totalvalue = $totalvalue + ($object->pmp * $obj->reel);
1079 $totalvaluesell = $totalvaluesell + ($object->price * $obj->reel);
1081 if ((!empty($conf->productbatch->enabled)) && $object->hasbatch()) {
1086 foreach ($details as $pdluo) {
1087 $product_lot_static->id = $pdluo->lotid;
1088 $product_lot_static->batch = $pdluo->batch;
1089 $product_lot_static->eatby = $pdluo->eatby;
1090 $product_lot_static->sellby = $pdluo->sellby;
1092 if ($action ==
'editline' &&
GETPOST(
'lineid',
'int') == $pdluo->id) {
1094 print
'<td colspan="9">';
1095 print
'<form action="'.$_SERVER[
"PHP_SELF"].
'" method="POST">';
1096 print
'<input type="hidden" name="token" value="'.newToken().
'">';
1097 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>';
1098 print
'<td class="right" width="10%"><input type="text" name="batch_number" value="'.$pdluo->batch.
'"></td>';
1099 if (empty($conf->global->PRODUCT_DISABLE_SELLBY)) {
1100 print
'<td class="center" width="10%">';
1101 print
$form->selectDate($pdluo->sellby,
'sellby',
'',
'', 1,
'', 1, 0);
1104 if (empty($conf->global->PRODUCT_DISABLE_EATBY)) {
1105 print
'<td class="center" width="10%">';
1106 print
$form->selectDate($pdluo->eatby,
'eatby',
'',
'', 1,
'', 1, 0);
1109 print
'<td class="right" colspan="3">'.$pdluo->qty.($pdluo->qty < 0 ?
' '.img_warning() :
'').
'</td>';
1110 print
'<td colspan="4"><input type="submit" class="button button-save" id="savelinebutton marginbottomonly" name="save" value="'.$langs->trans(
"Save").
'">';
1111 print
'<input type="submit" class="button button-cancel" id="cancellinebutton" name="Cancel" value="'.$langs->trans(
"Cancel").
'"></td></tr>';
1119 print
"\n".
'<tr style="display:'.(empty($conf->global->STOCK_SHOW_ALL_BATCH_BY_DEFAULT) ?
'none' :
'visible').
';" class="batch_warehouse'.$entrepotstatic->id.
'"><td class="left">';
1121 print
'<td class="right nowraponall">';
1122 print $product_lot_static->getNomUrl(1);
1125 if (empty($conf->global->PRODUCT_DISABLE_SELLBY)) {
1127 print
'<td class="center">'.dol_print_date($pdluo->sellby,
'day').
'</td>';
1129 if (empty($conf->global->PRODUCT_DISABLE_EATBY)) {
1131 print
'<td class="center">'.dol_print_date($pdluo->eatby,
'day').
'</td>';
1133 print
'<td class="right" colspan="'.$colspan.
'">'.$pdluo->qty.($pdluo->qty < 0 ?
' '.img_warning() :
'').
'</td>';
1134 print
'<td colspan="4"></td>';
1135 print
'<td class="center">';
1136 if ($entrepotstatic->status != $entrepotstatic::STATUS_CLOSED) {
1137 print
'<a href="'.$_SERVER[
"PHP_SELF"].
'?id='.$object->id.
'&id_entrepot='.$entrepotstatic->id.
'&action=transfert&pdluoid='.$pdluo->id.
'">';
1138 print
img_picto($langs->trans(
"TransferStock"),
'add',
'class="hideonsmartphone paddingright" style="color: #a69944"');
1139 print $langs->trans(
"TransferStock");
1147 print
'<td class="center">';
1148 if ($entrepotstatic->status != $entrepotstatic::STATUS_CLOSED) {
1149 print
'<a href="'.$_SERVER[
"PHP_SELF"].
'?id='.$object->id.
'&id_entrepot='.$entrepotstatic->id.
'&action=correction&pdluoid='.$pdluo->id.
'">';
1150 print
img_picto($langs->trans(
"CorrectStock"),
'add',
'class="hideonsmartphone paddingright" style="color: #a69944"');
1151 print $langs->trans(
"CorrectStock");
1170 print
'<tr class="liste_total"><td class="right liste_total" colspan="4">'.$langs->trans(
"Total").
':</td>';
1171 print
'<td class="liste_total right">'.price2num($total,
'MS').
'</td>';
1172 print
'<td class="liste_total right">';
1173 print ($totalwithpmp ?
price(
price2num($totalvalue / $totalwithpmp,
'MU')) :
' ');
1176 print
'<td class="liste_total right">';
1177 print $totalvalue ?
price(
price2num($totalvalue,
'MT'), 1) :
' ';
1179 print
'<td class="liste_total right">';
1182 print
'<span class="valignmiddle">';
1183 if (!empty($conf->global->PRODUIT_MULTIPRICES)) {
1184 print
$form->textwithpicto(
'', $langs->trans(
"Variable"));
1186 print
price($totalvaluesell / $total, 1);
1193 print
'<td class="liste_total right amount">';
1195 print
'<span class="valignmiddle">';
1196 if (empty($conf->global->PRODUIT_MULTIPRICES)) {
1199 print
$form->textwithpicto(
'', $langs->trans(
"Variable"));
1211 if (!empty($conf->global->STOCK_ALLOW_ADD_LIMIT_STOCK_BY_WAREHOUSE)) {
1215 if (!empty($user->rights->produit->creer)) {
1216 print
'<form action="'.$_SERVER[
"PHP_SELF"].
'" method="POST">';
1217 print
'<input type="hidden" name="token" value="'.newToken().
'">';
1218 print
'<input type="hidden" name="action" value="addlimitstockwarehouse">';
1219 print
'<input type="hidden" name="id" value="'.$id.
'">';
1221 print
'<table class="noborder centpercent">';
1222 if (!empty($user->rights->produit->creer)) {
1223 print
'<tr class="liste_titre"><td>'.$formproduct->selectWarehouses(
'',
'fk_entrepot').
'</td>';
1224 print
'<td class="right"><input name="seuil_stock_alerte" type="text" placeholder="'.$langs->trans(
"StockLimit").
'" /></td>';
1225 print
'<td class="right"><input name="desiredstock" type="text" placeholder="'.$langs->trans(
"DesiredStock").
'" /></td>';
1226 print
'<td class="right"><input type="submit" value="'.$langs->trans(
"Save").
'" class="button button-save" /></td>';
1229 print
'<tr class="liste_titre"><td>'.$langs->trans(
"Warehouse").
'</td>';
1230 print
'<td class="right">'.$langs->trans(
"StockLimit").
'</td>';
1231 print
'<td class="right">'.$langs->trans(
"DesiredStock").
'</td>';
1236 $lines = $pse->fetchAll($id);
1238 if (!empty($lines)) {
1240 foreach ($lines as $line) {
1242 $ent->fetch($line[
'fk_entrepot']);
1243 print
'<tr class="oddeven"><td>'.$ent->getNomUrl(3).
'</td>';
1244 print
'<td class="right">'.$line[
'seuil_stock_alerte'].
'</td>';
1245 print
'<td class="right">'.$line[
'desiredstock'].
'</td>';
1246 if (!empty($user->rights->produit->creer)) {
1247 print
'<td class="right"><a href="'.$_SERVER[
'PHP_SELF'].
'?id='.$id.
'&fk_productstockwarehouse='.$line[
'id'].
'&action=delete_productstockwarehouse&token='.
newToken().
'">'.
img_delete().
'</a></td>';
1255 if (!empty($user->rights->produit->creer)) {
1261 include_once DOL_DOCUMENT_ROOT.
'/variants/class/ProductCombination.class.php';
1262 include_once DOL_DOCUMENT_ROOT.
'/variants/class/ProductCombination2ValuePair.class.php';
1263 $prodstatic =
new Product($db);
1266 $productCombinations = $prodcomb->fetchAllByFkProductParent($object->id);
1268 print
'<form method="POST" action="'.$_SERVER[
"PHP_SELF"].
'">';
1269 print
'<input type="hidden" name="token" value="'.newToken().
'">';
1270 print
'<input type="hidden" name="action" value="massaction">';
1271 print
'<input type="hidden" name="id" value="'.$id.
'">';
1272 print
'<input type="hidden" name="backtopage" value="'.$backtopage.
'">';
1275 $title = $langs->trans(
"ProductCombinations");
1277 print_barre_liste($title, 0, $_SERVER[
"PHP_SELF"],
'', $sortfield, $sortorder,
'', 0);
1279 print
'<div class="div-table-responsive">';
1281 <table
class=
"liste">
1282 <tr
class=
"liste_titre">
1283 <td
class=
"liste_titre"><?php echo $langs->trans(
'Product') ?></td>
1284 <td
class=
"liste_titre"><?php echo $langs->trans(
'Combination') ?></td>
1285 <td
class=
"liste_titre center"><?php echo $langs->trans(
'OnSell') ?></td>
1286 <td
class=
"liste_titre center"><?php echo $langs->trans(
'OnBuy') ?></td>
1287 <td
class=
"liste_titre right"><?php echo $langs->trans(
'Stock') ?></td>
1288 <td
class=
"liste_titre"></td>
1292 if (count($productCombinations)) {
1294 foreach ($productCombinations as $currcomb) {
1295 $prodstatic->fetch($currcomb->fk_product_child);
1296 $prodstatic->load_stock();
1297 $stock_total += $prodstatic->stock_reel;
1299 <tr
class=
"oddeven">
1300 <td><?php echo $prodstatic->getNomUrl(1) ?></td>
1304 $productCombination2ValuePairs = $comb2val->fetchByFkCombination($currcomb->id);
1305 $iMax = count($productCombination2ValuePairs);
1307 for ($i = 0; $i < $iMax; $i++) {
1310 if ($i !== ($iMax - 1)) {
1315 <td style=
"text-align: center;"><?php echo $prodstatic->getLibStatut(2, 0) ?></td>
1316 <td style=
"text-align: center;"><?php echo $prodstatic->getLibStatut(2, 1) ?></td>
1317 <td
class=
"right"><?php echo $prodstatic->stock_reel ?></td>
1319 <a
class=
"paddingleft paddingright" href=
"<?php echo dol_buildpath('/product/stock/product.php?id='.$currcomb->fk_product_child, 2) ?>"><?php echo
img_edit() ?></a>
1327 print
'<tr class="liste_total">';
1328 print
'<td colspan="4" class="left">'.$langs->trans(
"Total").
'</td>';
1329 print
'<td class="right">'.$stock_total.
'</td>';
1333 print
'<tr><td colspan="8"><span class="opacitymedium">'.$langs->trans(
"None").
'</span></td></tr>';