From 7fc8007571efea964431a054f3b067db78d2dc68 Mon Sep 17 00:00:00 2001 From: fappels Date: Fri, 18 Dec 2015 18:00:31 +0100 Subject: [PATCH] New add shipping and dispatching from/to multiple warehouses Add select2 to warehouse selectors Ship customer orderline from multiple warehouses Dispatch client orderline to multiple warehouses --- htdocs/core/js/lib_batch.js | 3 +- htdocs/expedition/card.php | 390 ++++++++++++------ htdocs/expedition/class/expedition.class.php | 48 ++- htdocs/expedition/shipment.php | 5 +- htdocs/fourn/commande/dispatch.php | 73 ++-- htdocs/fourn/js/lib_dispatch.js | 77 ++++ .../product/class/html.formproduct.class.php | 67 ++- htdocs/product/stock/list.php | 13 +- 8 files changed, 483 insertions(+), 193 deletions(-) create mode 100644 htdocs/fourn/js/lib_dispatch.js diff --git a/htdocs/core/js/lib_batch.js b/htdocs/core/js/lib_batch.js index fd76085a36d..54ddb8fa77d 100644 --- a/htdocs/core/js/lib_batch.js +++ b/htdocs/core/js/lib_batch.js @@ -21,7 +21,8 @@ /** * addLineBatch - * + * @deprecated replaced by addDispatchLine and moved to module folder and file fourn/js/lib_dispatch.js + * * @param index int number of produt. 0 = first product line */ function addLineBatch(index) diff --git a/htdocs/expedition/card.php b/htdocs/expedition/card.php index dfe848808c8..35bc4f8c324 100644 --- a/htdocs/expedition/card.php +++ b/htdocs/expedition/card.php @@ -7,7 +7,7 @@ * Copyright (C) 2013 Florian Henry * Copyright (C) 2013 Marcos GarcĂ­a * Copyright (C) 2014 Cedric GROSS - * Copyright (C) 2014 Francis Appels + * Copyright (C) 2014-2015 Francis Appels * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -90,20 +90,6 @@ $permissiondellink=$user->rights->expedition->livraison->creer; // Used by the i * Actions */ -$warehousecanbeselectedlater=1; -if (($action == 'create') || ($action == 'add')) -{ - if (! empty($conf->productbatch->enabled)) - { - if (! (GETPOST('entrepot_id','int') > 0)) - { - $langs->load("errors"); - setEventMessages($langs->trans("WarehouseMustBeSelectedAtFirstStepWhenProductBatchModuleOn"), null, 'errors'); - header("Location: ".DOL_URL_ROOT.'/expedition/shipment.php?id='.$origin_id); - exit; - } - } -} // Set incoterm if ($action == 'set_incoterms' && !empty($conf->incoterm->enabled)) @@ -157,6 +143,7 @@ if (empty($reshook)) $object->location_incoterms = GETPOST('location_incoterms', 'alpha'); $batch_line = array(); + $stockLine = array(); $num=count($objectsrc->lines); $totalqty=0; @@ -170,6 +157,7 @@ if (empty($reshook)) $j=0; $batch="batchl".$i."_0"; + $stockLocation="ent1".$i."_0"; $qty = "qtyl".$i; if (isset($_POST[$batch])) @@ -196,6 +184,22 @@ if (empty($reshook)) $totalqty+=$subtotalqty; } + else if (isset($_POST[$stockLocation])) + { + //shipment line from multiple stock locations + $qty .= '_'.$j; + while (isset($_POST[$stockLocation])) + { + // save sub line of warehouse + $stockLine[$i][$j]['qty']=GETPOST($qty,'int'); + $stockLine[$i][$j]['warehouse_id']=GETPOST($stockLocation,'int'); + $stockLine[$i][$j]['ix_l']=GETPOST($idl,'int'); + + $j++; + $stockLocation="ent1".$i."_".$j; + $qty = "qtyl".$i.'_'.$j; + } + } else { //shipment line for product with no batch management @@ -214,6 +218,22 @@ if (empty($reshook)) if (! isset($batch_line[$i])) { // not batch mode + if (isset($stockLine[$i])) + { + //shipment from multiple stock locations + for($j = 0; $j < count($stockLine[$i]); $j++) + { + if ($stockLine[$i][$j]['qty']>0) + { + $ret=$object->addline($stockLine[$i][$j]['warehouse_id'], $stockLine[$i][$j]['ix_l'], $stockLine[$i][$j]['qty']); + if ($ret < 0) + { + setEventMessages($object->error, $object->errors, 'errors'); + $error++; + } + } + } + } if (GETPOST($qty,'int') > 0 || (GETPOST($qty,'int') == 0 && $conf->global->SHIPMENT_GETS_ALL_ORDER_PRODUCTS)) { $ent = "entl".$i; @@ -773,129 +793,261 @@ if ($action == 'create') print ''; $quantityAsked = $line->qty; - $quantityToBeDelivered = $quantityAsked - $quantityDelivered; - - $warehouse_id = GETPOST('entrepot_id','int'); - - $defaultqty=0; - if ($warehouse_id > 0) - { - //var_dump($product); - $stock = $product->stock_warehouse[$warehouse_id]->real; - $stock+=0; // Convertit en numerique - $defaultqty=min($quantityToBeDelivered, $stock); - if (($line->product_type == 1 && empty($conf->global->STOCK_SUPPORTS_SERVICES)) || $defaultqty < 0) $defaultqty=0; - } - - if (empty($conf->productbatch->enabled) || ! ($product->hasbatch() && is_object($product->stock_warehouse[$warehouse_id]))) + if ($line->product_type == 1 && empty($conf->global->STOCK_SUPPORTS_SERVICES)) { - // Quantity to send - print ''; - if ($line->product_type == 0 || ! empty($conf->global->STOCK_SUPPORTS_SERVICES)) - { - print ''; - print ''; - } - else print $langs->trans("NA"); - print ''; - - // Stock - if (! empty($conf->stock->enabled)) - { - print ''; - if ($line->product_type == 0 || ! empty($conf->global->STOCK_SUPPORTS_SERVICES)) - { - // Show warehouse combo list - $ent = "entl".$indiceAsked; - $idl = "idl".$indiceAsked; - $tmpentrepot_id = is_numeric(GETPOST($ent,'int'))?GETPOST($ent,'int'):$warehouse_id; - print $formproduct->selectWarehouses($tmpentrepot_id,'entl'.$indiceAsked,'',1,0,$line->fk_product); - if ($tmpentrepot_id > 0 && $tmpentrepot_id == $warehouse_id) - { - //print $stock.' '.$quantityToBeDelivered; - if ($stock < $quantityToBeDelivered) - { - print ' '.img_warning($langs->trans("StockTooLow")); // Stock too low for this $warehouse_id but you can change warehouse - } - } - } - else - { - print $langs->trans("Service"); - } - print ''; - } - - print "\n"; - - // Show subproducts of product - if (! empty($conf->global->PRODUIT_SOUSPRODUITS) && $line->fk_product > 0) - { - $product->get_sousproduits_arbo(); - $prods_arbo = $product->get_arbo_each_prod($qtyProdCom); - if(count($prods_arbo) > 0) - { - foreach($prods_arbo as $key => $value) - { - //print $value[0]; - $img=''; - if ($value['stock'] < $value['stock_alert']) - { - $img=img_warning($langs->trans("StockTooLow")); - } - print "      -> - ".$value['fullpath']." - (".$value['nb'].") ".$value['nb_total']."   - ".$value['stock']." ".$img.""; - } - } - } + $quantityToBeDelivered = 0; } else { - print ''; // end line and start a new one for lot/serial - - $staticwarehouse=new Entrepot($db); - $staticwarehouse->fetch($warehouse_id); - - $subj=0; - print ''; - if (count($product->stock_warehouse[$warehouse_id]->detail_batch)) + $quantityToBeDelivered = $quantityAsked - $quantityDelivered; + } + $warehouse_id = GETPOST('entrepot_id','int'); + + $warehouseObject = null; + if ($warehouse_id > 0) + { + //ship from preselected location + $stock = + $product->stock_warehouse[$warehouse_id]->real; // Convert to number + $deliverableQty=min($quantityToBeDelivered, $stock); + if (empty($conf->productbatch->enabled) || ! ($product->hasbatch() && is_object($product->stock_warehouse[$warehouse_id]))) { - foreach ($product->stock_warehouse[$warehouse_id]->detail_batch as $dbatch) + // Quantity to send + print ''; + if ($line->product_type == 0 || ! empty($conf->global->STOCK_SUPPORTS_SERVICES)) + { + print ''; + print ''; + } + else print $langs->trans("NA"); + print ''; + + // Stock + if (! empty($conf->stock->enabled)) { - //var_dump($dbatch); - $substock=$dbatch->qty +0 ; // To get a numeric - print ''; - print ''; - print ''; - print ''; - - print $staticwarehouse->getNomUrl(0).' / '; - - print ''; - print ''; - print $langs->trans("DetailBatchFormat", $dbatch->batch, dol_print_date($dbatch->eatby,"day"), dol_print_date($dbatch->sellby,"day"), $dbatch->qty); - if ($defaultqty<=0) { - $defaultqty=0; - } else { - $defaultqty -= min($defaultqty,$substock); + if ($line->product_type == 0 || ! empty($conf->global->STOCK_SUPPORTS_SERVICES)) + { + // Show warehouse combo list + $ent = "entl".$indiceAsked; + $idl = "idl".$indiceAsked; + $tmpentrepot_id = is_numeric(GETPOST($ent,'int'))?GETPOST($ent,'int'):$warehouse_id; + print $formproduct->selectWarehouses($tmpentrepot_id,'entl'.$indiceAsked,'',1,0,$line->fk_product); + if ($tmpentrepot_id > 0 && $tmpentrepot_id == $warehouse_id) + { + //print $stock.' '.$quantityToBeDelivered; + if ($stock < $quantityToBeDelivered) + { + print ' '.img_warning($langs->trans("StockTooLow")); // Stock too low for this $warehouse_id but you can change warehouse + } + } + } + else + { + print $langs->trans("Service"); + } + print ''; + } + + print "\n"; + + // Show subproducts of product + if (! empty($conf->global->PRODUIT_SOUSPRODUITS) && $line->fk_product > 0) + { + $product->get_sousproduits_arbo(); + $prods_arbo = $product->get_arbo_each_prod($qtyProdCom); + if(count($prods_arbo) > 0) + { + foreach($prods_arbo as $key => $value) + { + //print $value[0]; + $img=''; + if ($value['stock'] < $value['stock_alert']) + { + $img=img_warning($langs->trans("StockTooLow")); + } + print "      -> + ".$value['fullpath']." + (".$value['nb'].") ".$value['nb_total']."   + ".$value['stock']." ".$img.""; + } } - $subj++; } } else + { + print ''; // end line and start a new one for lot/serial + + $staticwarehouse=new Entrepot($db); + $staticwarehouse->fetch($warehouse_id); + + $subj=0; + print ''; + if (count($product->stock_warehouse[$warehouse_id]->detail_batch)) + { + foreach ($product->stock_warehouse[$warehouse_id]->detail_batch as $dbatch) + { + //var_dump($dbatch); + $batchStock = + $dbatch->qty; // To get a numeric + $deliverableQty = min($quantityToBeDelivered,$batchStock); + print ''; + print ''; + print ''; + + print ''; + + print $staticwarehouse->getNomUrl(0).' / '; + + print ''; + print ''; + print $langs->trans("DetailBatchFormat", $dbatch->batch, dol_print_date($dbatch->eatby,"day"), dol_print_date($dbatch->sellby,"day"), $dbatch->qty); + $quantityToBeDelivered -= $deliverableQty; + if ($quantityToBeDelivered < 0) + { + $quantityToBeDelivered = 0; + } + $subj++; + } + } + else + { + print ''; + print ' '; + print ''; + + print ''; + print img_warning().' '.$langs->trans("NoProductToShipFoundIntoStock", $staticwarehouse->libelle); + } + } + } + else + { + // ship from multiple locations + if (empty($conf->productbatch->enabled) || ! $product->hasbatch()) + { + print ''; // end line and start a new one for each warehouse + print ''; + $subj=0; + foreach ($product->stock_warehouse as $warehouse_id=>$stock_warehouse) { + $warehouseObject=new Entrepot($db); + $warehouseObject->fetch($warehouse_id); + if ($stock_warehouse->real > 0) { + $stock = + $stock_warehouse->real; // Convert it to number + $deliverableQty = min($quantityToBeDelivered,$stock); + // Quantity to send + print ''; + if ($line->product_type == 0 || ! empty($conf->global->STOCK_SUPPORTS_SERVICES)) + { + print ''; + print ''; + } + else print $langs->trans("NA"); + print ''; + + // Stock + if (! empty($conf->stock->enabled)) + { + print ''; + if ($line->product_type == 0 || ! empty($conf->global->STOCK_SUPPORTS_SERVICES)) + { + print $warehouseObject->getNomUrl(0).' / '; + + print ''; + print $stock; + + } + else + { + print $langs->trans("Service"); + } + print ''; + } + $quantityToBeDelivered -= $deliverableQty; + if ($quantityToBeDelivered < 0) + { + $quantityToBeDelivered = 0; + } + $subj++; + print "\n"; + } + } + // Show subproducts of product + if (! empty($conf->global->PRODUIT_SOUSPRODUITS) && $line->fk_product > 0) + { + $product->get_sousproduits_arbo(); + $prods_arbo = $product->get_arbo_each_prod($qtyProdCom); + if(count($prods_arbo) > 0) + { + foreach($prods_arbo as $key => $value) + { + //print $value[0]; + $img=''; + if ($value['stock'] < $value['stock_alert']) + { + $img=img_warning($langs->trans("StockTooLow")); + } + print "      -> + ".$value['fullpath']." + (".$value['nb'].") ".$value['nb_total']."   + ".$value['stock']." ".$img.""; + } + } + } + } + else + { + print ''; // end line and start a new one for lot/serial + + $subj=0; + print ''; + + foreach ($product->stock_warehouse as $warehouse_id=>$stock_warehouse) { + $warehouseObject=new Entrepot($db); + $warehouseObject->fetch($warehouse_id); + if (($stock_warehouse->real > 0) && (count($stock_warehouse->detail_batch))) { + foreach ($stock_warehouse->detail_batch as $dbatch) + { + //var_dump($dbatch); + $batchStock = + $dbatch->qty; // To get a numeric + $deliverableQty = min($quantityToBeDelivered,$batchStock); + print ''; + print ''; + print ''; + + print ''; + + print $warehouseObject->getNomUrl(0).' / '; + + print ''; + print ''; + print $langs->trans("DetailBatchFormat", $dbatch->batch, dol_print_date($dbatch->eatby,"day"), dol_print_date($dbatch->sellby,"day"), $dbatch->qty); + $quantityToBeDelivered -= $deliverableQty; + if ($quantityToBeDelivered < 0) + { + $quantityToBeDelivered = 0; + } + //dol_syslog('deliverableQty = '.$deliverableQty.' batchStock = '.$batchStock); + $subj++; + } + } + } + } + if ($subj == 0) { print ''; print ' '; print ''; print ''; - print img_warning().' '.$langs->trans("NoProductToShipFoundIntoStock", $staticwarehouse->libelle); + if ($warehouseObject) + { + print img_warning().' '.$langs->trans("NoProductToShipFoundIntoStock", $warehouseObject->libelle); + } + else + { + print img_warning().' '.$langs->trans("StockTooLow"); + } } } - $indiceAsked++; } diff --git a/htdocs/expedition/class/expedition.class.php b/htdocs/expedition/class/expedition.class.php index 2513de4caaa..44384f2e294 100644 --- a/htdocs/expedition/class/expedition.class.php +++ b/htdocs/expedition/class/expedition.class.php @@ -378,26 +378,40 @@ class Expedition extends CommonObject function create_line_batch($line_ext) { $error = 0; - - if ($this->create_line(($line_ext->entrepot_id?$line_ext->entrepot_id:'null'),$line_ext->origin_line_id,$line_ext->qty) < 0) + $stockLocationQty = array(); // associated array with batch qty in stock location + + $tab=$line_ext->detail_batch; + // create stockLocation Qty array + foreach ($tab as $detbatch) { - $error++; - } - - if (! $error) - { - $line_id= $this->db->last_insert_id(MAIN_DB_PREFIX."expeditiondet"); - $tab=$line_ext->detail_batch; - foreach ($tab as $detbatch) + if ($detbatch->entrepot_id) { - if (! ($detbatch->create($line_id) >0)) // Create an expeditionlinebatch + $stockLocationQty[$detbatch->entrepot_id] += $detbatch->dluo_qty; + } + } + // create shipment lines + foreach ($stockLocationQty as $stockLocation => $qty) + { + if ($this->create_line($stockLocation,$line_ext->origin_line_id,$qty) < 0) + { + $error++; + } + else + { + // create shipment batch lines for stockLocation + $line_id= $this->db->last_insert_id(MAIN_DB_PREFIX."expeditiondet"); + foreach ($tab as $detbatch) { - $error++; + if ($detbatch->entrepot_id == $stockLocation){ + if (! ($detbatch->create($line_id) >0)) // Create an expeditionlinebatch + { + $error++; + } + } } } } - if (! $error) return 1; else return -1; } @@ -1277,9 +1291,11 @@ class Expedition extends CommonObject $this->total_ttc+= $tabprice[2]; $this->total_localtax1+= $tabprice[9]; $this->total_localtax2+= $tabprice[10]; - - $line->detail_batch = array(); - + + if ($originline != $obj->fk_origin_line) + { + $line->detail_batch = array(); + } // Eat-by date if (! empty($conf->productbatch->enabled) && $obj->line_id > 0) { diff --git a/htdocs/expedition/shipment.php b/htdocs/expedition/shipment.php index 2fd7bbb359a..123766d9623 100644 --- a/htdocs/expedition/shipment.php +++ b/htdocs/expedition/shipment.php @@ -721,10 +721,7 @@ if ($id > 0 || ! empty($ref)) if (! empty($conf->stock->enabled)) { - $warehousecanbeselectedlater=1; - if (! empty($conf->productbatch->enabled)) $warehousecanbeselectedlater=0; - - print ''.$langs->trans("WarehouseSource").''; + print ''.$langs->trans("WarehouseSource").''; print ''; print $formproduct->selectWarehouses(! empty($commande->warehouse_id)?$commande->warehouse_id:-1,'entrepot_id','',1); if (count($formproduct->cache_warehouses) <= 0) diff --git a/htdocs/fourn/commande/dispatch.php b/htdocs/fourn/commande/dispatch.php index fd44b987f94..34dd4ad180f 100644 --- a/htdocs/fourn/commande/dispatch.php +++ b/htdocs/fourn/commande/dispatch.php @@ -33,6 +33,7 @@ require_once DOL_DOCUMENT_ROOT.'/product/stock/class/entrepot.class.php'; require_once DOL_DOCUMENT_ROOT.'/core/lib/fourn.lib.php'; require_once DOL_DOCUMENT_ROOT.'/fourn/class/fournisseur.commande.class.php'; require_once DOL_DOCUMENT_ROOT.'/fourn/class/fournisseur.commande.dispatch.class.php'; +require_once DOL_DOCUMENT_ROOT.'/product/class/html.formproduct.class.php'; if (! empty($conf->projet->enabled)) require_once DOL_DOCUMENT_ROOT.'/projet/class/project.class.php'; $langs->load('orders'); @@ -130,17 +131,17 @@ if ($action == 'dispatch' && $user->rights->fournisseur->commande->receptionner) $pos=0; foreach($_POST as $key => $value) { - if (preg_match('/^product_([0-9]+)$/i', $key, $reg)) // without batch module enabled + if (preg_match('/^product_([0-9]+)_([0-9]+)$/i', $key, $reg)) // without batch module enabled { $pos++; - //$numline=$reg[1] + 1; // line of product + //$numline=$reg[2] + 1; // line of product $numline=$pos; - $prod = "product_".$reg[1]; - $qty = "qty_".$reg[1]; - $ent = "entrepot_".$reg[1]; - $pu = "pu_".$reg[1]; // This is unit price including discount - $fk_commandefourndet = "fk_commandefourndet_".$reg[1]; + $prod = "product_".$reg[1].'_'.$reg[2]; + $qty = "qty_".$reg[1].'_'.$reg[2]; + $ent = "entrepot_".$reg[1].'_'.$reg[2]; + $pu = "pu_".$reg[1].'_'.$reg[2]; // This is unit price including discount + $fk_commandefourndet = "fk_commandefourndet_".$reg[1].'_'.$reg[2]; if (GETPOST($qty) > 0) // We ask to move a qty { @@ -163,14 +164,14 @@ if ($action == 'dispatch' && $user->rights->fournisseur->commande->receptionner) } } } - if (preg_match('/^product_([0-9]+)_([0-9]+)$/i', $key, $reg)) // with batch module enabled + if (preg_match('/^product_batch_([0-9]+)_([0-9]+)$/i', $key, $reg)) // with batch module enabled { $pos++; //eat-by date dispatch //$numline=$reg[2] + 1; // line of product $numline=$pos; - $prod = 'product_'.$reg[1].'_'.$reg[2]; + $prod = 'product_batch_'.$reg[1].'_'.$reg[2]; $qty = 'qty_'.$reg[1].'_'.$reg[2]; $ent = 'entrepot_'.$reg[1].'_'.$reg[2]; $pu = 'pu_'.$reg[1].'_'.$reg[2]; @@ -191,7 +192,7 @@ if ($action == 'dispatch' && $user->rights->fournisseur->commande->receptionner) $error++; } - if (! ($_POST[$lot] || $dDLUO || $dDLC)) + if (! (GETPOST($lot, 'alpha') || $dDLUO || $dDLC)) { dol_syslog('No dispatch for line '.$key.' as serial/eat-by/sellby date are not set'); $text = $langs->transnoentities('atleast1batchfield').', '.$langs->transnoentities('Line').' ' .($numline).'-'.($reg[1]+1); @@ -245,19 +246,13 @@ if ($action == 'dispatch' && $user->rights->fournisseur->commande->receptionner) */ $form = new Form($db); +$formproduct = new FormProduct($db); $warehouse_static = new Entrepot($db); $supplierorderdispatch = new CommandeFournisseurDispatch($db); $help_url='EN:CommandeFournisseur'; -if (!empty($conf->productbatch->enabled)) -{ - llxHeader('',$langs->trans("OrderCard"),$help_url,'',0,0,array('/core/js/lib_batch.js')); -} -else -{ - llxHeader('',$langs->trans("OrderCard"),$help_url); -} +llxHeader('',$langs->trans("OrderCard"),$help_url,'',0,0,array('/fourn/js/lib_dispatch.js')); $now=dol_now(); @@ -425,7 +420,7 @@ if ($id > 0 || ! empty($ref)) $nbfreeproduct=0; $nbproduct=0; - $var=true; + $var=false; while ($i < $num) { $objp = $db->fetch_object($resql); @@ -449,13 +444,7 @@ if ($id > 0 || ! empty($ref)) // To show detail cref and description value, we must make calculation by cref //print ($objp->cref?' ('.$objp->cref.')':''); //if ($objp->description) print '
'.nl2br($objp->description); - if ((empty($conf->productbatch->enabled)) || $objp->tobatch==0) - { - $suffix='_'.$i; - } else { - $suffix='_0_'.$i; - } - + $suffix='_0_'.$i; print "\n"; print ''."\n"; @@ -489,6 +478,7 @@ if ($id > 0 || ! empty($ref)) print ""; } + $var=!$var; $up_ht_disc=$objp->subprice; if (! empty($objp->remise_percent) && empty($conf->global->STOCK_EXCLUDE_DISCOUNT_FOR_PMP)) $up_ht_disc=price2num($up_ht_disc * (100 - $objp->remise_percent) / 100, 'MU'); @@ -500,15 +490,19 @@ if ($id > 0 || ! empty($ref)) if (! empty($conf->productbatch->enabled) && $objp->tobatch==1) { - print ''.img_picto($langs->trans('AddDispatchBatchLine'),'split.png','onClick="addLineBatch('.$i.')"').''; // Dispatch column + $type = 'batch'; + print ''.img_picto($langs->trans('AddDispatchBatchLine'),'split.png','onClick="addDispatchLine('.$i.',\''.$type.'\')"').''; // Dispatch column print ''; // Warehouse column print ''; - print ''; + print ''; print ''; print ''; - print ''; + print ''; print ''; + // hidden fields for js function + print ''; + print ''; print ''; print ''; @@ -524,15 +518,24 @@ if ($id > 0 || ! empty($ref)) print ''; print ' '; // Qty ordered + qty already dispatached } - - // Dispatch - print ''; - if (empty($conf->productbatch->enabled) || $objp->tobatch!=1) + else { + $type = 'dispatch'; + print ''.img_picto($langs->trans('AddStockLocationLine'),'split.png','onClick="addDispatchLine('.$i.',\''.$type.'\')"').''; // Dispatch column + print ''; + print ''; + print ''; + print ''; print ''; print ''; print ''; + // hidden fields for js function + print ''; + print ''; + print ''; } + // Dispatch + print ''; print ''; print ''; @@ -540,11 +543,11 @@ if ($id > 0 || ! empty($ref)) print ''; if (count($listwarehouses)>1) { - print $form->selectarray("entrepot".$suffix, $listwarehouses, GETPOST("entrepot".$suffix), 1, 0, 0, '', 0, 0, $disabled); + print $formproduct->selectWarehouses(GETPOST("entrepot".$suffix), "entrepot".$suffix,'',1,0,$objp->fk_product); } elseif (count($listwarehouses)==1) { - print $form->selectarray("entrepot".$suffix, $listwarehouses, GETPOST("entrepot".$suffix), 0, 0, 0, '', 0, 0, $disabled); + print $formproduct->selectWarehouses(GETPOST("entrepot".$suffix), "entrepot".$suffix,'',0,0,$objp->fk_product); } else { diff --git a/htdocs/fourn/js/lib_dispatch.js b/htdocs/fourn/js/lib_dispatch.js new file mode 100644 index 00000000000..c58a4e1fe9d --- /dev/null +++ b/htdocs/fourn/js/lib_dispatch.js @@ -0,0 +1,77 @@ +// Copyright (C) 2014 Cedric GROSS +// Copyright (C) 2015 Francis Appels +// +// This program is free software; you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation; either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . +// or see http://www.gnu.org/ + +// +// \file htdocs/core/js/lib_dispatch.js +// \brief File that include javascript functions used dispatch.php +// + +/** + * addDispatchLine + * Adds new table row for dispatching to multiple stock locations + * + * @param index int index of produt line. 0 = first product line + * @param type string type of dispatch (batch = batch dispatch, dispatch = non batch dispatch) + */ +function addDispatchLine(index,type) +{ + var $row = $("tr[name='"+type+'_0_'+index+"']").clone(true), // clone first batch line to jQuery object + nbrTrs = $("tr[name^='"+type+"_'][name$='_"+index+"']").length, // position of line for batch + qtyOrdered = parseFloat($("#qty_ordered_"+(nbrTrs - 1)+"_"+index).val()), + qty = parseFloat($("#qty_"+(nbrTrs - 1)+"_"+index).val()), + qtyDispatched; + + if (type === 'batch') + { + qtyDispatched = parseFloat($("#qty_dispatched_"+(nbrTrs - 1)+"_"+index).val()) + 1; + } + else + { + qtyDispatched = parseFloat($("#qty_dispatched_"+(nbrTrs - 1)+"_"+index).val()) + qty; + } + + if (qtyDispatched < qtyOrdered) + { + //replace tr suffix nbr + $row.html($row.html().replace(/_0_/g,"_"+nbrTrs+"_")); + //create new select2 to avoid duplicate id of cloned one + $row.find("select[name='"+'entrepot_'+nbrTrs+'_'+index+"']").select2(); + // TODO find solution to copy selected option to new select + // TODO find solution to keep new tr's after page refresh + //clear value + $row.find("input[name^='qty']").val(''); + //change name of new row + $row.attr('name',type+'_'+nbrTrs+'_'+index); + //insert new row before last row + $("tr[name^='"+type+"_'][name$='_"+index+"']:last").after($row); + //remove cloned select2 with duplicate id. + $("#s2id_entrepot_"+nbrTrs+'_'+index).detach(); + /* Suffix of lines are: _ trs.length _ index */ + $("#qty_"+nbrTrs+"_"+index).focus(); + $("#qty_dispatched_"+(nbrTrs)+"_"+index).val(qtyDispatched); + if (type === 'batch') + { + $("#qty_"+(nbrTrs)+"_"+index).val(qty-1); + $("#qty_"+(nbrTrs-1)+"_"+index).val(1); + } + else + { + + $("#qty_"+nbrTrs+"_"+index).val(qtyOrdered - qtyDispatched); + } + } +} \ No newline at end of file diff --git a/htdocs/product/class/html.formproduct.class.php b/htdocs/product/class/html.formproduct.class.php index f08143c443c..27b8e6f3ba2 100644 --- a/htdocs/product/class/html.formproduct.class.php +++ b/htdocs/product/class/html.formproduct.class.php @@ -51,25 +51,50 @@ class FormProduct * Load in cache array list of warehouses * If fk_product is not 0, we do not use cache * - * @param int $fk_product Add quantity of stock in label for product with id fk_product. Nothing if 0. - * @return int Nb of loaded lines, 0 if already loaded, <0 if KO + * @param int $fk_product Add quantity of stock in label for product with id fk_product. Nothing if 0. + * @param string $batch Add quantity of batch stock in label for product with batch name batch, batch name precedes batch_id. Nothing if ''. + * @param int $fk_product_batch Add quantity of batch stock in label for product with batch id fk_product_batch. Nothing if 0. + * @param boolean $sumStock sum total stock of a warehouse, default true + * @return int Nb of loaded lines, 0 if already loaded, <0 if KO */ - function loadWarehouses($fk_product=0) + function loadWarehouses($fk_product=0, $batch = '', $fk_product_batch=0, $sumStock = true) { global $conf, $langs; if (empty($fk_product) && count($this->cache_warehouses)) return 0; // Cache already loaded and we do not want a list with information specific to a product $sql = "SELECT e.rowid, e.label"; - if ($fk_product) $sql.= ", ps.reel"; - $sql.= " FROM ".MAIN_DB_PREFIX."entrepot as e"; - if ($fk_product) + if (!empty($fk_product)) + { + if (!empty($fk_product_batch) || !empty($batch)) + { + $sql.= ", pb.qty as stock"; + } + else + { + $sql.= ", ps.reel as stock"; + } + } + else if ($sumStock) + { + $sql.= ", sum(ps.reel) as stock"; + } + $sql.= " FROM ".MAIN_DB_PREFIX."entrepot as e"; + $sql.= " LEFT JOIN ".MAIN_DB_PREFIX."product_stock as ps on ps.fk_entrepot = e.rowid"; + if (!empty($fk_product)) { - $sql.= " LEFT JOIN ".MAIN_DB_PREFIX."product_stock as ps on ps.fk_entrepot = e.rowid"; $sql.= " AND ps.fk_product = '".$fk_product."'"; + if (!empty($batch)) + { + $sql.= " LEFT JOIN ".MAIN_DB_PREFIX."product_batch as pb on pb.fk_product_stock = ps.rowid AND pb.batch = '".$batch."'"; + } else if (!empty($fk_product_batch)) + { + $sql.= " LEFT JOIN ".MAIN_DB_PREFIX."product_batch as pb on pb.fk_product_stock = ps.rowid AND pb.rowid = '".$fk_product_batch."'"; + } } $sql.= " WHERE e.entity IN (".getEntity('stock', 1).")"; $sql.= " AND e.statut = 1"; + if ($sumStock && empty($fk_product)) $sql.= " GROUP BY e.rowid, e.label, e.description"; $sql.= " ORDER BY e.label"; dol_syslog(get_class($this).'::loadWarehouses', LOG_DEBUG); @@ -81,10 +106,11 @@ class FormProduct while ($i < $num) { $obj = $this->db->fetch_object($resql); - + if ($sumStock) $obj->stock = price2num($obj->stock,5); $this->cache_warehouses[$obj->rowid]['id'] =$obj->rowid; $this->cache_warehouses[$obj->rowid]['label']=$obj->label; - if ($fk_product) $this->cache_warehouses[$obj->rowid]['stock']=$obj->reel; + $this->cache_warehouses[$obj->rowid]['description'] = $obj->description; + $this->cache_warehouses[$obj->rowid]['stock'] = $obj->stock; $i++; } return $num; @@ -106,18 +132,31 @@ class FormProduct * @param int $disabled 1=Select is disabled * @param int $fk_product Add quantity of stock in label for product with id fk_product. Nothing if 0. * @param string $empty_label Empty label if needed (only if $empty=1) + * @param int $showstock 1=show stock count + * @param int $forcecombo force combo iso ajax select2 + * @param array $events events to add to select2 * @return string HTML select */ - function selectWarehouses($selected='',$htmlname='idwarehouse',$filtertype='',$empty=0,$disabled=0,$fk_product=0,$empty_label='') + function selectWarehouses($selected='',$htmlname='idwarehouse',$filtertype='',$empty=0,$disabled=0,$fk_product=0,$empty_label='', $showstock=0, $forcecombo=0, $events=array()) { - global $langs,$user; + global $conf,$langs,$user; dol_syslog(get_class($this)."::selectWarehouses $selected, $htmlname, $filtertype, $empty, $disabled, $fk_product",LOG_DEBUG); - + + $out=''; + $this->loadWarehouses($fk_product); $nbofwarehouses=count($this->cache_warehouses); - $out=''; if ($empty) $out.=''; foreach($this->cache_warehouses as $id => $arraytypes) { @@ -125,7 +164,7 @@ class FormProduct if ($selected == $id || ($selected == 'ifone' && $nbofwarehouses == 1)) $out.=' selected'; $out.='>'; $out.=$arraytypes['label']; - if ($fk_product) $out.=' ('.$langs->trans("Stock").': '.($arraytypes['stock']>0?$arraytypes['stock']:'?').')'; + if (($fk_product || ($showstock > 0)) && ($arraytypes['stock'] != 0)) $out.='('.$langs->trans("Stock").':'.$arraytypes['stock'].')'; $out.=''; } $out.=''; diff --git a/htdocs/product/stock/list.php b/htdocs/product/stock/list.php index 43c5bc0c184..314daf7ab9c 100644 --- a/htdocs/product/stock/list.php +++ b/htdocs/product/stock/list.php @@ -57,7 +57,7 @@ $form=new Form($db); $warehouse=new Entrepot($db); $sql = "SELECT e.rowid, e.label as ref, e.statut, e.lieu, e.address, e.zip, e.town, e.fk_pays,"; -$sql.= " SUM(p.pmp * ps.reel) as estimatedvalue, SUM(p.price * ps.reel) as sellvalue"; +$sql.= " SUM(p.pmp * ps.reel) as estimatedvalue, SUM(p.price * ps.reel) as sellvalue, SUM(ps.reel) as stockqty"; $sql.= " FROM ".MAIN_DB_PREFIX."entrepot as e"; $sql.= " LEFT JOIN ".MAIN_DB_PREFIX."product_stock as ps ON e.rowid = ps.fk_entrepot"; $sql.= " LEFT JOIN ".MAIN_DB_PREFIX."product as p ON ps.fk_product = p.rowid"; @@ -94,7 +94,8 @@ if ($result) print ""; print_liste_field_titre($langs->trans("Ref"),$_SERVER["PHP_SELF"], "e.label","","","",$sortfield,$sortorder); print_liste_field_titre($langs->trans("LocationSummary"),$_SERVER["PHP_SELF"], "e.lieu","","","",$sortfield,$sortorder); - print_liste_field_titre($langs->trans("EstimatedStockValue"), $_SERVER["PHP_SELF"], "e.valo_pmp",'','','align="right"',$sortfield,$sortorder); + print_liste_field_titre($langs->trans("PhysicalStock"), $_SERVER["PHP_SELF"], "stockqty",'','','align="right"',$sortfield,$sortorder); + print_liste_field_titre($langs->trans("EstimatedStockValue"), $_SERVER["PHP_SELF"], "e.valo_pmp",'','','align="right"',$sortfield,$sortorder); print_liste_field_titre($langs->trans("EstimatedStockValueSell"), $_SERVER["PHP_SELF"], "",'','','align="right"',$sortfield,$sortorder); print_liste_field_titre($langs->trans("Status"),$_SERVER["PHP_SELF"], "e.statut",'','','align="right"',$sortfield,$sortorder); print_liste_field_titre('',$_SERVER["PHP_SELF"],"",'','','',$sortfield,$sortorder,'maxwidthsearch '); @@ -111,7 +112,7 @@ if ($result) print ''; print ''; - print ''; + print ''; print ''; print ''; @@ -128,7 +129,7 @@ if ($result) if ($num) { $entrepot=new Entrepot($db); - $total = $totalsell = 0; + $total = $totalsell = $totalStock = 0; $var=false; while ($i < min($num,$limit)) { @@ -140,6 +141,8 @@ if ($result) print '' . $entrepot->getNomUrl(1) . ''; // Location print ''.$objp->lieu.''; + // Stock qty + print ''.price2num($objp->stockqty,5).''; // PMP value print ''; if (price2num($objp->estimatedvalue,'MT')) print price(price2num($objp->estimatedvalue,'MT'),1); @@ -163,6 +166,7 @@ if ($result) $total += price2num($objp->estimatedvalue,'MU'); $totalsell += price2num($objp->sellvalue,'MU'); + $totalStock += $objp->stockqty; $var=!$var; $i++; @@ -170,6 +174,7 @@ if ($result) print ''; print ''.$langs->trans("Total").''; + print ''.price2num($totalStock,5).''; print ''.price(price2num($total,'MT'),1,$langs,0,0,-1,$conf->currency).''; print ''; if (empty($conf->global->PRODUIT_MULTIPRICES)) print price(price2num($totalsell,'MT'),1,$langs,0,0,-1,$conf->currency);