35require
'../main.inc.php';
36require_once DOL_DOCUMENT_ROOT.
'/core/modules/supplier_order/modules_commandefournisseur.php';
37require_once DOL_DOCUMENT_ROOT.
'/product/stock/class/entrepot.class.php';
38require_once DOL_DOCUMENT_ROOT.
'/core/lib/fourn.lib.php';
39require_once DOL_DOCUMENT_ROOT.
'/fourn/class/fournisseur.commande.class.php';
40require_once DOL_DOCUMENT_ROOT.
'/fourn/class/fournisseur.commande.dispatch.class.php';
41require_once DOL_DOCUMENT_ROOT.
'/product/class/html.formproduct.class.php';
42require_once DOL_DOCUMENT_ROOT.
'/product/stock/class/mouvementstock.class.php';
43require_once DOL_DOCUMENT_ROOT.
'/core/lib/sendings.lib.php';
45 require_once DOL_DOCUMENT_ROOT.
'/projet/class/project.class.php';
57$langs->loadLangs(array(
"sendings",
"companies",
"bills",
'orders',
'stocks',
'other',
'propal',
'receptions'));
62if ($is_mod_batch_enabled) {
63 $langs->load(
'productbatch');
70$action =
GETPOST(
'action',
'aZ09');
71$fk_default_warehouse =
GETPOSTINT(
'fk_default_warehouse');
72$cancel =
GETPOST(
'cancel',
'alpha');
73$confirm =
GETPOST(
'confirm',
'alpha');
79 $socid = $user->socid;
82$hookmanager->initHooks(array(
'expeditiondispatch'));
86if (GETPOSTISSET(
"projectid")) {
94if ($id > 0 || !empty($ref)) {
95 $result =
$object->fetch($id, $ref);
99 $result =
$object->fetch_thirdparty();
118$usercancreate = $user->hasRight(
'expedition',
'creer');
119$permissiontoadd = $usercancreate;
126$parameters = array();
127$reshook = $hookmanager->executeHooks(
'doActions', $parameters, $object, $action);
132if (empty($reshook)) {
134 if ($action ==
'updatelines' && $usercancreate) {
143 foreach ($_POST as $key => $value) {
146 if (preg_match(
'/^(?:product|productbatch)([0-9]+)_([0-9]+)_([0-9]+)$/i', $key, $reg)) {
149 if (preg_match(
'/^product([0-9]+)_([0-9]+)_([0-9]+)$/i', $key, $reg)) {
150 $modebatch =
"barcode";
151 } elseif (preg_match(
'/^productbatch([0-9]+)_([0-9]+)_([0-9]+)$/i', $key, $reg)) {
152 $modebatch =
"batch";
156 $dispatch_line_suffix = $reg[1].
'_'.$reg[2].
'_'.$reg[3];
157 if ($modebatch ==
"barcode") {
158 $prod =
"product".$dispatch_line_suffix;
160 $prod =
'productbatch'.$dispatch_line_suffix;
162 $qty =
"qty".$dispatch_line_suffix;
163 $ent =
"entrepot".$dispatch_line_suffix;
164 $fk_commandedet =
"fk_commandedet".$dispatch_line_suffix;
165 $idline =
GETPOSTINT(
"idline".$dispatch_line_suffix);
172 if ($modebatch ==
"batch") {
173 $lot =
GETPOST(
'lot_number'.$dispatch_line_suffix);
182 if (($modebatch ==
"batch" && $newqty >= 0) || ($modebatch ==
"barcode" && $newqty != 0)) {
184 if (!($warehouse_id > 0)) {
185 dol_syslog(
'No dispatch for line '.$key.
' as no warehouse was chosen.');
186 $text = $langs->transnoentities(
'Warehouse').
', '.$langs->transnoentities(
'Line').
' '.($numline);
187 setEventMessages($langs->trans(
'ErrorFieldRequired', $text),
null,
'errors');
190 if (!$error && $modebatch ==
"batch") {
191 $sql =
"SELECT pb.rowid ";
192 $sql .=
" FROM ".$db->prefix().
"product_batch as pb";
193 $sql .=
" JOIN ".$db->prefix().
"product_stock as ps";
194 $sql .=
" ON ps.rowid = pb.fk_product_stock";
195 $sql .=
" WHERE pb.batch = '".$db->escape($lot).
"'";
196 $sql .=
" AND ps.fk_product = ".((int) $prod_id) ;
197 $sql .=
" AND ps.fk_entrepot = ".((int) $warehouse_id) ;
199 $resql = $db->query($sql);
201 $num = $db->num_rows($resql);
203 dol_syslog(
'No dispatch for line '.$key.
' as too many combination warehouse, product, batch code was found ('.$num.
').');
204 setEventMessages($langs->trans(
'ErrorTooManyCombinationBatchcode', $numline, $num),
null,
'errors');
206 } elseif ($num < 1) {
208 $tmpwarehouse->fetch($warehouse_id);
210 $tmpprod->fetch($prod_id);
211 dol_syslog(
'No dispatch for line '.$key.
' as no combination warehouse, product, batch code was found.');
212 setEventMessages($langs->trans(
'ErrorNoCombinationBatchcode', $numline, $tmpwarehouse->ref, $tmpprod->ref, $lot),
null,
'errors');
224 $result = $expeditiondispatch->fetch($idline);
226 setEventMessages($expeditiondispatch->error, $expeditiondispatch->errors,
'errors');
229 $qtystart = $expeditiondispatch->qty;
230 $expeditiondispatch->qty = $newqty;
231 $expeditiondispatch->entrepot_id =
GETPOSTINT($ent);
234 $result = $expeditiondispatch->update($user);
236 $result = $expeditiondispatch->delete($user);
239 setEventMessages($expeditiondispatch->error, $expeditiondispatch->errors,
'errors');
243 if (!$error && $modebatch ==
"batch") {
245 $suffixkeyfordate = preg_replace(
'/^productbatch/',
'', $key);
249 $sqlsearchdet =
"SELECT rowid FROM ".$db->prefix().$expeditionlinebatch->table_element;
250 $sqlsearchdet .=
" WHERE fk_expeditiondet = ".((int) $idline);
251 $resqlsearchdet = $db->query($sqlsearchdet);
253 $objsearchdet =
null;
254 if ($resqlsearchdet) {
255 $objsearchdet = $db->fetch_object($resqlsearchdet);
261 $sql =
"UPDATE ".$db->prefix().$expeditionlinebatch->table_element.
" SET";
262 $sql .=
" batch = '".$db->escape($lot).
"'";
263 $sql .=
", eatby = ".($eatby ?
"'".$db->idate($eatby).
"'" :
"null");
264 $sql .=
", sellby = ".($sellby ?
"'".$db->idate($sellby).
"'" :
"null");
265 $sql .=
", qty = ".((float) $newqty);
266 $sql .=
", fk_warehouse = ".((int) $warehouse_id);
267 $sql .=
" WHERE rowid = ".((int) $objsearchdet->rowid);
269 $sql =
"INSERT INTO ".$db->prefix().$expeditionlinebatch->table_element.
" (";
270 $sql .=
"fk_expeditiondet, eatby, sellby, batch, qty, fk_origin_stock, fk_warehouse)";
271 $sql .=
" VALUES (".((int) $idline).
", ".($eatby ?
"'".$db->idate($eatby).
"'" :
"null").
", ".($sellby ?
"'".$db->idate($sellby).
"'" :
"null").
", ";
272 $sql .=
" '".$db->escape($lot).
"', ".((float) $newqty).
", 0, ".((int) $warehouse_id).
")";
275 $sql =
"DELETE FROM ".$db->prefix().$expeditionlinebatch->table_element;
276 $sql .=
" WHERE fk_expeditiondet = ".((int) $idline);
277 $sql .=
" AND batch = '".$db->escape($lot).
"'";
280 $resql = $db->query($sql);
288 $expeditiondispatch->fk_expedition =
$object->id;
289 $expeditiondispatch->entrepot_id =
GETPOSTINT($ent);
290 $expeditiondispatch->fk_parent =
GETPOSTINT(
'fk_parent'.$dispatch_line_suffix);
291 $expeditiondispatch->fk_product = $prod_id;
292 if (!($expeditiondispatch->fk_parent > 0)) {
293 $expeditiondispatch->fk_elementdet =
GETPOSTINT($fk_commandedet);
295 $expeditiondispatch->qty = $newqty;
298 $idline = $expeditiondispatch->insert($user);
300 setEventMessages($expeditiondispatch->error, $expeditiondispatch->errors,
'errors');
304 if ($modebatch ==
"batch" && !$error) {
305 $expeditionlinebatch->sellby = $dDLC;
306 $expeditionlinebatch->eatby = $dDLUO;
307 $expeditionlinebatch->batch = $lot;
308 $expeditionlinebatch->qty = $newqty;
309 $expeditionlinebatch->fk_origin_stock = 0;
310 $expeditionlinebatch->fk_warehouse =
GETPOSTINT($ent);
312 $result = $expeditionlinebatch->create($idline);
314 setEventMessages($expeditionlinebatch->error, $expeditionlinebatch->errors,
'errors');
371 header(
"Location: ".DOL_URL_ROOT.
'/expedition/dispatch.php?id='.
$object->id);
374 } elseif ($action ==
'setdate_livraison' && $usercancreate) {
378 $result =
$object->setDeliveryDate($user, $datedelivery);
392$form =
new Form($db);
394$warehouse_static =
new Entrepot($db);
396$title =
$object->ref.
" - ".$langs->trans(
'ShipmentDistribution');
397$help_url =
'EN:Module_Shipments|FR:Module_Expéditions|ES:Módulo_Expediciones|DE:Modul_Lieferungen';
398$morejs = array(
'/expedition/js/lib_dispatch.js.php');
401llxHeader(
'', $title, $help_url,
'', 0, 0, $morejs,
'',
'',
'mod-expedition page-card_dispatch');
407 $num_prod = count($lines);
419 $author =
new User($db);
420 $author->fetch(
$object->user_author_id);
430 if ($action ==
'ask_deleteline') {
431 $formconfirm = $form->formconfirm($_SERVER[
"PHP_SELF"].
'?id='.
$object->id.
'&lineid='.$lineid, $langs->trans(
'DeleteLine'), $langs->trans(
'ConfirmDeleteLine'),
'confirm_deleteline',
'', 0, 1);
435 $parameters = array(
'lineid' => $lineid);
437 $reshook = $hookmanager->executeHooks(
'formConfirm', $parameters, $object, $action);
438 if (empty($reshook)) {
439 $formconfirm .= $hookmanager->resPrint;
440 } elseif ($reshook > 0) {
441 $formconfirm = $hookmanager->resPrint;
449 $objectsrc->fetch(
$object->origin_object->id);
452 $objectsrc =
new Propal($db);
453 $objectsrc->fetch(
$object->origin_object->id);
457 $linkback =
'<a href="'.DOL_URL_ROOT.
'/expedition/list.php?restore_lastsearch_values=1'.(!empty($socid) ?
'&socid='.$socid :
'').
'">'.$langs->trans(
"BackToList").
'</a>';
458 $morehtmlref =
'<div class="refidno">';
461 $morehtmlref .= $form->editfieldkey(
"RefCustomer",
'ref_customer',
$object->ref_customer, $object, $user->hasRight(
'expedition',
'creer'),
'string',
'', 0, 1);
462 $morehtmlref .= $form->editfieldval(
"RefCustomer",
'ref_customer',
$object->ref_customer, $object, $user->hasRight(
'expedition',
'creer'),
'string'.(isset($conf->global->THIRDPARTY_REF_INPUT_SIZE) ?
':' .
getDolGlobalString(
'THIRDPARTY_REF_INPUT_SIZE') :
''),
'', null, null,
'', 1);
465 $morehtmlref .=
'<br>'.$object->thirdparty->getNomUrl(1);
468 $langs->load(
"projects");
469 $morehtmlref .=
'<br>';
471 $morehtmlref .=
img_picto($langs->trans(
"Project"),
'project',
'class="pictofixedwidth"');
472 if ($action !=
'classify' && $permissiontoadd) {
473 $morehtmlref .=
'<a class="editfielda" href="'.dolBuildUrl($_SERVER[
'PHP_SELF'], [
'action' =>
'classify',
'id' =>
$object->id],
true).
'">'.
img_edit($langs->transnoentitiesnoconv(
'SetProject')).
'</a> ';
475 $morehtmlref .= $form->form_project($_SERVER[
'PHP_SELF'].
'?id='.
$object->id, (!
getDolGlobalString(
'PROJECT_CAN_ALWAYS_LINK_TO_ALL_SUPPLIERS') ?
$object->socid : -1), (string)
$object->fk_project, ($action ==
'classify' ?
'projectid' :
'none'), 0, 0, 0, 1,
'',
'maxwidth300');
477 if (!empty($objectsrc) && !empty($objectsrc->fk_project)) {
479 $proj->fetch($objectsrc->fk_project);
480 $morehtmlref .= $proj->getNomUrl(1);
482 $morehtmlref .=
'<span class="opacitymedium"> - '.dol_escape_htmltag($proj->title).
'</span>';
487 $morehtmlref .=
'</div>';
489 dol_banner_tab($object,
'ref', $linkback, 1,
'ref',
'ref', $morehtmlref);
492 print
'<div class="fichecenter">';
493 print
'<div class="underbanner clearboth"></div>';
495 print
'<table class="border tableforfield centpercent">';
500 print $langs->trans(
"RefOrder").
'</td>';
501 print
'<td colspan="3">';
502 print $objectsrc->getNomUrl(1,
'commande');
508 print $langs->trans(
"RefProposal").
'</td>';
509 print
'<td colspan="3">';
510 print $objectsrc->getNomUrl(1,
'expedition');
516 print
'<tr><td class="titlefield">'.$langs->trans(
"DateCreation").
'</td>';
517 print
'<td colspan="3">'.dol_print_date(
$object->date_creation,
"dayhour",
"tzuserrel").
"</td>\n";
521 print
'<tr><td height="10">';
522 print
'<table class="nobordernopadding" width="100%"><tr><td>';
523 print $langs->trans(
'DateDeliveryPlanned');
525 if ($action !=
'editdate_livraison') {
526 print
'<td class="right"><a class="editfielda" href="'.$_SERVER[
"PHP_SELF"].
'?action=editdate_livraison&token='.
newToken().
'&id='.
$object->id.
'">'.
img_edit($langs->trans(
'SetDeliveryDate'), 1).
'</a></td>';
528 print
'</tr></table>';
529 print
'</td><td colspan="2">';
530 if ($action ==
'editdate_livraison') {
531 print
'<form name="setdate_livraison" action="'.$_SERVER[
"PHP_SELF"].
'?id='.
$object->id.
'" method="post">';
532 print
'<input type="hidden" name="token" value="'.newToken().
'">';
533 print
'<input type="hidden" name="action" value="setdate_livraison">';
534 print $form->selectDate(
$object->date_delivery ?
$object->date_delivery : -1,
'liv_', 1, 1, 0,
"setdate_livraison", 1, 0);
535 print
'<input type="submit" class="button button-edit smallpaddingimp" value="'.$langs->trans(
'Modify').
'">';
541 print
'</tr></table>';
543 print
'<br><br><center>';
545 print
'<a href="'.$_SERVER[
"PHP_SELF"].
'?id='.
$object->id.
'&action=updatebyscaning&token='.
currentToken().
'" class="marginrightonly paddingright marginleftonly paddingleft">'.
img_picto(
'',
'barcode',
'class="paddingrightonly"').$langs->trans(
"UpdateByScaning").
'</a>';
547 print
'<a href="#" id="resetalltoexpected" class="marginrightonly paddingright marginleftonly paddingleft">'.img_picto(
"",
'autofill',
'class="pictofixedwidth"').$langs->trans(
"RestoreWithCurrentQtySaved").
'</a></td>';
549 print
'<a href="#" id="autoreset" class="marginrightonly paddingright marginleftonly paddingleft">'.img_picto(
"",
'eraser',
'class="pictofixedwidth"').$langs->trans(
"ClearQtys").
'</a></td>';
556 require_once DOL_DOCUMENT_ROOT.
'/product/class/html.formproduct.class.php';
558 $formproduct->loadWarehouses();
560 $listwarehouses = $entrepot->list_array(1);
568 print
'<form method="post" action="'.dolBuildUrl($_SERVER[
"PHP_SELF"]).
'">';
570 print
'<input type="hidden" name="token" value="'.newToken().
'">';
571 print
'<input type="hidden" name="action" value="updatelines">';
572 print
'<input type="hidden" name="id" value="'.$object->id.
'">';
574 print
'<div class="div-table-responsive-no-min">';
575 print
'<table class="noborder centpercent">';
578 $products_dispatched = array();
579 $sql =
"SELECT ed.fk_elementdet as rowid, sum(ed.qty) as qty";
580 $sql .=
" FROM ".$db->prefix().
"expeditiondet as ed";
581 $sql .=
" WHERE ed.fk_expedition = ".((int)
$object->id);
582 $sql .=
" GROUP BY ed.fk_elementdet";
584 $resql = $db->query($sql);
586 $num = $db->num_rows($resql);
591 $objd = $db->fetch_object($resql);
592 $products_dispatched[$objd->rowid] =
price2num($objd->qty,
'MS');
600 $sql =
"SELECT l.rowid, l.fk_product, l.subprice, l.remise_percent, '' AS sref, l.qty as qty,";
601 $sql .=
" p.ref, p.label, p.tobatch, p.fk_default_warehouse, p.barcode, p.stockable_product";
603 $parameters = array();
604 $reshook = $hookmanager->executeHooks(
605 'printFieldListSelect',
613 $sql .= $hookmanager->resPrint;
615 $sql .=
" FROM ".$db->prefix().
"commandedet as l";
616 $sql .=
" LEFT JOIN ".$db->prefix().
"product as p ON l.fk_product=p.rowid";
617 $sql .=
" WHERE l.fk_commande = ".((int) $objectsrc->id);
619 $sql .=
" AND l.product_type = 0";
622 $parameters = array();
623 $reshook = $hookmanager->executeHooks(
624 'printFieldListWhere',
632 $sql .= $hookmanager->resPrint;
635 $sql .=
" ORDER BY l.rang, p.ref, p.label";
637 $resql = $db->query($sql);
639 $num = $db->num_rows($resql);
644 print
'<tr class="liste_titre">';
646 print
'<td>'.$langs->trans(
"Description").
'</td>';
647 if ($is_mod_batch_enabled) {
648 print
'<td class="dispatch_batch_number_title">'.$langs->trans(
"batch_number").
'</td>';
649 if ($is_sell_by_enabled) {
650 print
'<td class="dispatch_dlc_title">'.$langs->trans(
"SellByDate").
'</td>';
652 if ($is_eat_by_enabled) {
653 print
'<td class="dispatch_dluo_title">'.$langs->trans(
"EatByDate").
'</td>';
660 print
'<td class="right">'.$langs->trans(
"QtyOrdered").
'</td>';
662 print
'<td class="right">'.$langs->trans(
"QtyToShip");
664 print
'<td class="right">'.$langs->trans(
"QtyDispatchedShort").
'</td>';
666 print
'<td class="right">'.$langs->trans(
"Details");
667 print
'<td width="32"></td>';
670 if (!
isModEnabled(
"multicurrency") && empty($conf->dynamicprices->enabled)) {
671 print
'<td class="right">'.$langs->trans(
"Price").
'</td>';
672 print
'<td class="right">'.$langs->trans(
"ReductionShort").
' (%)</td>';
673 print
'<td class="right">'.$langs->trans(
"UpdatePrice").
'</td>';
677 print
'<td align="right">'.$langs->trans(
"Warehouse");
680 if (count($listwarehouses) > 1) {
681 print
'<br><span class="opacitymedium">'.$langs->trans(
"ForceTo").
'</span> '.$form->selectarray(
'fk_default_warehouse', $listwarehouses, $fk_default_warehouse, 1, 0, 0,
'', 0, 0, $disabled,
'',
'minwidth100 maxwidth300', 1);
682 } elseif (count($listwarehouses) == 1) {
683 print
'<br><span class="opacitymedium">'.$langs->trans(
"ForceTo").
'</span> '.$form->selectarray(
'fk_default_warehouse', $listwarehouses, $fk_default_warehouse, 0, 0, 0,
'', 0, 0, $disabled,
'',
'minwidth100 maxwidth300', 1);
689 $parameters = array();
690 $reshook = $hookmanager->executeHooks(
691 'printFieldListTitle',
699 print $hookmanager->resPrint;
704 $conf->cache[
'product'] = array();
708 $objp = $db->fetch_object($resql);
711 if (!$objp->fk_product > 0) {
714 $alreadydispatched = isset($products_dispatched[$objp->rowid]) ? $products_dispatched[$objp->rowid] : 0;
715 $remaintodispatch =
price2num($objp->qty, 5);
716 if ($remaintodispatch < 0 && !
getDolGlobalString(
'SUPPLIER_ORDER_ALLOW_NEGATIVE_QTY_FOR_SUPPLIER_ORDER_RETURN')) {
717 $remaintodispatch = 0;
720 if ($remaintodispatch || !
getDolGlobalString(
'SUPPLIER_ORDER_DISABLE_STOCK_DISPATCH_WHEN_TOTAL_REACHED')) {
729 print
'<!-- Line to dispatch '.$suffix.
' -->'.
"\n";
731 print
'<input id="qty_ordered'.$suffix.
'" type="hidden" value="'.$objp->qty.
'">';
732 print
'<input id="qty_dispatched'.$suffix.
'" type="hidden" data-dispatched="'.((float) $alreadydispatched).
'" value="'.(float) $alreadydispatched.
'">';
733 print
'<tr class="oddeven">';
735 if (empty($conf->cache[
'product'][$objp->fk_product])) {
736 $tmpproduct =
new Product($db);
737 $tmpproduct->fetch($objp->fk_product);
738 $conf->cache[
'product'][$objp->fk_product] = $tmpproduct;
740 $tmpproduct = $conf->cache[
'product'][$objp->fk_product];
743 $linktoprod = $tmpproduct->getNomUrl(1);
744 $linktoprod .=
' - '.$objp->label.
"\n";
746 if ($is_mod_batch_enabled) {
747 if ($objp->tobatch) {
749 print
'<td id="product_'.$i.
'" data-idproduct="'.$objp->fk_product.
'" data-barcode="'.$objp->barcode.
'">';
752 print
'<td class="dispatch_batch_number"></td>';
753 if ($is_sell_by_enabled) {
754 print
'<td class="dispatch_dlc"></td>';
756 if ($is_eat_by_enabled) {
757 print
'<td class="dispatch_dluo"></td>';
761 print
'<td id="product_'.$i.
'" data-idproduct="'.$objp->fk_product.
'" data-barcode="'.$objp->barcode.
'">';
764 print
'<td class="dispatch_batch_number">';
765 print
'<span class="opacitymedium small">'.$langs->trans(
"ProductDoesNotUseBatchSerial").
'</span>';
767 if ($is_sell_by_enabled) {
768 print
'<td class="dispatch_dlc"></td>';
770 if ($is_eat_by_enabled) {
771 print
'<td class="dispatch_dluo"></td>';
775 print
'<td colspan="4">';
781 $up_ht_disc = $objp->subprice;
782 if (!empty($objp->remise_percent) && !
getDolGlobalString(
'STOCK_EXCLUDE_DISCOUNT_FOR_PMP')) {
783 $up_ht_disc =
price2num($up_ht_disc * (100 - $objp->remise_percent) / 100,
'MU');
787 print
'<td class="right">'.$objp->qty.
'</td>';
790 print
'<td class="right">'.$alreadydispatched.
'</td>';
792 print
'<td class="right">';
798 $sql =
"SELECT ed.rowid, ed.fk_parent";
799 $sql .=
", cd.fk_product";
800 $sql .=
", ".$db->ifsql(
'eb.rowid IS NULL',
'ed.qty',
'eb.qty').
" as qty";
801 $sql .=
", ".$db->ifsql(
'eb.rowid IS NULL OR eb.fk_warehouse IS NULL',
'ed.fk_entrepot',
'eb.fk_warehouse').
" as fk_warehouse";
802 $sql .=
", eb.batch, eb.eatby, eb.sellby";
803 $sql .=
" FROM ".$db->prefix().
"expeditiondet as ed";
804 $sql .=
" LEFT JOIN ".$db->prefix().
"expeditiondet_batch as eb on ed.rowid = eb.fk_expeditiondet";
805 $sql .=
" INNER JOIN ".$db->prefix().
"commandedet as cd on ed.fk_elementdet = cd.rowid";
806 $sql .=
" WHERE ed.fk_elementdet = ".(int) $objp->rowid;
807 $sql .=
" AND ed.fk_expedition = ".(int)
$object->id;
808 $sql .=
" ORDER BY ed.rowid, ed.fk_elementdet";
810 $resultsql = $db->query($sql);
813 $numd = $db->num_rows($resultsql);
814 while ($obj_exp = $db->fetch_object($resultsql)) {
815 $suffix =
"_" . $j .
"_" . $i;
817 $productChildrenNb = 0;
818 $expedition_line_child_list = array();
821 $productChildrenNb = $tmpproduct->hasFatherOrChild(1);
822 if ($productChildrenNb > 0) {
823 $line_id_list = array();
827 $result = $expeditionLine->findAllChild($obj_exp->rowid, $line_id_list, 1);
830 foreach ($line_id_list as $line_id_arr) {
831 foreach ($line_id_arr as $line_obj) {
832 $child_product_id = (int) $line_obj->fk_product;
833 if (empty($conf->cache[
'product'][$child_product_id])) {
834 $child_product =
new Product($db);
835 $child_product->fetch($child_product_id);
836 $conf->cache[
'product'][$child_product_id] = $child_product;
838 $child_product = $conf->cache[
'product'][$child_product_id];
842 $batch_list = array();
843 if ($is_mod_batch_enabled && $child_product->hasbatch()) {
845 $sql_line_batch_search =
"SELECT eb.rowid, eb.qty, eb.batch, eb.sellby, eb.eatby";
846 $sql_line_batch_search .=
" FROM ".$db->prefix().
"expeditiondet_batch as eb";
847 $sql_line_batch_search .=
" WHERE eb.fk_expeditiondet = ".((int) $line_obj->rowid);
848 $res_line_batch_search = $db->query($sql_line_batch_search);
849 if ($res_line_batch_search) {
850 while ($obj_batch = $db->fetch_object($res_line_batch_search)) {
852 if ($obj_batch->batch !=
'') {
853 $line_obj->batch = $obj_batch->batch;
857 $batch_list[] = $obj_batch;
859 $db->free($res_line_batch_search);
863 if (empty($batch_list)) {
864 $batch_sort_field_arr = array();
865 $batch_sort_order_arr = array();
866 if ($is_sell_by_enabled) {
867 $batch_sort_field_arr[] =
'pl.sellby';
868 $batch_sort_order_arr[] =
'ASC';
870 if ($is_eat_by_enabled) {
871 $batch_sort_field_arr[] =
'pl.eatby';
872 $batch_sort_order_arr[] =
'ASC';
874 $batch_sort_field_arr[] =
'pb.qty';
875 $batch_sort_order_arr[] =
'ASC';
876 $batch_sort_field_arr[] =
'pl.rowid';
877 $batch_sort_order_arr[] =
'ASC';
879 $product_batch_result = $product_batch->findAllForProduct($child_product_id, $line_obj->fk_warehouse, (
getDolGlobalInt(
'STOCK_DISALLOW_NEGATIVE_TRANSFER') ? 0 : null), implode(
',', $batch_sort_field_arr), implode(
',', $batch_sort_order_arr));
880 if (is_array($product_batch_result)) {
881 foreach ($product_batch_result as $batch_current) {
882 $batch_current->eatby =
dol_print_date($batch_current->eatby,
'day');
883 $batch_current->sellby =
dol_print_date($batch_current->sellby,
'day');
884 $batch_list[] = $batch_current;
889 $line_obj->batch_list = $batch_list;
892 $line_obj->iskit = 0;
893 if ($child_product->stockable_product == Product::ENABLED_STOCK) {
894 $can_manage_stock = 1;
896 $can_manage_stock = 0;
898 $line_obj->incdec = $can_manage_stock;
899 $sql_child =
"SELECT ";
900 $sql_child .=
" SUM(".$db->ifsql(
"pa.rowid IS NOT NULL",
"1",
"0").
") as iskit";
901 $sql_child .=
", ".$db->ifsql(
"pai.incdec IS NULL",
"1",
"pai.incdec").
" as incdec";
902 $sql_child .=
" FROM ".$db->prefix().
"expeditiondet as ed";
903 $sql_child .=
" LEFT JOIN ".$db->prefix().
"expeditiondet as edp ON edp.rowid = ".((int) $line_obj->fk_parent);
904 $sql_child .=
" LEFT JOIN ".$db->prefix().
"product_association as pa ON pa.fk_product_pere = ".((int) $child_product_id);
905 $sql_child .=
" LEFT JOIN ".$db->prefix().
"product_association as pai ON pai.fk_product_pere = edp.fk_product AND pai.fk_product_fils = ".((int) $child_product_id);
906 $sql_child .=
" WHERE ed.rowid = ".((int) $line_obj->rowid);
907 $sql_child .=
" GROUP BY pa.rowid, pai.incdec";
908 $resql_child = $db->query($sql_child);
910 if ($child_obj = $db->fetch_object($resql_child)) {
911 if (!
getDolGlobalInt(
'PRODUIT_SOUSPRODUITS_ALSO_ENABLE_PARENT_STOCK_MOVE')) $line_obj->iskit = (
int) $child_obj->iskit;
912 if ($can_manage_stock) {
913 $line_obj->incdec = (int) $child_obj->incdec;
916 $db->free($resql_child);
918 $line_obj->html_label = str_repeat(
" ", $child_level) .
"→" . $child_product->getNomUrl(1);
919 $expedition_line_child_list[] = $line_obj;
926 if (empty($expedition_line_child_list)) {
929 if ($objp->stockable_product == Product::ENABLED_STOCK) {
930 $obj_exp->incdec = 1;
932 $obj_exp->incdec = 0;
934 $expedition_line_child_list[] = $obj_exp;
937 $child_suffix = $suffix;
938 foreach ($expedition_line_child_list as $objd) {
939 $child_line_id = $objd->rowid;
941 $can_update_stock = empty($objd->iskit) && !empty($objd->incdec);
942 $suffix = $child_line_id.$child_suffix;
945 $dispatch_line_batch_current =
null;
946 if (!empty($objd->batch_list)) {
947 $dispatch_line_batch_count = count($objd->batch_list);
949 if ($dispatch_line_batch_count == 1) {
950 $dispatch_line_batch_current = current($objd->batch_list);
953 if (is_object($dispatch_line_batch_current)) {
954 $objd->batch = $dispatch_line_batch_current->batch;
955 $objd->eatby = $dispatch_line_batch_current->eatby;
956 $objd->sellby = $dispatch_line_batch_current->sellby;
959 if ($is_mod_batch_enabled
962 || (is_null($objd->batch) && $tmpproduct->status_batch > 0)
963 || !empty($objd->batch_list)
971 'is_information_row' =>
true,
976 $reshook = $hookmanager->executeHooks(
977 'printFieldListValue',
985 print $hookmanager->resPrint;
989 print
'<!-- line for batch ' . $numline .
' -->';
990 print
'<tr class="oddeven autoresettr" name="' . $type .
'-' . $suffix .
'" data-remove="clear">';
992 print
'<input id="fk_commandedet' . $suffix .
'" name="fk_commandedet' . $suffix .
'" type="hidden" value="' . $objp->rowid .
'">';
993 print
'<input id="idline' . $suffix .
'" name="idline' . $suffix .
'" type="hidden" value="' . $objd->rowid .
'">';
994 print
'<input id="fk_parent' . $suffix .
'" name="fk_parent' . $suffix .
'" type="hidden" value="' . $objd->fk_parent .
'">';
995 print
'<input name="productbatch' . $suffix .
'" type="hidden" value="' . $objd->fk_product .
'">';
997 print
'<!-- This is a U.P. (may include discount or not depending on STOCK_EXCLUDE_DISCOUNT_FOR_PMP. will be used for PMP calculation) -->';
998 print
'<input class="maxwidth75" name="pu' . $suffix .
'" type="hidden" value="' .
price2num($up_ht_disc,
'MU') .
'">';
999 if (!empty($objd->html_label)) {
1000 print $objd->html_label;
1005 print
'<input type="text" class="inputlotnumber quatrevingtquinzepercent csslotnumber" name="lot_number'.$suffix.
'" value="'.(GETPOSTISSET(
'lot_number'.$suffix) ?
GETPOST(
'lot_number'.$suffix) : $objd->batch).
'" list="lot_number'.$suffix.
'">';
1006 print $formproduct->selectLotDataList(
'lot_number'.$suffix, 0, $objd->fk_product,
GETPOST(
"entrepot".$suffix) ?
GETPOST(
"entrepot".$suffix) : $objd->fk_warehouse, array());
1009 if ($is_sell_by_enabled) {
1010 print
'<td class="nowraponall">';
1012 print $form->selectDate($dlcdatesuffix,
'dlc'.$suffix, 0, 0, 1,
'');
1015 if ($is_eat_by_enabled) {
1016 print
'<td class="nowraponall">';
1018 print $form->selectDate($dluodatesuffix,
'dluo'.$suffix, 0, 0, 1,
'');
1021 print
'<td colspan="2"> </td>';
1025 $colspan = $is_sell_by_enabled ? $colspan : --$colspan;
1026 $colspan = $is_eat_by_enabled ? $colspan : --$colspan;
1029 $parameters = array(
1031 'is_information_row' =>
true,
1033 'suffix' => $suffix,
1036 $reshook = $hookmanager->executeHooks(
1037 'printFieldListValue',
1045 print $hookmanager->resPrint;
1049 print
'<!-- line no batch '.$numline.
' -->';
1050 print
'<tr class="oddeven autoresettr" name="'.$type.
'-'.$suffix.
'" data-remove="clear">';
1051 print
'<td colspan="'.$colspan.
'">';
1052 print
'<input id="fk_commandedet'.$suffix.
'" name="fk_commandedet'.$suffix.
'" type="hidden" value="'.$objp->rowid.
'">';
1053 print
'<input id="idline'.$suffix.
'" name="idline'.$suffix.
'" type="hidden" value="'.$objd->rowid.
'">';
1054 print
'<input id="fk_parent'.$suffix.
'" name="fk_parent'.$suffix.
'" type="hidden" value="'.$objd->fk_parent.
'">';
1055 print
'<input name="product'.$suffix.
'" type="hidden" value="'.$objd->fk_product.
'">';
1056 print
'<!-- This is a up (may include discount or not depending on STOCK_EXCLUDE_DISCOUNT_FOR_PMP. will be used for PMP calculation) -->';
1057 print
'<input class="maxwidth75" name="pu'.$suffix.
'" type="hidden" value="'.
price2num($up_ht_disc,
'MU').
'">';
1058 if (!empty($objd->html_label)) {
1059 print $objd->html_label;
1064 print
'<td class="right nowraponall">';
1065 $suggestedvalue = (GETPOSTISSET(
'qty'.$suffix) ?
GETPOSTFLOAT(
'qty'.$suffix) : $objd->qty);
1067 if ($can_update_stock) {
1068 print
'<a href="" id="reset'.$suffix.
'" class="resetline">'.
img_picto($langs->trans(
"Reset"),
'eraser',
'class="pictofixedwidth opacitymedium"').
'</a>';
1069 print
'<input id="qty'.$suffix.
'" onchange="onChangeDispatchLineQty($(this))" name="qty'.$suffix.
'" data-type="'.$type.
'" data-index="'.$i.
'" class="width50 right qtydispatchinput" value="'.$suggestedvalue.
'" data-expected="'.$objd->qty.
'">';
1071 print
'<input type="hidden" id="qty'.$suffix.
'" name="qty'.$suffix.
'" value="">';
1075 if ($can_update_stock) {
1076 print
img_picto($langs->trans(
'AddStockLocationLine'),
'split',
'class="splitbutton" onClick="addDispatchLine('.$i.
', \''.$type.
'-'.$child_line_id.
'\')
"');
1081 print '<td class="right
">';
1082 if ($can_update_stock) {
1083 if (count($listwarehouses) > 1) {
1084 print $formproduct->selectWarehouses(GETPOST("entrepot
".$suffix) ? GETPOST("entrepot
".$suffix) : $objd->fk_warehouse, "entrepot
".$suffix, '', 1, 0, $objd->fk_product, '', 1, 0, array(), 'csswarehouse'.$suffix);
1085 } elseif (count($listwarehouses) == 1) {
1086 print $formproduct->selectWarehouses(GETPOST("entrepot
".$suffix) ? GETPOST("entrepot
".$suffix) : $objd->fk_warehouse, "entrepot
".$suffix, '', 0, 0, $objd->fk_product, '', 1, 0, array(), 'csswarehouse'.$suffix);
1088 $langs->load("errors
");
1089 print $langs->trans("ErrorNoWarehouseDefined
");
1092 // on force l'entrepot pour passer le test d'ajout de ligne dans expedition.class.php
1093 print '<input id="entrepot
'.$suffix.'" name="entrepot
'.$suffix.'" type="hidden
" value="'.$objd->fk_warehouse.'">';
1094 print img_warning().' '.$langs->trans('StockDisabled');
1098 // Enable hooks to append additional columns
1099 $parameters = array(
1100 'is_information_row' => false, // this is a dispatch form row
1102 'suffix' => $suffix,
1106 $reshook = $hookmanager->executeHooks(
1107 'printFieldListValue',
1113 setEventMessages($hookmanager->error, $hookmanager->errors, 'errors');
1115 print $hookmanager->resPrint;
1124 //$suffix = "_
".$j."_
".$i;
1126 $errorMsg = 'Shipment dispatch SQL error : '.$db->lasterror();
1127 setEventMessage($errorMsg, 'errors');
1128 dol_syslog($errorMsg, LOG_ERR);
1133 if ($is_mod_batch_enabled && !empty($objp->tobatch)) {
1136 // Enable hooks to append additional columns
1137 $parameters = array(
1138 // allows hook to distinguish between the rows with information and the rows with dispatch form input
1139 'is_information_row' => true,
1141 'suffix' => $suffix,
1144 $reshook = $hookmanager->executeHooks(
1145 'printFieldListValue',
1151 setEventMessages($hookmanager->error, $hookmanager->errors, 'errors');
1153 print $hookmanager->resPrint;
1157 print '<!-- line for batch '.$numline.' (not dispatched line yet for this order line) -->';
1158 print '<tr class="oddeven autoresettr
" name="'.$type.$suffix.'" data-remove="clear
">';
1160 print '<input id="fk_commandedet
'.$suffix.'" name="fk_commandedet
'.$suffix.'" type="hidden
" value="'.$objp->rowid.'">';
1161 print '<input id="idline
'.$suffix.'" name="idline
'.$suffix.'" type="hidden
" value="-1
">';
1162 print '<input name="productbatch
'.$suffix.'" type="hidden
" value="'.$objp->fk_product.'">';
1164 print '<!-- This is a up (may include discount or not depending on STOCK_EXCLUDE_DISCOUNT_FOR_PMP. will be used for PMP calculation) -->';
1165 print '<input class="maxwidth75
" name="pu
'.$suffix.'" type="hidden
" value="'.price2num($up_ht_disc, 'MU
').'">';
1169 print '<input type="text
" class="inputlotnumber quatrevingtquinzepercent
" id="lot_number
'.$suffix.'" name="lot_number
'.$suffix.'" value="'.GETPOST('lot_number
'.$suffix).'">';
1171 if ($is_sell_by_enabled) {
1172 print '<td class="nowraponall
">';
1173 $dlcdatesuffix = dol_mktime(0, 0, 0, GETPOSTINT('dlc'.$suffix.'month'), GETPOSTINT('dlc'.$suffix.'day'), GETPOSTINT('dlc'.$suffix.'year'));
1174 print $form->selectDate($dlcdatesuffix, 'dlc'.$suffix, 0, 0, 1, '');
1177 if ($is_eat_by_enabled) {
1178 print '<td class="nowraponall
">';
1179 $dluodatesuffix = dol_mktime(0, 0, 0, GETPOSTINT('dluo'.$suffix.'month'), GETPOSTINT('dluo'.$suffix.'day'), GETPOSTINT('dluo'.$suffix.'year'));
1180 print $form->selectDate($dluodatesuffix, 'dluo'.$suffix, 0, 0, 1, '');
1183 print '<td colspan="2
"> </td>'; // Supplier ref + Qty ordered + qty already dispatched
1187 $colspan = $is_sell_by_enabled ? $colspan : --$colspan;
1188 $colspan = $is_eat_by_enabled ? $colspan : --$colspan;
1190 // Enable hooks to append additional columns
1191 $parameters = array(
1192 // allows hook to distinguish between the rows with information and the rows with dispatch form input
1193 'is_information_row' => true,
1195 'suffix' => $suffix,
1198 $reshook = $hookmanager->executeHooks(
1199 'printFieldListValue',
1205 setEventMessages($hookmanager->error, $hookmanager->errors, 'errors');
1207 print $hookmanager->resPrint;
1211 print '<!-- line no batch '.$numline.' (not dispatched line yet for this order line) -->';
1212 print '<tr class="oddeven autoresettr
" name="'.$type.$suffix.'" data-remove="clear
">';
1213 print '<td colspan="'.$colspan.'">';
1214 print '<input id="fk_commandedet
'.$suffix.'" name="fk_commandedet
'.$suffix.'" type="hidden
" value="'.$objp->rowid.'">';
1215 print '<input id="idline
'.$suffix.'" name="idline
'.$suffix.'" type="hidden
" value="-1
">';
1216 print '<input name="product
'.$suffix.'" type="hidden
" value="'.$objp->fk_product.'">';
1218 print '<!-- This is a up (may include discount or not depending on STOCK_EXCLUDE_DISCOUNT_FOR_PMP. will be used for PMP calculation) -->';
1219 print '<input class="maxwidth75
" name="pu
'.$suffix.'" type="hidden
" value="'.price2num($up_ht_disc, 'MU
').'">';
1223 print '<td class="right
">';
1224 print '<a href="" id="reset
'.$suffix.'" class="resetline
">'.img_picto($langs->trans("Reset
"), 'eraser', 'class="pictofixedwidth opacitymedium
"').'</a>';
1225 $amounttosuggest = (GETPOSTISSET('qty'.$suffix) ? GETPOSTINT('qty'.$suffix) : (!getDolGlobalString('SUPPLIER_ORDER_DISPATCH_FORCE_QTY_INPUT_TO_ZERO') ? $remaintodispatch : 0));
1226 if (count($products_dispatched)) {
1227 // There is already existing lines into llx_expeditiondet, this means a plan for the shipment has already been started.
1228 // In such a case, we do not suggest new values, we suggest the value known.
1229 $amounttosuggest = (GETPOSTISSET('qty'.$suffix) ? GETPOSTINT('qty'.$suffix) : (isset($products_dispatched[$objp->rowid]) ? $products_dispatched[$objp->rowid] : ''));
1231 print '<input id="qty
'.$suffix.'" onchange="onChangeDispatchLineQty($(
this))
" name="qty
'.$suffix.'" data-index="'.$i.'" data-type="text
" class="width50 right qtydispatchinput
" value="'.$amounttosuggest.'" data-expected="'.$amounttosuggest.'">';
1234 if ($is_mod_batch_enabled && $objp->tobatch > 0) {
1236 print img_picto($langs->trans('AddStockLocationLine'), 'split', 'class="splitbutton
" onClick="addDispatchLine(
'.$i.', \
''.$type.
'\')
"');
1239 print img_picto($langs->trans('AddStockLocationLine'), 'split', 'class="splitbutton
" onClick="addDispatchLine(
'.$i.', \
''.$type.
'\')
"');
1245 print '<td class="right
">';
1246 if (count($listwarehouses) > 1) {
1247 print $formproduct->selectWarehouses(GETPOST("entrepot
".$suffix) ? GETPOST("entrepot
".$suffix) : ($objp->fk_default_warehouse ? $objp->fk_default_warehouse : ''), "entrepot
".$suffix, '', 1, 0, $objp->fk_product, '', 1, 0, array(), 'csswarehouse'.$suffix);
1248 } elseif (count($listwarehouses) == 1) {
1249 print $formproduct->selectWarehouses(GETPOST("entrepot
".$suffix) ? GETPOST("entrepot
".$suffix) : ($objp->fk_default_warehouse ? $objp->fk_default_warehouse : ''), "entrepot
".$suffix, '', 0, 0, $objp->fk_product, '', 1, 0, array(), 'csswarehouse'.$suffix);
1251 $langs->load("errors
");
1252 print $langs->trans("ErrorNoWarehouseDefined
");
1256 // Enable hooks to append additional columns
1257 $parameters = array(
1258 'is_information_row' => false, // this is a dispatch form row
1260 'suffix' => $suffix,
1263 $reshook = $hookmanager->executeHooks(
1264 'printFieldListValue',
1270 setEventMessages($hookmanager->error, $hookmanager->errors, 'errors');
1272 print $hookmanager->resPrint;
1281 // reload batch select and warehouse select on change (Ajax)
1282 $out_js_line_list = array();
1283 $out_js_line = 'function updateselectbatchbywarehouse() {';
1284 $out_js_line .= ' jQuery(document).on("change
", "select[
name*=\
"entrepot\"]",
function() {
';
1285 $out_js_line .= ' var selectwarehouse = jQuery(
this);
';
1286 $out_js_line .= ' var selectbatch_name = selectwarehouse.attr(
"name").replace(
"entrepot",
"lot_number");
';
1287 $out_js_line .= ' var selectbatch = jQuery(
"datalist[id*=\""+selectbatch_name+
"\"]");
';
1288 $out_js_line .= ' var selectedbatch = selectbatch.val();
';
1289 $out_js_line .= ' var product_element_name = selectwarehouse.attr(
"name").replace(
"entrepot",
"productbatch");
';
1290 $out_js_line .= ' jQuery.ajax({
';
1291 $out_js_line .= ' type:
"POST",
';
1292 $out_js_line .= ' url:
"'.dol_escape_js(dol_buildpath('/expedition/ajax/interface.php', 1)).'",
';
1293 $out_js_line .= ' data: {
';
1294 $out_js_line .= ' action:
"updateselectbatchbywarehouse",
';
1295 $out_js_line .= ' warehouse_id: jQuery(
this).val(),
';
1296 $out_js_line .= ' token:
"'.currentToken().'",
';
1297 $out_js_line .= ' product_id: jQuery(
"input[name=\""+product_element_name+
"\"]").val()
';
1298 $out_js_line .= ' }
';
1299 $out_js_line .= ' }).done(
function(data) {
';
1300 $out_js_line .= ' selectbatch.empty();
';
1301 $out_js_line .= ' if (typeof data ==
"object") {
';
1302 $out_js_line .= ' console.log(
"data is already type object, no need to parse it");
';
1303 $out_js_line .= ' }
else {
';
1304 $out_js_line .= ' console.log(
"data is type "+(typeof data));
';
1305 $out_js_line .= ' data = JSON.parse(data);
';
1306 $out_js_line .= ' }
';
1307 $out_js_line .= ' selectbatch.append(jQuery(
"<option>", {
';
1308 $out_js_line .= ' value:
"",
';
1309 $out_js_line .= ' }));
';
1310 $out_js_line .= ' jQuery.each(data,
function(key, objBatch) {
';
1311 $out_js_line .= ' var dataEatByDate = objBatch.eatbydate;
';
1312 $out_js_line .= ' var dataSellByDate = objBatch.sellbydate;
';
1313 $out_js_line .= ' var optionLabel = key+
" (";
';
1314 $out_js_line .= ' if (selectwarehouse.val() == -1) {
';
1315 $out_js_line .= ' optionLabel +=
"'.dol_escape_js($langs->trans('TotalStock')).': "+objBatch.qty;
';
1316 $out_js_line .= ' }
else {
';
1317 $out_js_line .= ' optionLabel +=
"'.dol_escape_js($langs->trans('Stock')).': "+objBatch.qty;
';
1318 $out_js_line .= ' }
';
1319 $out_js_line .= ' if (dataEatByDate !=
"") {
';
1320 $out_js_line .= ' optionLabel +=
" - '.dol_escape_js($langs->trans('EatByDate')).': "+dataEatByDate;
';
1321 $out_js_line .= ' }
';
1322 $out_js_line .= ' if (dataSellByDate !=
"") {
';
1323 $out_js_line .= ' optionLabel +=
" - '.dol_escape_js($langs->trans('SellByDate')).': "+dataSellByDate;
';
1324 $out_js_line .= ' }
';
1325 $out_js_line .= ' optionLabel +=
")";
';
1326 $out_js_line .= ' var option =
"<option data-eatbydate=\""+dataEatByDate+
"\" data-sellbydate=\""+dataSellByDate+
"\" value=\""+key+
"\"";
';
1327 $out_js_line .= ' if (key === selectedbatch) {
';
1328 $out_js_line .= ' option +=
" selected";
';
1329 $out_js_line .= ' }
';
1330 $out_js_line .= ' option +=
">"+optionLabel+
"</option>";
';
1331 $out_js_line .= ' selectbatch.append(option);
';
1332 $out_js_line .= ' });
';
1333 $out_js_line .= ' });
';
1334 $out_js_line .= ' });
';
1335 $out_js_line .= '}
';
1337 $out_js_line .= 'function updateselectwarehousebybatch() {
';
1338 $out_js_line .= ' jQuery(document).on(
"change",
"input[name*=lot_number]",
function() {
';
1339 $out_js_line .= ' var selectbatch = jQuery(
this);
';
1340 $out_js_line .= ' var selectwarehouse_name = selectbatch.attr(
"name").replace(
"lot_number",
"entrepot");
';
1341 $out_js_line .= ' var selectwarehouse = jQuery(
"select[name*=\""+selectwarehouse_name+
"\"]");
';
1342 $out_js_line .= ' var selectedwarehouse = selectwarehouse.val();
';
1343 $out_js_line .= ' var inputbatchdlc_name = selectbatch.attr(
"name").replace(
"lot_number",
"dlc");
';
1344 $out_js_line .= ' var inputbatchdlc = jQuery(
"input[name*=\""+inputbatchdlc_name+
"\"]");
';
1345 $out_js_line .= ' var inputbatchdluo_name = selectbatch.attr(
"name").replace(
"lot_number",
"dluo");
';
1346 $out_js_line .= ' var inputbatchdluo = jQuery(
"input[name*=\""+inputbatchdluo_name+
"\"]");
';
1347 $out_js_line .= ' var datalistselectedbatch = jQuery(
"#"+selectbatch.attr(
"name")+
" option[value=\""+selectbatch.val()+
"\"]");
';
1348 $out_js_line .= ' var selectedbatch_dlc = datalistselectedbatch.data(
"sellbydate");
';
1349 $out_js_line .= ' var selectedbatch_dluo = datalistselectedbatch.data(
"eatbydate");
';
1350 $out_js_line .= ' if (typeof selectedbatch_dlc ===
"undefined") {
';
1351 $out_js_line .= ' selectedbatch_dlc =
"";
';
1352 $out_js_line .= ' }
';
1353 $out_js_line .= ' if (typeof selectedbatch_dluo ===
"undefined") {
';
1354 $out_js_line .= ' selectedbatch_dluo =
"";
';
1355 $out_js_line .= ' }
';
1356 $out_js_line .= ' inputbatchdlc.val(selectedbatch_dlc).trigger(
"change");
';
1357 $out_js_line .= ' inputbatchdluo.val(selectedbatch_dluo).trigger(
"change");
';
1358 $out_js_line .= ' if (selectedwarehouse != -1) {
';
1359 $out_js_line .= ' return;
';
1360 $out_js_line .= ' }
';
1361 $out_js_line .= ' var product_element_name = selectbatch.attr(
"name").replace(
"lot_number",
"productbatch");
';
1362 $out_js_line .= ' jQuery.ajax({
';
1363 $out_js_line .= ' type:
"POST",
';
1364 $out_js_line .= ' url:
"'.dol_escape_js(dol_buildpath('/expedition/ajax/interface.php', 1)).'",
';
1365 $out_js_line .= ' data: {
';
1366 $out_js_line .= ' action:
"updateselectwarehousebybatch",
';
1367 $out_js_line .= ' batch: jQuery(
this).val(),
';
1368 $out_js_line .= ' token:
"'.currentToken().'",
';
1369 $out_js_line .= ' product_id: jQuery(
"input[name=\""+product_element_name+
"\"]").val()
';
1370 $out_js_line .= ' }
';
1371 $out_js_line .= ' }).done(
function(data) {
';
1372 $out_js_line .= ' if (typeof data ==
"object") {
';
1373 $out_js_line .= ' console.log(
"data is already type object, no need to parse it");
';
1374 $out_js_line .= ' }
else {
';
1375 $out_js_line .= ' console.log(
"data is type "+(typeof data));
';
1376 $out_js_line .= ' data = JSON.parse(data);
';
1377 $out_js_line .= ' }
';
1378 $out_js_line .= ' if (data != 0) {
';
1379 $out_js_line .= ' selectwarehouse.val(data).change();
';
1380 $out_js_line .= ' }
';
1381 $out_js_line .= ' });
';
1382 $out_js_line .= ' });
';
1383 $out_js_line .= '}
';
1384 $out_js_line_list[] = $out_js_line;
1386 $out_js = '<script
type=
"text/javascript" language=
"javascript">
';
1387 $out_js .= 'jQuery(document).ready(
function() {
';
1388 // when a warehouse is selected, only the lot/serial numbers that are available in it are offered
1389 $out_js .= 'updateselectbatchbywarehouse();
';
1390 // when a lot/serial number is selected and it is only available in one warehouse, the warehouse is automatically selected
1391 $out_js .= 'updateselectwarehousebybatch();
';
1392 $out_js .= implode('', $out_js_line_list);
1394 $out_js .= '</script>
';
1399 dol_print_error($db);
1406 //$checkboxlabel = $langs->trans("CloseReceivedSupplierOrdersAutomatically", $langs->transnoentitiesnoconv('StatusOrderReceivedAll
'));
1408 print '<div
class=
"center">
';
1409 $parameters = array();
1410 $reshook = $hookmanager->executeHooks('addMoreActionsButtons
', $parameters, $object, $action); // Note that $action and $object may have been
1412 if (empty($reshook)) {
1413 /*if (empty($conf->reception->enabled)) {
1414 print $langs->trans("Comment").' :
';
1415 print '<input
type=
"text" class=
"minwidth400" maxlength=
"128" name=
"comment" value=
"';
1416 print GETPOSTISSET("comment
") ? GETPOST("comment
") : $langs->trans("DispatchSupplierOrder
", $object->ref);
1417 // print ' / '.$object->ref_supplier; // Not yet available
1418 print '" class=
"flat"><br>
';
1420 print '<input
type=
"checkbox" checked=
"checked" name=
"closeopenorder">
'.$checkboxlabel;
1423 $dispatchBt = empty($conf->reception->enabled) ? $langs->trans("Receive") : $langs->trans("CreateReception");
1428 print '<input
type=
"submit" id=
"submitform" class=
"button" name=
"dispatch" value=
"'.$langs->trans("Save
").'"';
1430 if (!$usercancreate) {
1433 if (count($listwarehouses) <= 0) {
1445 // Message if nothing to dispatch
1448 if (!getDolGlobalString('SUPPLIER_ORDER_DISABLE_STOCK_DISPATCH_WHEN_TOTAL_REACHED
')) {
1449 print '<div
class=
"opacitymedium">
'.$langs->trans("NoPredefinedProductToDispatch").'</div>
'; // No predefined line at all
1451 print '<div
class=
"opacitymedium">
'.$langs->trans("NoMorePredefinedProductToDispatch").'</div>
'; // No predefined line that remain to be dispatched.
1458 print dol_get_fiche_end();
1460 // Popup for mass barcode scanning
1461 if ($action == 'updatebyscaning
') {
1462 if ($permissiontoadd) {
1463 // Output the javascript to manage the scanner tool.
1467 var duplicatedbatchcode = [];
1473 function barcodescannerjs() {
1474 console.log(
"We catch inputs in scanner box");
1475 jQuery(
"#scantoolmessage").text();
1477 var selectaddorreplace = $(
"select[name=selectaddorreplace]").val();
1478 var barcodemode = $(
"input[name=barcodemode]:checked").val();
1479 var barcodeproductqty = $(
"input[name=barcodeproductqty]").val();
1480 var warehousetouse = $(
"select[name=warehousenew]").val();
1481 var textarea = $(
"textarea[name=barcodelist]").val();
1482 var textarray = textarea.split(/[\s,;]+/);
1483 var tabproduct = [];
1484 duplicatedbatchcode = [];
1490 textarray = textarray.filter(
function(value) {
1493 if (textarray.some((element) => element !=
"")) {
1494 $(
".qtydispatchinput").each(
function() {
1495 id = $(
this).attr(\
'id\');
1496 idarray = id.split(\'_\');
1497 idproduct = idarray[2];
1498 id = idarray[1] + \'_\' + idarray[2];
1499 console.log("Analyze the line "+id+" in inventory, barcodemode="+barcodemode);
1500 warehouse = $("#entrepot_"+id).val();
1501 console.log(warehouse);
1502 productbarcode = $("#product_"+idproduct).attr(\'data-barcode\');
1503 console.log(productbarcode);
1504 productbatchcode = $("#lot_number_"+id).val();
1505 if (productbatchcode == undefined) {
1506 productbatchcode = "";
1508 console.log(productbatchcode);
1510 if (barcodemode != "barcodeforproduct") {
1511 tabproduct.forEach(product=>{
1512 console.log("product.Batch="+product.Batch+" productbatchcode="+productbatchcode);
1513 if (product.Batch != "" && product.Batch == productbatchcode) {
1514 console.log("duplicate batch code found for batch code "+productbatchcode);
1515 duplicatedbatchcode.push(productbatchcode);
1519 productinput = $("#qty_"+id).val();
1520 if (productinput == "") {
1523 tabproduct.push({\'Id\':id,\'Warehouse\':warehouse,\'Barcode\':productbarcode,\'Batch\':productbatchcode,\'Qty\':productinput,\'fetched\':false});
1525 console.log("Loop on each record entered in the textarea");
1527 textarray.forEach(function(element,index) {
1528 console.log("Process record element="+element+" id="+id);
1529 var verify_batch = false;
1530 var verify_barcode = false;
1531 switch(barcodemode) {
1532 case "barcodeforautodetect":
1533 verify_barcode = barcodeserialforproduct(tabproduct,index,element,barcodeproductqty,warehousetouse,selectaddorreplace,"barcode",true);
1534 verify_batch = barcodeserialforproduct(tabproduct,index,element,barcodeproductqty,warehousetouse,selectaddorreplace,"lotserial",true);
1536 case "barcodeforproduct":
1537 verify_barcode = barcodeserialforproduct(tabproduct,index,element,barcodeproductqty,warehousetouse,selectaddorreplace,"barcode");
1539 case "barcodeforlotserial":
1540 verify_batch = barcodeserialforproduct(tabproduct,index,element,barcodeproductqty,warehousetouse,selectaddorreplace,"lotserial");
1543 alert(\''.
dol_escape_js($langs->trans(
"ErrorWrongBarcodemode")).
' "\'+barcodemode+\'"\');
1544 throw \''.
dol_escape_js($langs->trans(
'ErrorWrongBarcodemode')).
' "\'+barcodemode+\'"\';
1547 if (verify_batch == false && verify_barcode == false) { /* If the 2 flags are false, not found error */
1548 errortab2.push(element);
1549 } else if (verify_batch == true && verify_barcode == true) { /* If the 2 flags are true, error: we don t know which one to take */
1550 errortab3.push(element);
1551 } else if (verify_batch == true) {
1552 console.log("element="+element);
1553 console.log(duplicatedbatchcode);
1554 if (duplicatedbatchcode.includes(element)) {
1555 errortab1.push(element);
1560 if (Object.keys(errortab1).length < 1 && Object.keys(errortab2).length < 1 && Object.keys(errortab3).length < 1) {
1561 tabproduct.forEach(product => {
1562 if (product.Qty!=0) {
1563 if (product.hasOwnProperty("reelqty")) {
1564 idprod = $("td[data-idproduct=\'"+product.fk_product+"\']").attr("id");
1565 idproduct = idprod.split("_")[1];
1566 console.log("We create a new line for product_"+idproduct);
1567 if (product.Barcode != null) {
1568 modedispatch = "dispatch";
1570 modedispatch = "batch";
1572 addDispatchLine(idproduct,modedispatch);
1573 console.log($("tr[name^=\'"+modedispatch+"_\'][name$=\'_"+idproduct+"\']"));
1574 nbrTrs = $("tr[name^=\'"+modedispatch+"_\'][name$=\'_"+idproduct+"\']").length;
1576 $("#qty_"+(nbrTrs-1)+"_"+idproduct).val(product.Qty);
1577 $("#entrepot_"+(nbrTrs-1)+"_"+idproduct).val(product.Warehouse);
1579 if (modedispatch == "batch") {
1580 $("#lot_number_"+(nbrTrs-1)+"_"+idproduct).val(product.Batch);
1584 console.log("We change #qty_"+product.Id +" to match input in scanner box");
1585 $("#qty_"+product.Id).val(product.Qty);
1589 jQuery("#scantoolmessage").text("'.
dol_escape_js($langs->transnoentities(
"QtyWasAddedToTheScannedBarcode")).
'\n");
1590 /* document.forms["formrecord"].submit(); */
1592 let stringerror = "";
1593 if (Object.keys(errortab1).length > 0) {
1594 stringerror += "<br>'.
dol_escape_js($langs->transnoentities(
'ErrorSameBatchNumber')).
': ";
1595 errortab1.forEach(element => {
1596 stringerror += (element + ", ")
1598 stringerror = stringerror.slice(0, -2); /* Remove last ", " */
1600 if (Object.keys(errortab2).length > 0) {
1601 stringerror += "<br>'.
dol_escape_js($langs->transnoentities(
'ErrorCantFindCodeInInventory')).
': ";
1602 errortab2.forEach(element => {
1603 stringerror += (element + ", ")
1605 stringerror = stringerror.slice(0, -2); /* Remove last ", " */
1607 if (Object.keys(errortab3).length > 0) {
1608 stringerror += "<br>'.
dol_escape_js($langs->transnoentities(
'ErrorCodeScannedIsBothProductAndSerial')).
': ";
1609 errortab3.forEach(element => {
1610 stringerror += (element + ", ")
1612 stringerror = stringerror.slice(0, -2); /* Remove last ", " */
1614 if (Object.keys(errortab4).length > 0) {
1615 stringerror += "<br>'.
dol_escape_js($langs->transnoentities(
'ErrorBarcodeNotFoundForProductWarehouse')).
': ";
1616 errortab4.forEach(element => {
1617 stringerror += (element + ", ")
1619 stringerror = stringerror.slice(0, -2); /* Remove last ", " */
1622 jQuery("#scantoolmessage").html(\''.
dol_escape_js($langs->transnoentities(
"ErrorOnElementsInventory")).
'\' + stringerror);
1630 function barcodeserialforproduct(tabproduct,index,element,barcodeproductqty,warehousetouse,selectaddorreplace,mode,autodetect=
false) {
1631 BarcodeIsInProduct=0;
1634 tabproduct.forEach(product => {
1635 $.ajax({ url: \
''.DOL_URL_ROOT.
'/expedition/ajax/searchfrombarcode.php\',
1636 data: { "token":"'.
newToken().
'", "action":"existbarcode","fk_entrepot": warehousetouse, "barcode":element, "mode":mode},
1639 success: function(response) {
1640 if (response.status == "success") {
1641 console.log(response.message);
1642 if (!newproductrow) {
1643 newproductrow = response.object;
1646 if (mode!="lotserial" && autodetect==false && !errortab4.includes(element)) {
1647 errortab4.push(element);
1648 console.error(response.message);
1652 error : function(output) {
1653 console.error("Error on barcodeserialforproduct function");
1656 console.log("Product "+(index+=1)+": "+element);
1657 if (mode == "barcode") {
1658 testonproduct = product.Barcode
1659 }else if (mode == "lotserial") {
1660 testonproduct = product.Batch
1662 testonwarehouse = product.Warehouse;
1663 if (testonproduct == element && testonwarehouse == warehousetouse) {
1664 if (selectaddorreplace == "add") {
1665 productqty = parseInt(product.Qty,10);
1666 product.Qty = productqty + parseInt(barcodeproductqty,10);
1667 }else if (selectaddorreplace == "replace") {
1668 if (product.fetched == false) {
1669 product.Qty = barcodeproductqty
1670 product.fetched=true
1672 productqty = parseInt(product.Qty,10);
1673 product.Qty = productqty + parseInt(barcodeproductqty,10);
1676 BarcodeIsInProduct+=1;
1679 if (BarcodeIsInProduct==0 && newproductrow!=0) {
1680 tabproduct.push({\'Id\':tabproduct.length-1,\'Warehouse\':newproductrow.fk_warehouse,\'Barcode\':mode=="barcode"?element:null,\'Batch\':mode=="lotserial"?element:null,\'Qty\':barcodeproductqty,\'fetched\':true,\'reelqty\':newproductrow.reelqty,\'fk_product\':newproductrow.fk_product,\'mode\':mode});
1683 if (BarcodeIsInProduct > 0) {
1691 include DOL_DOCUMENT_ROOT.
'/core/class/html.formother.class.php';
1693 print $formother->getHTMLScannerForm(
"barcodescannerjs",
'all', 1);
1697 print
'<script type="text/javascript">
1698 $(document).ready(function () {
1699 $("select[name=fk_default_warehouse]").change(function() {
1700 console.log("warehouse is modified");
1701 var fk_default_warehouse = $("option:selected", this).val();
1702 $("select[name^=entrepot]").val(fk_default_warehouse).change();
1705 $("#autoreset").click(function() {
1706 console.log("we click on autoreset");
1707 $(".autoresettr").each(function() {
1708 id = $(this).attr("name");
1709 idtab = id.split("_");
1710 console.log("we process line "+id+" "+idtab);
1711 if ($(this).data("remove") == "clear") { /* data-remove=clear means that line qty must be cleared but line must not be removed */
1712 console.log("We clear the object to expected value")
1713 var idlinetab = idtab[0].split("-");
1715 if (idlinetab.length > 0) {
1716 idline = idlinetab[1];
1718 $("#qty"+idline+"_"+idtab[1]+"_"+idtab[2]).val("");
1720 qtyexpected = $("#qty_"+idtab[1]+"_"+idtab[2]).data("expected")
1721 console.log(qtyexpected);
1722 $("#qty_"+idtab[1]+"_"+idtab[2]).val(qtyexpected);
1723 qtydispatched = $("#qty_dispatched_0_"+idtab[2]).data("dispatched")
1724 $("#qty_dispatched_0_"+idtab[2]).val(qtydispatched);
1726 } else { /* data-remove=remove means that line must be removed */
1727 console.log("We remove the object")
1729 $("tr[name^=\'"+idtab[0]+"_\'][name$=\'_"+idtab[2]+"\']:last .splitbutton").show();
1735 $("#resetalltoexpected").click(function() {
1736 $(".qtydispatchinput").each(function() {
1737 console.log("We reset to expected "+$(this).attr("id")+" qty to dispatch");
1738 $(this).val($(this).data("expected"));
1743 $(".resetline").on("click", function(event) {
1744 event.preventDefault();
1745 id = $(this).attr("id");
1746 id = id.split("reset");
1747 console.log("Reset trigger for id = qty"+id[1]);
1748 $("#qty"+id[1]).val("");
$id
Support class for third parties, contacts, members, users or resources.
if(! $sortfield) if(! $sortorder) $object
llxFooter($comment='', $zone='private', $disabledoutputofmessages=0)
Empty footer.
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.
Class to manage customers orders.
Class to manage warehouses.
const STATUS_DRAFT
Draft status.
Class to manage lines of shipment.
CRUD class for batch number management within shipment.
Class to manage products or services.
Manage record for batch number management.
Class to manage projects.
Class to manage proposals.
Class to manage third parties objects (customers, suppliers, prospects...)
Class to manage Dolibarr users.
dol_stringtotime($string, $gm=1)
Convert a string date into a GM Timestamps date Warning: YYYY-MM-DDTHH:MM:SS+02:00 (RFC3339) is not s...
dol_now($mode='gmt')
Return date for now.
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...
setEventMessages($mesg, $mesgs, $style='mesgs', $messagekey='', $noduplicate=0, $attop=0)
Set event messages in dol_events session object.
img_picto($titlealt, $picto, $moreatt='', $pictoisfullpath=0, $srconly=0, $notitle=0, $alt='', $morecss='', $marginleftonlyshort=2, $allowothertags=array())
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, $morecssdiv='')
Show tabs of a record.
price2num($amount, $rounding='', $option=0)
Function that return a number with universal decimal format (decimal separator is '.
currentToken()
Return the value of token currently saved into session with name 'token'.
getDolGlobalInt($key, $default=0)
Return a Dolibarr global constant int value.
dol_escape_js($stringtoescape, $mode=0, $noescapebackslashn=0)
Returns text escaped for inclusion into javascript code.
newToken()
Return the value of token currently saved into session with name 'newtoken'.
GETPOSTFLOAT($paramname, $rounding='', $option=2)
Return the value of a $_GET or $_POST supervariable, converted into float.
GETPOST($paramname, $check='alphanohtml', $method=0, $filter=null, $options=null, $noreplace=0)
Return value of a param into GET or POST supervariable.
dol_print_date($time, $format='', $tzoutput='auto', $outputlangs=null, $encodetooutput=false, $decorate=0)
Output date in a string format according to outputlangs (or langs if not defined).
dol_print_error($db=null, $error='', $errors=null)
Displays error message system with all the information to facilitate the diagnosis and the escalation...
getDolGlobalString($key, $default='')
Return a Dolibarr global constant string value.
isModEnabled($module)
Is Dolibarr module enabled.
img_edit($titlealt='default', $float=0, $other='')
Show logo edit/modify fiche.
dol_syslog($message, $level=LOG_INFO, $ident=0, $suffixinfilename='', $restricttologhandler='', $logcontext=null)
Write log message into outputs.
if(!defined( 'CSRFCHECK_WITH_TOKEN'))
Abort invoice creation with a given error message.
if(preg_match('/(crypted|dolcrypt):/i', $dolibarr_main_db_pass)||!empty($dolibarr_main_db_encrypted_pass)) $conf db type
'integer', 'integer:ObjectClass:PathToClass[:AddCreateButtonOrNot[:Filter[:Sortfield]]]',...
$conf db name
Only used if Module[ID]Name translation string is not found.
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.
accessforbidden($message='', $printheader=1, $printfooter=1, $showonlymessage=0, $params=null)
Show a message to say access is forbidden and stop program.
shipping_prepare_head($object)
Prepare array with list of tabs.