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);
225 if ($result > 0 && (
int) $expeditiondispatch->fk_expedition !== (
int)
$object->id) {
227 $expeditiondispatch->error = $langs->trans(
'ErrorRecordNotFound');
231 setEventMessages($expeditiondispatch->error, $expeditiondispatch->errors,
'errors');
234 $qtystart = $expeditiondispatch->qty;
235 $expeditiondispatch->qty = $newqty;
236 $expeditiondispatch->entrepot_id =
GETPOSTINT($ent);
239 $result = $expeditiondispatch->update($user);
241 $result = $expeditiondispatch->delete($user);
244 setEventMessages($expeditiondispatch->error, $expeditiondispatch->errors,
'errors');
248 if (!$error && $modebatch ==
"batch") {
250 $suffixkeyfordate = preg_replace(
'/^productbatch/',
'', $key);
254 $sqlsearchdet =
"SELECT rowid FROM ".$db->prefix().$expeditionlinebatch->table_element;
255 $sqlsearchdet .=
" WHERE fk_expeditiondet = ".((int) $idline);
256 $resqlsearchdet = $db->query($sqlsearchdet);
258 $objsearchdet =
null;
259 if ($resqlsearchdet) {
260 $objsearchdet = $db->fetch_object($resqlsearchdet);
266 $sql =
"UPDATE ".$db->prefix().$expeditionlinebatch->table_element.
" SET";
267 $sql .=
" batch = '".$db->escape($lot).
"'";
268 $sql .=
", eatby = ".($eatby ?
"'".$db->idate($eatby).
"'" :
"null");
269 $sql .=
", sellby = ".($sellby ?
"'".$db->idate($sellby).
"'" :
"null");
270 $sql .=
", qty = ".((float) $newqty);
271 $sql .=
", fk_warehouse = ".((int) $warehouse_id);
272 $sql .=
" WHERE rowid = ".((int) $objsearchdet->rowid);
274 $sql =
"INSERT INTO ".$db->prefix().$expeditionlinebatch->table_element.
" (";
275 $sql .=
"fk_expeditiondet, eatby, sellby, batch, qty, fk_origin_stock, fk_warehouse)";
276 $sql .=
" VALUES (".((int) $idline).
", ".($eatby ?
"'".$db->idate($eatby).
"'" :
"null").
", ".($sellby ?
"'".$db->idate($sellby).
"'" :
"null").
", ";
277 $sql .=
" '".$db->escape($lot).
"', ".((float) $newqty).
", 0, ".((int) $warehouse_id).
")";
280 $sql =
"DELETE FROM ".$db->prefix().$expeditionlinebatch->table_element;
281 $sql .=
" WHERE fk_expeditiondet = ".((int) $idline);
282 $sql .=
" AND batch = '".$db->escape($lot).
"'";
285 $resql = $db->query($sql);
293 $expeditiondispatch->fk_expedition =
$object->id;
294 $expeditiondispatch->entrepot_id =
GETPOSTINT($ent);
295 $expeditiondispatch->fk_parent =
GETPOSTINT(
'fk_parent'.$dispatch_line_suffix);
296 $expeditiondispatch->fk_product = $prod_id;
297 if (!($expeditiondispatch->fk_parent > 0)) {
298 $expeditiondispatch->fk_elementdet =
GETPOSTINT($fk_commandedet);
300 $expeditiondispatch->qty = $newqty;
303 $idline = $expeditiondispatch->insert($user);
305 setEventMessages($expeditiondispatch->error, $expeditiondispatch->errors,
'errors');
309 if ($modebatch ==
"batch" && !$error) {
310 $expeditionlinebatch->sellby = $dDLC;
311 $expeditionlinebatch->eatby = $dDLUO;
312 $expeditionlinebatch->batch = $lot;
313 $expeditionlinebatch->qty = $newqty;
314 $expeditionlinebatch->fk_origin_stock = 0;
315 $expeditionlinebatch->fk_warehouse =
GETPOSTINT($ent);
317 $result = $expeditionlinebatch->create($idline);
319 setEventMessages($expeditionlinebatch->error, $expeditionlinebatch->errors,
'errors');
376 header(
"Location: ".DOL_URL_ROOT.
'/expedition/dispatch.php?id='.
$object->id);
379 } elseif ($action ==
'setdate_livraison' && $usercancreate) {
383 $result =
$object->setDeliveryDate($user, $datedelivery);
397$form =
new Form($db);
399$warehouse_static =
new Entrepot($db);
401$title =
$object->ref.
" - ".$langs->trans(
'ShipmentDistribution');
402$help_url =
'EN:Module_Shipments|FR:Module_Expéditions|ES:Módulo_Expediciones|DE:Modul_Lieferungen';
403$morejs = array(
'/expedition/js/lib_dispatch.js.php');
406llxHeader(
'', $title, $help_url,
'', 0, 0, $morejs,
'',
'',
'mod-expedition page-card_dispatch');
412 $num_prod = count($lines);
424 $author =
new User($db);
425 $author->fetch(
$object->user_author_id);
435 if ($action ==
'ask_deleteline') {
436 $formconfirm = $form->formconfirm($_SERVER[
"PHP_SELF"].
'?id='.
$object->id.
'&lineid='.$lineid, $langs->trans(
'DeleteLine'), $langs->trans(
'ConfirmDeleteLine'),
'confirm_deleteline',
'', 0, 1);
440 $parameters = array(
'lineid' => $lineid);
442 $reshook = $hookmanager->executeHooks(
'formConfirm', $parameters, $object, $action);
443 if (empty($reshook)) {
444 $formconfirm .= $hookmanager->resPrint;
445 } elseif ($reshook > 0) {
446 $formconfirm = $hookmanager->resPrint;
454 $objectsrc->fetch(
$object->origin_object->id);
457 $objectsrc =
new Propal($db);
458 $objectsrc->fetch(
$object->origin_object->id);
462 $linkback =
'<a href="'.DOL_URL_ROOT.
'/expedition/list.php?restore_lastsearch_values=1'.(!empty($socid) ?
'&socid='.$socid :
'').
'">'.$langs->trans(
"BackToList").
'</a>';
463 $morehtmlref =
'<div class="refidno">';
466 $morehtmlref .= $form->editfieldkey(
"RefCustomer",
'ref_customer',
$object->ref_customer, $object, $user->hasRight(
'expedition',
'creer'),
'string',
'', 0, 1);
467 $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);
470 $morehtmlref .=
'<br>'.$object->thirdparty->getNomUrl(1);
473 $langs->load(
"projects");
474 $morehtmlref .=
'<br>';
476 $morehtmlref .=
img_picto($langs->trans(
"Project"),
'project',
'class="pictofixedwidth"');
477 if ($action !=
'classify' && $permissiontoadd) {
478 $morehtmlref .=
'<a class="editfielda" href="'.dolBuildUrl($_SERVER[
'PHP_SELF'], [
'action' =>
'classify',
'id' =>
$object->id],
true).
'">'.
img_edit($langs->transnoentitiesnoconv(
'SetProject')).
'</a> ';
480 $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');
482 if (!empty($objectsrc) && !empty($objectsrc->fk_project)) {
484 $proj->fetch($objectsrc->fk_project);
485 $morehtmlref .= $proj->getNomUrl(1);
487 $morehtmlref .=
'<span class="opacitymedium"> - '.dol_escape_htmltag($proj->title).
'</span>';
492 $morehtmlref .=
'</div>';
494 dol_banner_tab($object,
'ref', $linkback, 1,
'ref',
'ref', $morehtmlref);
497 print
'<div class="fichecenter">';
498 print
'<div class="underbanner clearboth"></div>';
500 print
'<table class="border tableforfield centpercent">';
505 print $langs->trans(
"RefOrder").
'</td>';
506 print
'<td colspan="3">';
507 print $objectsrc->getNomUrl(1,
'commande');
513 print $langs->trans(
"RefProposal").
'</td>';
514 print
'<td colspan="3">';
515 print $objectsrc->getNomUrl(1,
'expedition');
521 print
'<tr><td class="titlefield">'.$langs->trans(
"DateCreation").
'</td>';
522 print
'<td colspan="3">'.dol_print_date(
$object->date_creation,
"dayhour",
"tzuserrel").
"</td>\n";
526 print
'<tr><td height="10">';
527 print
'<table class="nobordernopadding" width="100%"><tr><td>';
528 print $langs->trans(
'DateDeliveryPlanned');
530 if ($action !=
'editdate_livraison') {
531 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>';
533 print
'</tr></table>';
534 print
'</td><td colspan="2">';
535 if ($action ==
'editdate_livraison') {
536 print
'<form name="setdate_livraison" action="'.$_SERVER[
"PHP_SELF"].
'?id='.
$object->id.
'" method="post">';
537 print
'<input type="hidden" name="token" value="'.newToken().
'">';
538 print
'<input type="hidden" name="action" value="setdate_livraison">';
539 print $form->selectDate(
$object->date_delivery ?
$object->date_delivery : -1,
'liv_', 1, 1, 0,
"setdate_livraison", 1, 0);
540 print
'<input type="submit" class="button button-edit smallpaddingimp" value="'.$langs->trans(
'Modify').
'">';
546 print
'</tr></table>';
548 print
'<br><br><center>';
550 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>';
552 print
'<a href="#" id="resetalltoexpected" class="marginrightonly paddingright marginleftonly paddingleft">'.img_picto(
"",
'autofill',
'class="pictofixedwidth"').$langs->trans(
"RestoreWithCurrentQtySaved").
'</a></td>';
554 print
'<a href="#" id="autoreset" class="marginrightonly paddingright marginleftonly paddingleft">'.img_picto(
"",
'eraser',
'class="pictofixedwidth"').$langs->trans(
"ClearQtys").
'</a></td>';
561 require_once DOL_DOCUMENT_ROOT.
'/product/class/html.formproduct.class.php';
563 $formproduct->loadWarehouses();
565 $listwarehouses = $entrepot->list_array(1);
573 print
'<form method="post" action="'.dolBuildUrl($_SERVER[
"PHP_SELF"]).
'">';
575 print
'<input type="hidden" name="token" value="'.newToken().
'">';
576 print
'<input type="hidden" name="action" value="updatelines">';
577 print
'<input type="hidden" name="id" value="'.$object->id.
'">';
579 print
'<div class="div-table-responsive-no-min">';
580 print
'<table class="noborder centpercent">';
583 $products_dispatched = array();
584 $sql =
"SELECT ed.fk_elementdet as rowid, sum(ed.qty) as qty";
585 $sql .=
" FROM ".$db->prefix().
"expeditiondet as ed";
586 $sql .=
" WHERE ed.fk_expedition = ".((int)
$object->id);
587 $sql .=
" GROUP BY ed.fk_elementdet";
589 $resql = $db->query($sql);
591 $num = $db->num_rows($resql);
596 $objd = $db->fetch_object($resql);
597 $products_dispatched[$objd->rowid] =
price2num($objd->qty,
'MS');
605 $sql =
"SELECT l.rowid, l.fk_product, l.subprice, l.remise_percent, '' AS sref, l.qty as qty,";
606 $sql .=
" p.ref, p.label, p.tobatch, p.fk_default_warehouse, p.barcode, p.stockable_product";
608 $parameters = array();
609 $reshook = $hookmanager->executeHooks(
610 'printFieldListSelect',
618 $sql .= $hookmanager->resPrint;
620 $sql .=
" FROM ".$db->prefix().
"commandedet as l";
621 $sql .=
" LEFT JOIN ".$db->prefix().
"product as p ON l.fk_product=p.rowid";
622 $sql .=
" WHERE l.fk_commande = ".((int) $objectsrc->id);
624 $sql .=
" AND l.product_type = 0";
627 $parameters = array();
628 $reshook = $hookmanager->executeHooks(
629 'printFieldListWhere',
637 $sql .= $hookmanager->resPrint;
640 $sql .=
" ORDER BY l.rang, p.ref, p.label";
642 $resql = $db->query($sql);
644 $num = $db->num_rows($resql);
649 print
'<tr class="liste_titre">';
651 print
'<td>'.$langs->trans(
"Description").
'</td>';
652 if ($is_mod_batch_enabled) {
653 print
'<td class="dispatch_batch_number_title">'.$langs->trans(
"batch_number").
'</td>';
654 if ($is_sell_by_enabled) {
655 print
'<td class="dispatch_dlc_title">'.$langs->trans(
"SellByDate").
'</td>';
657 if ($is_eat_by_enabled) {
658 print
'<td class="dispatch_dluo_title">'.$langs->trans(
"EatByDate").
'</td>';
665 print
'<td class="right">'.$langs->trans(
"QtyOrdered").
'</td>';
667 print
'<td class="right">'.$langs->trans(
"QtyToShip");
669 print
'<td class="right">'.$langs->trans(
"QtyDispatchedShort").
'</td>';
671 print
'<td class="right">'.$langs->trans(
"Details");
672 print
'<td width="32"></td>';
675 if (!
isModEnabled(
"multicurrency") && empty($conf->dynamicprices->enabled)) {
676 print
'<td class="right">'.$langs->trans(
"Price").
'</td>';
677 print
'<td class="right">'.$langs->trans(
"ReductionShort").
' (%)</td>';
678 print
'<td class="right">'.$langs->trans(
"UpdatePrice").
'</td>';
682 print
'<td align="right">'.$langs->trans(
"Warehouse");
685 if (count($listwarehouses) > 1) {
686 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);
687 } elseif (count($listwarehouses) == 1) {
688 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);
694 $parameters = array();
695 $reshook = $hookmanager->executeHooks(
696 'printFieldListTitle',
704 print $hookmanager->resPrint;
709 $conf->cache[
'product'] = array();
713 $objp = $db->fetch_object($resql);
716 if (!$objp->fk_product > 0) {
719 $alreadydispatched = isset($products_dispatched[$objp->rowid]) ? $products_dispatched[$objp->rowid] : 0;
720 $remaintodispatch =
price2num($objp->qty, 5);
721 if ($remaintodispatch < 0 && !
getDolGlobalString(
'SUPPLIER_ORDER_ALLOW_NEGATIVE_QTY_FOR_SUPPLIER_ORDER_RETURN')) {
722 $remaintodispatch = 0;
725 if ($remaintodispatch || !
getDolGlobalString(
'SUPPLIER_ORDER_DISABLE_STOCK_DISPATCH_WHEN_TOTAL_REACHED')) {
734 print
'<!-- Line to dispatch '.$suffix.
' -->'.
"\n";
736 print
'<input id="qty_ordered'.$suffix.
'" type="hidden" value="'.$objp->qty.
'">';
737 print
'<input id="qty_dispatched'.$suffix.
'" type="hidden" data-dispatched="'.((float) $alreadydispatched).
'" value="'.(float) $alreadydispatched.
'">';
738 print
'<tr class="oddeven">';
740 if (empty($conf->cache[
'product'][$objp->fk_product])) {
741 $tmpproduct =
new Product($db);
742 $tmpproduct->fetch($objp->fk_product);
743 $conf->cache[
'product'][$objp->fk_product] = $tmpproduct;
745 $tmpproduct = $conf->cache[
'product'][$objp->fk_product];
748 $linktoprod = $tmpproduct->getNomUrl(1);
749 $linktoprod .=
' - '.$objp->label.
"\n";
751 if ($is_mod_batch_enabled) {
752 if ($objp->tobatch) {
754 print
'<td id="product_'.$i.
'" data-idproduct="'.$objp->fk_product.
'" data-barcode="'.$objp->barcode.
'">';
757 print
'<td class="dispatch_batch_number"></td>';
758 if ($is_sell_by_enabled) {
759 print
'<td class="dispatch_dlc"></td>';
761 if ($is_eat_by_enabled) {
762 print
'<td class="dispatch_dluo"></td>';
766 print
'<td id="product_'.$i.
'" data-idproduct="'.$objp->fk_product.
'" data-barcode="'.$objp->barcode.
'">';
769 print
'<td class="dispatch_batch_number">';
770 print
'<span class="opacitymedium small">'.$langs->trans(
"ProductDoesNotUseBatchSerial").
'</span>';
772 if ($is_sell_by_enabled) {
773 print
'<td class="dispatch_dlc"></td>';
775 if ($is_eat_by_enabled) {
776 print
'<td class="dispatch_dluo"></td>';
780 print
'<td colspan="4">';
786 $up_ht_disc = $objp->subprice;
787 if (!empty($objp->remise_percent) && !
getDolGlobalString(
'STOCK_EXCLUDE_DISCOUNT_FOR_PMP')) {
788 $up_ht_disc =
price2num($up_ht_disc * (100 - $objp->remise_percent) / 100,
'MU');
792 print
'<td class="right">'.$objp->qty.
'</td>';
795 print
'<td class="right">'.$alreadydispatched.
'</td>';
797 print
'<td class="right">';
803 $sql =
"SELECT ed.rowid, ed.fk_parent";
804 $sql .=
", cd.fk_product";
805 $sql .=
", ".$db->ifsql(
'eb.rowid IS NULL',
'ed.qty',
'eb.qty').
" as qty";
806 $sql .=
", ".$db->ifsql(
'eb.rowid IS NULL OR eb.fk_warehouse IS NULL',
'ed.fk_entrepot',
'eb.fk_warehouse').
" as fk_warehouse";
807 $sql .=
", eb.batch, eb.eatby, eb.sellby";
808 $sql .=
" FROM ".$db->prefix().
"expeditiondet as ed";
809 $sql .=
" LEFT JOIN ".$db->prefix().
"expeditiondet_batch as eb on ed.rowid = eb.fk_expeditiondet";
810 $sql .=
" INNER JOIN ".$db->prefix().
"commandedet as cd on ed.fk_elementdet = cd.rowid";
811 $sql .=
" WHERE ed.fk_elementdet = ".(int) $objp->rowid;
812 $sql .=
" AND ed.fk_expedition = ".(int)
$object->id;
813 $sql .=
" ORDER BY ed.rowid, ed.fk_elementdet";
815 $resultsql = $db->query($sql);
818 $numd = $db->num_rows($resultsql);
819 while ($obj_exp = $db->fetch_object($resultsql)) {
820 $suffix =
"_" . $j .
"_" . $i;
822 $productChildrenNb = 0;
823 $expedition_line_child_list = array();
826 $productChildrenNb = $tmpproduct->hasFatherOrChild(1);
827 if ($productChildrenNb > 0) {
828 $line_id_list = array();
832 $result = $expeditionLine->findAllChild($obj_exp->rowid, $line_id_list, 1);
835 foreach ($line_id_list as $line_id_arr) {
836 foreach ($line_id_arr as $line_obj) {
837 $child_product_id = (int) $line_obj->fk_product;
838 if (empty($conf->cache[
'product'][$child_product_id])) {
839 $child_product =
new Product($db);
840 $child_product->fetch($child_product_id);
841 $conf->cache[
'product'][$child_product_id] = $child_product;
843 $child_product = $conf->cache[
'product'][$child_product_id];
847 $batch_list = array();
848 if ($is_mod_batch_enabled && $child_product->hasbatch()) {
850 $sql_line_batch_search =
"SELECT eb.rowid, eb.qty, eb.batch, eb.sellby, eb.eatby";
851 $sql_line_batch_search .=
" FROM ".$db->prefix().
"expeditiondet_batch as eb";
852 $sql_line_batch_search .=
" WHERE eb.fk_expeditiondet = ".((int) $line_obj->rowid);
853 $res_line_batch_search = $db->query($sql_line_batch_search);
854 if ($res_line_batch_search) {
855 while ($obj_batch = $db->fetch_object($res_line_batch_search)) {
857 if ($obj_batch->batch !=
'') {
858 $line_obj->batch = $obj_batch->batch;
862 $batch_list[] = $obj_batch;
864 $db->free($res_line_batch_search);
868 if (empty($batch_list)) {
869 $batch_sort_field_arr = array();
870 $batch_sort_order_arr = array();
871 if ($is_sell_by_enabled) {
872 $batch_sort_field_arr[] =
'pl.sellby';
873 $batch_sort_order_arr[] =
'ASC';
875 if ($is_eat_by_enabled) {
876 $batch_sort_field_arr[] =
'pl.eatby';
877 $batch_sort_order_arr[] =
'ASC';
879 $batch_sort_field_arr[] =
'pb.qty';
880 $batch_sort_order_arr[] =
'ASC';
881 $batch_sort_field_arr[] =
'pl.rowid';
882 $batch_sort_order_arr[] =
'ASC';
884 $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));
885 if (is_array($product_batch_result)) {
886 foreach ($product_batch_result as $batch_current) {
887 $batch_current->eatby =
dol_print_date($batch_current->eatby,
'day');
888 $batch_current->sellby =
dol_print_date($batch_current->sellby,
'day');
889 $batch_list[] = $batch_current;
894 $line_obj->batch_list = $batch_list;
897 $line_obj->iskit = 0;
898 if ($child_product->stockable_product == Product::ENABLED_STOCK) {
899 $can_manage_stock = 1;
901 $can_manage_stock = 0;
903 $line_obj->incdec = $can_manage_stock;
904 $sql_child =
"SELECT ";
905 $sql_child .=
" SUM(".$db->ifsql(
"pa.rowid IS NOT NULL",
"1",
"0").
") as iskit";
906 $sql_child .=
", ".$db->ifsql(
"pai.incdec IS NULL",
"1",
"pai.incdec").
" as incdec";
907 $sql_child .=
" FROM ".$db->prefix().
"expeditiondet as ed";
908 $sql_child .=
" LEFT JOIN ".$db->prefix().
"expeditiondet as edp ON edp.rowid = ".((int) $line_obj->fk_parent);
909 $sql_child .=
" LEFT JOIN ".$db->prefix().
"product_association as pa ON pa.fk_product_pere = ".((int) $child_product_id);
910 $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);
911 $sql_child .=
" WHERE ed.rowid = ".((int) $line_obj->rowid);
912 $sql_child .=
" GROUP BY pa.rowid, pai.incdec";
913 $resql_child = $db->query($sql_child);
915 if ($child_obj = $db->fetch_object($resql_child)) {
916 if (!
getDolGlobalInt(
'PRODUIT_SOUSPRODUITS_ALSO_ENABLE_PARENT_STOCK_MOVE')) $line_obj->iskit = (
int) $child_obj->iskit;
917 if ($can_manage_stock) {
918 $line_obj->incdec = (int) $child_obj->incdec;
921 $db->free($resql_child);
923 $line_obj->html_label = str_repeat(
" ", $child_level) .
"→" . $child_product->getNomUrl(1);
924 $expedition_line_child_list[] = $line_obj;
931 if (empty($expedition_line_child_list)) {
934 if ($objp->stockable_product == Product::ENABLED_STOCK) {
935 $obj_exp->incdec = 1;
937 $obj_exp->incdec = 0;
939 $expedition_line_child_list[] = $obj_exp;
942 $child_suffix = $suffix;
943 foreach ($expedition_line_child_list as $objd) {
944 $child_line_id = $objd->rowid;
946 $can_update_stock = empty($objd->iskit) && !empty($objd->incdec);
947 $suffix = $child_line_id.$child_suffix;
950 $dispatch_line_batch_current =
null;
951 if (!empty($objd->batch_list)) {
952 $dispatch_line_batch_count = count($objd->batch_list);
954 if ($dispatch_line_batch_count == 1) {
955 $dispatch_line_batch_current = current($objd->batch_list);
958 if (is_object($dispatch_line_batch_current)) {
959 $objd->batch = $dispatch_line_batch_current->batch;
960 $objd->eatby = $dispatch_line_batch_current->eatby;
961 $objd->sellby = $dispatch_line_batch_current->sellby;
964 if ($is_mod_batch_enabled
967 || (is_null($objd->batch) && $tmpproduct->status_batch > 0)
968 || !empty($objd->batch_list)
976 'is_information_row' =>
true,
981 $reshook = $hookmanager->executeHooks(
982 'printFieldListValue',
990 print $hookmanager->resPrint;
994 print
'<!-- line for batch ' . $numline .
' -->';
995 print
'<tr class="oddeven autoresettr" name="' . $type .
'-' . $suffix .
'" data-remove="clear">';
997 print
'<input id="fk_commandedet' . $suffix .
'" name="fk_commandedet' . $suffix .
'" type="hidden" value="' . $objp->rowid .
'">';
998 print
'<input id="idline' . $suffix .
'" name="idline' . $suffix .
'" type="hidden" value="' . $objd->rowid .
'">';
999 print
'<input id="fk_parent' . $suffix .
'" name="fk_parent' . $suffix .
'" type="hidden" value="' . $objd->fk_parent .
'">';
1000 print
'<input name="productbatch' . $suffix .
'" type="hidden" value="' . $objd->fk_product .
'">';
1002 print
'<!-- This is a U.P. (may include discount or not depending on STOCK_EXCLUDE_DISCOUNT_FOR_PMP. will be used for PMP calculation) -->';
1003 print
'<input class="maxwidth75" name="pu' . $suffix .
'" type="hidden" value="' .
price2num($up_ht_disc,
'MU') .
'">';
1004 if (!empty($objd->html_label)) {
1005 print $objd->html_label;
1010 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.
'">';
1011 print $formproduct->selectLotDataList(
'lot_number'.$suffix, 0, $objd->fk_product,
GETPOST(
"entrepot".$suffix) ?
GETPOST(
"entrepot".$suffix) : $objd->fk_warehouse, array());
1014 if ($is_sell_by_enabled) {
1015 print
'<td class="nowraponall">';
1017 print $form->selectDate($dlcdatesuffix,
'dlc'.$suffix, 0, 0, 1,
'');
1020 if ($is_eat_by_enabled) {
1021 print
'<td class="nowraponall">';
1023 print $form->selectDate($dluodatesuffix,
'dluo'.$suffix, 0, 0, 1,
'');
1026 print
'<td colspan="2"> </td>';
1030 $colspan = $is_sell_by_enabled ? $colspan : --$colspan;
1031 $colspan = $is_eat_by_enabled ? $colspan : --$colspan;
1034 $parameters = array(
1036 'is_information_row' =>
true,
1038 'suffix' => $suffix,
1041 $reshook = $hookmanager->executeHooks(
1042 'printFieldListValue',
1050 print $hookmanager->resPrint;
1054 print
'<!-- line no batch '.$numline.
' -->';
1055 print
'<tr class="oddeven autoresettr" name="'.$type.
'-'.$suffix.
'" data-remove="clear">';
1056 print
'<td colspan="'.$colspan.
'">';
1057 print
'<input id="fk_commandedet'.$suffix.
'" name="fk_commandedet'.$suffix.
'" type="hidden" value="'.$objp->rowid.
'">';
1058 print
'<input id="idline'.$suffix.
'" name="idline'.$suffix.
'" type="hidden" value="'.$objd->rowid.
'">';
1059 print
'<input id="fk_parent'.$suffix.
'" name="fk_parent'.$suffix.
'" type="hidden" value="'.$objd->fk_parent.
'">';
1060 print
'<input name="product'.$suffix.
'" type="hidden" value="'.$objd->fk_product.
'">';
1061 print
'<!-- This is a up (may include discount or not depending on STOCK_EXCLUDE_DISCOUNT_FOR_PMP. will be used for PMP calculation) -->';
1062 print
'<input class="maxwidth75" name="pu'.$suffix.
'" type="hidden" value="'.
price2num($up_ht_disc,
'MU').
'">';
1063 if (!empty($objd->html_label)) {
1064 print $objd->html_label;
1069 print
'<td class="right nowraponall">';
1070 $suggestedvalue = (GETPOSTISSET(
'qty'.$suffix) ?
GETPOSTFLOAT(
'qty'.$suffix) : $objd->qty);
1072 if ($can_update_stock) {
1073 print
'<a href="" id="reset'.$suffix.
'" class="resetline">'.
img_picto($langs->trans(
"Reset"),
'eraser',
'class="pictofixedwidth opacitymedium"').
'</a>';
1074 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.
'">';
1076 print
'<input type="hidden" id="qty'.$suffix.
'" name="qty'.$suffix.
'" value="">';
1080 if ($can_update_stock) {
1081 print
img_picto($langs->trans(
'AddStockLocationLine'),
'split',
'class="splitbutton" onClick="addDispatchLine('.$i.
', \''.$type.
'-'.$child_line_id.
'\')
"');
1086 print '<td class="right
">';
1087 if ($can_update_stock) {
1088 if (count($listwarehouses) > 1) {
1089 print $formproduct->selectWarehouses(GETPOST("entrepot
".$suffix) ? GETPOST("entrepot
".$suffix) : $objd->fk_warehouse, "entrepot
".$suffix, '', 1, 0, $objd->fk_product, '', 1, 0, array(), 'csswarehouse'.$suffix);
1090 } elseif (count($listwarehouses) == 1) {
1091 print $formproduct->selectWarehouses(GETPOST("entrepot
".$suffix) ? GETPOST("entrepot
".$suffix) : $objd->fk_warehouse, "entrepot
".$suffix, '', 0, 0, $objd->fk_product, '', 1, 0, array(), 'csswarehouse'.$suffix);
1093 $langs->load("errors
");
1094 print $langs->trans("ErrorNoWarehouseDefined
");
1097 // on force l'entrepot pour passer le test d'ajout de ligne dans expedition.class.php
1098 print '<input id="entrepot
'.$suffix.'" name="entrepot
'.$suffix.'" type="hidden
" value="'.$objd->fk_warehouse.'">';
1099 print img_warning().' '.$langs->trans('StockDisabled');
1103 // Enable hooks to append additional columns
1104 $parameters = array(
1105 'is_information_row' => false, // this is a dispatch form row
1107 'suffix' => $suffix,
1111 $reshook = $hookmanager->executeHooks(
1112 'printFieldListValue',
1118 setEventMessages($hookmanager->error, $hookmanager->errors, 'errors');
1120 print $hookmanager->resPrint;
1129 //$suffix = "_
".$j."_
".$i;
1131 $errorMsg = 'Shipment dispatch SQL error : '.$db->lasterror();
1132 setEventMessage($errorMsg, 'errors');
1133 dol_syslog($errorMsg, LOG_ERR);
1138 if ($is_mod_batch_enabled && !empty($objp->tobatch)) {
1141 // Enable hooks to append additional columns
1142 $parameters = array(
1143 // allows hook to distinguish between the rows with information and the rows with dispatch form input
1144 'is_information_row' => true,
1146 'suffix' => $suffix,
1149 $reshook = $hookmanager->executeHooks(
1150 'printFieldListValue',
1156 setEventMessages($hookmanager->error, $hookmanager->errors, 'errors');
1158 print $hookmanager->resPrint;
1162 print '<!-- line for batch '.$numline.' (not dispatched line yet for this order line) -->';
1163 print '<tr class="oddeven autoresettr
" name="'.$type.$suffix.'" data-remove="clear
">';
1165 print '<input id="fk_commandedet
'.$suffix.'" name="fk_commandedet
'.$suffix.'" type="hidden
" value="'.$objp->rowid.'">';
1166 print '<input id="idline
'.$suffix.'" name="idline
'.$suffix.'" type="hidden
" value="-1
">';
1167 print '<input name="productbatch
'.$suffix.'" type="hidden
" value="'.$objp->fk_product.'">';
1169 print '<!-- This is a up (may include discount or not depending on STOCK_EXCLUDE_DISCOUNT_FOR_PMP. will be used for PMP calculation) -->';
1170 print '<input class="maxwidth75
" name="pu
'.$suffix.'" type="hidden
" value="'.price2num($up_ht_disc, 'MU
').'">';
1174 print '<input type="text
" class="inputlotnumber quatrevingtquinzepercent
" id="lot_number
'.$suffix.'" name="lot_number
'.$suffix.'" value="'.GETPOST('lot_number
'.$suffix).'">';
1176 if ($is_sell_by_enabled) {
1177 print '<td class="nowraponall
">';
1178 $dlcdatesuffix = dol_mktime(0, 0, 0, GETPOSTINT('dlc'.$suffix.'month'), GETPOSTINT('dlc'.$suffix.'day'), GETPOSTINT('dlc'.$suffix.'year'));
1179 print $form->selectDate($dlcdatesuffix, 'dlc'.$suffix, 0, 0, 1, '');
1182 if ($is_eat_by_enabled) {
1183 print '<td class="nowraponall
">';
1184 $dluodatesuffix = dol_mktime(0, 0, 0, GETPOSTINT('dluo'.$suffix.'month'), GETPOSTINT('dluo'.$suffix.'day'), GETPOSTINT('dluo'.$suffix.'year'));
1185 print $form->selectDate($dluodatesuffix, 'dluo'.$suffix, 0, 0, 1, '');
1188 print '<td colspan="2
"> </td>'; // Supplier ref + Qty ordered + qty already dispatched
1192 $colspan = $is_sell_by_enabled ? $colspan : --$colspan;
1193 $colspan = $is_eat_by_enabled ? $colspan : --$colspan;
1195 // Enable hooks to append additional columns
1196 $parameters = array(
1197 // allows hook to distinguish between the rows with information and the rows with dispatch form input
1198 'is_information_row' => true,
1200 'suffix' => $suffix,
1203 $reshook = $hookmanager->executeHooks(
1204 'printFieldListValue',
1210 setEventMessages($hookmanager->error, $hookmanager->errors, 'errors');
1212 print $hookmanager->resPrint;
1216 print '<!-- line no batch '.$numline.' (not dispatched line yet for this order line) -->';
1217 print '<tr class="oddeven autoresettr
" name="'.$type.$suffix.'" data-remove="clear
">';
1218 print '<td colspan="'.$colspan.'">';
1219 print '<input id="fk_commandedet
'.$suffix.'" name="fk_commandedet
'.$suffix.'" type="hidden
" value="'.$objp->rowid.'">';
1220 print '<input id="idline
'.$suffix.'" name="idline
'.$suffix.'" type="hidden
" value="-1
">';
1221 print '<input name="product
'.$suffix.'" type="hidden
" value="'.$objp->fk_product.'">';
1223 print '<!-- This is a up (may include discount or not depending on STOCK_EXCLUDE_DISCOUNT_FOR_PMP. will be used for PMP calculation) -->';
1224 print '<input class="maxwidth75
" name="pu
'.$suffix.'" type="hidden
" value="'.price2num($up_ht_disc, 'MU
').'">';
1228 print '<td class="right
">';
1229 print '<a href="" id="reset
'.$suffix.'" class="resetline
">'.img_picto($langs->trans("Reset
"), 'eraser', 'class="pictofixedwidth opacitymedium
"').'</a>';
1230 $amounttosuggest = (GETPOSTISSET('qty'.$suffix) ? GETPOSTINT('qty'.$suffix) : (!getDolGlobalString('SUPPLIER_ORDER_DISPATCH_FORCE_QTY_INPUT_TO_ZERO') ? $remaintodispatch : 0));
1231 if (count($products_dispatched)) {
1232 // There is already existing lines into llx_expeditiondet, this means a plan for the shipment has already been started.
1233 // In such a case, we do not suggest new values, we suggest the value known.
1234 $amounttosuggest = (GETPOSTISSET('qty'.$suffix) ? GETPOSTINT('qty'.$suffix) : (isset($products_dispatched[$objp->rowid]) ? $products_dispatched[$objp->rowid] : ''));
1236 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.'">';
1239 if ($is_mod_batch_enabled && $objp->tobatch > 0) {
1241 print img_picto($langs->trans('AddStockLocationLine'), 'split', 'class="splitbutton
" onClick="addDispatchLine(
'.$i.', \
''.$type.
'\')
"');
1244 print img_picto($langs->trans('AddStockLocationLine'), 'split', 'class="splitbutton
" onClick="addDispatchLine(
'.$i.', \
''.$type.
'\')
"');
1250 print '<td class="right
">';
1251 if (count($listwarehouses) > 1) {
1252 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);
1253 } elseif (count($listwarehouses) == 1) {
1254 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);
1256 $langs->load("errors
");
1257 print $langs->trans("ErrorNoWarehouseDefined
");
1261 // Enable hooks to append additional columns
1262 $parameters = array(
1263 'is_information_row' => false, // this is a dispatch form row
1265 'suffix' => $suffix,
1268 $reshook = $hookmanager->executeHooks(
1269 'printFieldListValue',
1275 setEventMessages($hookmanager->error, $hookmanager->errors, 'errors');
1277 print $hookmanager->resPrint;
1286 // reload batch select and warehouse select on change (Ajax)
1287 $out_js_line_list = array();
1288 $out_js_line = 'function updateselectbatchbywarehouse() {';
1289 $out_js_line .= ' jQuery(document).on("change
", "select[
name*=\
"entrepot\"]",
function() {
';
1290 $out_js_line .= ' var selectwarehouse = jQuery(
this);
';
1291 $out_js_line .= ' var selectbatch_name = selectwarehouse.attr(
"name").replace(
"entrepot",
"lot_number");
';
1292 $out_js_line .= ' var selectbatch = jQuery(
"datalist[id*=\""+selectbatch_name+
"\"]");
';
1293 $out_js_line .= ' var selectedbatch = selectbatch.val();
';
1294 $out_js_line .= ' var product_element_name = selectwarehouse.attr(
"name").replace(
"entrepot",
"productbatch");
';
1295 $out_js_line .= ' jQuery.ajax({
';
1296 $out_js_line .= ' type:
"POST",
';
1297 $out_js_line .= ' url:
"'.dol_escape_js(dol_buildpath('/expedition/ajax/interface.php', 1)).'",
';
1298 $out_js_line .= ' data: {
';
1299 $out_js_line .= ' action:
"updateselectbatchbywarehouse",
';
1300 $out_js_line .= ' warehouse_id: jQuery(
this).val(),
';
1301 $out_js_line .= ' token:
"'.currentToken().'",
';
1302 $out_js_line .= ' product_id: jQuery(
"input[name=\""+product_element_name+
"\"]").val()
';
1303 $out_js_line .= ' }
';
1304 $out_js_line .= ' }).done(
function(data) {
';
1305 $out_js_line .= ' selectbatch.empty();
';
1306 $out_js_line .= ' if (typeof data ==
"object") {
';
1307 $out_js_line .= ' console.log(
"data is already type object, no need to parse it");
';
1308 $out_js_line .= ' }
else {
';
1309 $out_js_line .= ' console.log(
"data is type "+(typeof data));
';
1310 $out_js_line .= ' data = JSON.parse(data);
';
1311 $out_js_line .= ' }
';
1312 $out_js_line .= ' selectbatch.append(jQuery(
"<option>", {
';
1313 $out_js_line .= ' value:
"",
';
1314 $out_js_line .= ' }));
';
1315 $out_js_line .= ' jQuery.each(data,
function(key, objBatch) {
';
1316 $out_js_line .= ' var dataEatByDate = objBatch.eatbydate;
';
1317 $out_js_line .= ' var dataSellByDate = objBatch.sellbydate;
';
1318 $out_js_line .= ' var optionLabel = key+
" (";
';
1319 $out_js_line .= ' if (selectwarehouse.val() == -1) {
';
1320 $out_js_line .= ' optionLabel +=
"'.dol_escape_js($langs->trans('TotalStock')).': "+objBatch.qty;
';
1321 $out_js_line .= ' }
else {
';
1322 $out_js_line .= ' optionLabel +=
"'.dol_escape_js($langs->trans('Stock')).': "+objBatch.qty;
';
1323 $out_js_line .= ' }
';
1324 $out_js_line .= ' if (dataEatByDate !=
"") {
';
1325 $out_js_line .= ' optionLabel +=
" - '.dol_escape_js($langs->trans('EatByDate')).': "+dataEatByDate;
';
1326 $out_js_line .= ' }
';
1327 $out_js_line .= ' if (dataSellByDate !=
"") {
';
1328 $out_js_line .= ' optionLabel +=
" - '.dol_escape_js($langs->trans('SellByDate')).': "+dataSellByDate;
';
1329 $out_js_line .= ' }
';
1330 $out_js_line .= ' optionLabel +=
")";
';
1331 $out_js_line .= ' var option =
"<option data-eatbydate=\""+dataEatByDate+
"\" data-sellbydate=\""+dataSellByDate+
"\" value=\""+key+
"\"";
';
1332 $out_js_line .= ' if (key === selectedbatch) {
';
1333 $out_js_line .= ' option +=
" selected";
';
1334 $out_js_line .= ' }
';
1335 $out_js_line .= ' option +=
">"+optionLabel+
"</option>";
';
1336 $out_js_line .= ' selectbatch.append(option);
';
1337 $out_js_line .= ' });
';
1338 $out_js_line .= ' });
';
1339 $out_js_line .= ' });
';
1340 $out_js_line .= '}
';
1342 $out_js_line .= 'function updateselectwarehousebybatch() {
';
1343 $out_js_line .= ' jQuery(document).on(
"change",
"input[name*=lot_number]",
function() {
';
1344 $out_js_line .= ' var selectbatch = jQuery(
this);
';
1345 $out_js_line .= ' var selectwarehouse_name = selectbatch.attr(
"name").replace(
"lot_number",
"entrepot");
';
1346 $out_js_line .= ' var selectwarehouse = jQuery(
"select[name*=\""+selectwarehouse_name+
"\"]");
';
1347 $out_js_line .= ' var selectedwarehouse = selectwarehouse.val();
';
1348 $out_js_line .= ' var inputbatchdlc_name = selectbatch.attr(
"name").replace(
"lot_number",
"dlc");
';
1349 $out_js_line .= ' var inputbatchdlc = jQuery(
"input[name*=\""+inputbatchdlc_name+
"\"]");
';
1350 $out_js_line .= ' var inputbatchdluo_name = selectbatch.attr(
"name").replace(
"lot_number",
"dluo");
';
1351 $out_js_line .= ' var inputbatchdluo = jQuery(
"input[name*=\""+inputbatchdluo_name+
"\"]");
';
1352 $out_js_line .= ' var datalistselectedbatch = jQuery(
"#"+selectbatch.attr(
"name")+
" option[value=\""+selectbatch.val()+
"\"]");
';
1353 $out_js_line .= ' var selectedbatch_dlc = datalistselectedbatch.data(
"sellbydate");
';
1354 $out_js_line .= ' var selectedbatch_dluo = datalistselectedbatch.data(
"eatbydate");
';
1355 $out_js_line .= ' if (typeof selectedbatch_dlc ===
"undefined") {
';
1356 $out_js_line .= ' selectedbatch_dlc =
"";
';
1357 $out_js_line .= ' }
';
1358 $out_js_line .= ' if (typeof selectedbatch_dluo ===
"undefined") {
';
1359 $out_js_line .= ' selectedbatch_dluo =
"";
';
1360 $out_js_line .= ' }
';
1361 $out_js_line .= ' inputbatchdlc.val(selectedbatch_dlc).trigger(
"change");
';
1362 $out_js_line .= ' inputbatchdluo.val(selectedbatch_dluo).trigger(
"change");
';
1363 $out_js_line .= ' if (selectedwarehouse != -1) {
';
1364 $out_js_line .= ' return;
';
1365 $out_js_line .= ' }
';
1366 $out_js_line .= ' var product_element_name = selectbatch.attr(
"name").replace(
"lot_number",
"productbatch");
';
1367 $out_js_line .= ' jQuery.ajax({
';
1368 $out_js_line .= ' type:
"POST",
';
1369 $out_js_line .= ' url:
"'.dol_escape_js(dol_buildpath('/expedition/ajax/interface.php', 1)).'",
';
1370 $out_js_line .= ' data: {
';
1371 $out_js_line .= ' action:
"updateselectwarehousebybatch",
';
1372 $out_js_line .= ' batch: jQuery(
this).val(),
';
1373 $out_js_line .= ' token:
"'.currentToken().'",
';
1374 $out_js_line .= ' product_id: jQuery(
"input[name=\""+product_element_name+
"\"]").val()
';
1375 $out_js_line .= ' }
';
1376 $out_js_line .= ' }).done(
function(data) {
';
1377 $out_js_line .= ' if (typeof data ==
"object") {
';
1378 $out_js_line .= ' console.log(
"data is already type object, no need to parse it");
';
1379 $out_js_line .= ' }
else {
';
1380 $out_js_line .= ' console.log(
"data is type "+(typeof data));
';
1381 $out_js_line .= ' data = JSON.parse(data);
';
1382 $out_js_line .= ' }
';
1383 $out_js_line .= ' if (data != 0) {
';
1384 $out_js_line .= ' selectwarehouse.val(data).change();
';
1385 $out_js_line .= ' }
';
1386 $out_js_line .= ' });
';
1387 $out_js_line .= ' });
';
1388 $out_js_line .= '}
';
1389 $out_js_line_list[] = $out_js_line;
1391 $out_js = '<script
type=
"text/javascript" language=
"javascript">
';
1392 $out_js .= 'jQuery(document).ready(
function() {
';
1393 // when a warehouse is selected, only the lot/serial numbers that are available in it are offered
1394 $out_js .= 'updateselectbatchbywarehouse();
';
1395 // when a lot/serial number is selected and it is only available in one warehouse, the warehouse is automatically selected
1396 $out_js .= 'updateselectwarehousebybatch();
';
1397 $out_js .= implode('', $out_js_line_list);
1399 $out_js .= '</script>
';
1404 dol_print_error($db);
1411 //$checkboxlabel = $langs->trans("CloseReceivedSupplierOrdersAutomatically", $langs->transnoentitiesnoconv('StatusOrderReceivedAll
'));
1413 print '<div
class=
"center">
';
1414 $parameters = array();
1415 $reshook = $hookmanager->executeHooks('addMoreActionsButtons
', $parameters, $object, $action); // Note that $action and $object may have been
1417 if (empty($reshook)) {
1418 /*if (empty($conf->reception->enabled)) {
1419 print $langs->trans("Comment").' :
';
1420 print '<input
type=
"text" class=
"minwidth400" maxlength=
"128" name=
"comment" value=
"';
1421 print GETPOSTISSET("comment
") ? GETPOST("comment
") : $langs->trans("DispatchSupplierOrder
", $object->ref);
1422 // print ' / '.$object->ref_supplier; // Not yet available
1423 print '" class=
"flat"><br>
';
1425 print '<input
type=
"checkbox" checked=
"checked" name=
"closeopenorder">
'.$checkboxlabel;
1428 $dispatchBt = empty($conf->reception->enabled) ? $langs->trans("Receive") : $langs->trans("CreateReception");
1433 print '<input
type=
"submit" id=
"submitform" class=
"button" name=
"dispatch" value=
"'.$langs->trans("Save
").'"';
1435 if (!$usercancreate) {
1438 if (count($listwarehouses) <= 0) {
1450 // Message if nothing to dispatch
1453 if (!getDolGlobalString('SUPPLIER_ORDER_DISABLE_STOCK_DISPATCH_WHEN_TOTAL_REACHED
')) {
1454 print '<div
class=
"opacitymedium">
'.$langs->trans("NoPredefinedProductToDispatch").'</div>
'; // No predefined line at all
1456 print '<div
class=
"opacitymedium">
'.$langs->trans("NoMorePredefinedProductToDispatch").'</div>
'; // No predefined line that remain to be dispatched.
1463 print dol_get_fiche_end();
1465 // Popup for mass barcode scanning
1466 if ($action == 'updatebyscaning
') {
1467 if ($permissiontoadd) {
1468 // Output the javascript to manage the scanner tool.
1472 var duplicatedbatchcode = [];
1478 function barcodescannerjs() {
1479 console.log(
"We catch inputs in scanner box");
1480 jQuery(
"#scantoolmessage").text();
1482 var selectaddorreplace = $(
"select[name=selectaddorreplace]").val();
1483 var barcodemode = $(
"input[name=barcodemode]:checked").val();
1484 var barcodeproductqty = $(
"input[name=barcodeproductqty]").val();
1485 var warehousetouse = $(
"select[name=warehousenew]").val();
1486 var textarea = $(
"textarea[name=barcodelist]").val();
1487 var textarray = textarea.split(/[\s,;]+/);
1488 var tabproduct = [];
1489 duplicatedbatchcode = [];
1495 textarray = textarray.filter(
function(value) {
1498 if (textarray.some((element) => element !=
"")) {
1499 $(
".qtydispatchinput").each(
function() {
1500 id = $(
this).attr(\
'id\');
1501 idarray = id.split(\'_\');
1502 idproduct = idarray[2];
1503 id = idarray[1] + \'_\' + idarray[2];
1504 console.log("Analyze the line "+id+" in inventory, barcodemode="+barcodemode);
1505 warehouse = $("#entrepot_"+id).val();
1506 console.log(warehouse);
1507 productbarcode = $("#product_"+idproduct).attr(\'data-barcode\');
1508 console.log(productbarcode);
1509 productbatchcode = $("#lot_number_"+id).val();
1510 if (productbatchcode == undefined) {
1511 productbatchcode = "";
1513 console.log(productbatchcode);
1515 if (barcodemode != "barcodeforproduct") {
1516 tabproduct.forEach(product=>{
1517 console.log("product.Batch="+product.Batch+" productbatchcode="+productbatchcode);
1518 if (product.Batch != "" && product.Batch == productbatchcode) {
1519 console.log("duplicate batch code found for batch code "+productbatchcode);
1520 duplicatedbatchcode.push(productbatchcode);
1524 productinput = $("#qty_"+id).val();
1525 if (productinput == "") {
1528 tabproduct.push({\'Id\':id,\'Warehouse\':warehouse,\'Barcode\':productbarcode,\'Batch\':productbatchcode,\'Qty\':productinput,\'fetched\':false});
1530 console.log("Loop on each record entered in the textarea");
1532 textarray.forEach(function(element,index) {
1533 console.log("Process record element="+element+" id="+id);
1534 var verify_batch = false;
1535 var verify_barcode = false;
1536 switch(barcodemode) {
1537 case "barcodeforautodetect":
1538 verify_barcode = barcodeserialforproduct(tabproduct,index,element,barcodeproductqty,warehousetouse,selectaddorreplace,"barcode",true);
1539 verify_batch = barcodeserialforproduct(tabproduct,index,element,barcodeproductqty,warehousetouse,selectaddorreplace,"lotserial",true);
1541 case "barcodeforproduct":
1542 verify_barcode = barcodeserialforproduct(tabproduct,index,element,barcodeproductqty,warehousetouse,selectaddorreplace,"barcode");
1544 case "barcodeforlotserial":
1545 verify_batch = barcodeserialforproduct(tabproduct,index,element,barcodeproductqty,warehousetouse,selectaddorreplace,"lotserial");
1548 alert(\''.
dol_escape_js($langs->trans(
"ErrorWrongBarcodemode")).
' "\'+barcodemode+\'"\');
1549 throw \''.
dol_escape_js($langs->trans(
'ErrorWrongBarcodemode')).
' "\'+barcodemode+\'"\';
1552 if (verify_batch == false && verify_barcode == false) { /* If the 2 flags are false, not found error */
1553 errortab2.push(element);
1554 } else if (verify_batch == true && verify_barcode == true) { /* If the 2 flags are true, error: we don t know which one to take */
1555 errortab3.push(element);
1556 } else if (verify_batch == true) {
1557 console.log("element="+element);
1558 console.log(duplicatedbatchcode);
1559 if (duplicatedbatchcode.includes(element)) {
1560 errortab1.push(element);
1565 if (Object.keys(errortab1).length < 1 && Object.keys(errortab2).length < 1 && Object.keys(errortab3).length < 1) {
1566 tabproduct.forEach(product => {
1567 if (product.Qty!=0) {
1568 if (product.hasOwnProperty("reelqty")) {
1569 idprod = $("td[data-idproduct=\'"+product.fk_product+"\']").attr("id");
1570 idproduct = idprod.split("_")[1];
1571 console.log("We create a new line for product_"+idproduct);
1572 if (product.Barcode != null) {
1573 modedispatch = "dispatch";
1575 modedispatch = "batch";
1577 addDispatchLine(idproduct,modedispatch);
1578 console.log($("tr[name^=\'"+modedispatch+"_\'][name$=\'_"+idproduct+"\']"));
1579 nbrTrs = $("tr[name^=\'"+modedispatch+"_\'][name$=\'_"+idproduct+"\']").length;
1581 $("#qty_"+(nbrTrs-1)+"_"+idproduct).val(product.Qty);
1582 $("#entrepot_"+(nbrTrs-1)+"_"+idproduct).val(product.Warehouse);
1584 if (modedispatch == "batch") {
1585 $("#lot_number_"+(nbrTrs-1)+"_"+idproduct).val(product.Batch);
1589 console.log("We change #qty_"+product.Id +" to match input in scanner box");
1590 $("#qty_"+product.Id).val(product.Qty);
1594 jQuery("#scantoolmessage").text("'.
dol_escape_js($langs->transnoentities(
"QtyWasAddedToTheScannedBarcode")).
'\n");
1595 /* document.forms["formrecord"].submit(); */
1597 let stringerror = "";
1598 if (Object.keys(errortab1).length > 0) {
1599 stringerror += "<br>'.
dol_escape_js($langs->transnoentities(
'ErrorSameBatchNumber')).
': ";
1600 errortab1.forEach(element => {
1601 stringerror += (element + ", ")
1603 stringerror = stringerror.slice(0, -2); /* Remove last ", " */
1605 if (Object.keys(errortab2).length > 0) {
1606 stringerror += "<br>'.
dol_escape_js($langs->transnoentities(
'ErrorCantFindCodeInInventory')).
': ";
1607 errortab2.forEach(element => {
1608 stringerror += (element + ", ")
1610 stringerror = stringerror.slice(0, -2); /* Remove last ", " */
1612 if (Object.keys(errortab3).length > 0) {
1613 stringerror += "<br>'.
dol_escape_js($langs->transnoentities(
'ErrorCodeScannedIsBothProductAndSerial')).
': ";
1614 errortab3.forEach(element => {
1615 stringerror += (element + ", ")
1617 stringerror = stringerror.slice(0, -2); /* Remove last ", " */
1619 if (Object.keys(errortab4).length > 0) {
1620 stringerror += "<br>'.
dol_escape_js($langs->transnoentities(
'ErrorBarcodeNotFoundForProductWarehouse')).
': ";
1621 errortab4.forEach(element => {
1622 stringerror += (element + ", ")
1624 stringerror = stringerror.slice(0, -2); /* Remove last ", " */
1627 jQuery("#scantoolmessage").html(\''.
dol_escape_js($langs->transnoentities(
"ErrorOnElementsInventory")).
'\' + stringerror);
1635 function barcodeserialforproduct(tabproduct,index,element,barcodeproductqty,warehousetouse,selectaddorreplace,mode,autodetect=
false) {
1636 BarcodeIsInProduct=0;
1639 tabproduct.forEach(product => {
1640 $.ajax({ url: \
''.DOL_URL_ROOT.
'/expedition/ajax/searchfrombarcode.php\',
1641 data: { "token":"'.
newToken().
'", "action":"existbarcode","fk_entrepot": warehousetouse, "barcode":element, "mode":mode},
1644 success: function(response) {
1645 if (response.status == "success") {
1646 console.log(response.message);
1647 if (!newproductrow) {
1648 newproductrow = response.object;
1651 if (mode!="lotserial" && autodetect==false && !errortab4.includes(element)) {
1652 errortab4.push(element);
1653 console.error(response.message);
1657 error : function(output) {
1658 console.error("Error on barcodeserialforproduct function");
1661 console.log("Product "+(index+=1)+": "+element);
1662 if (mode == "barcode") {
1663 testonproduct = product.Barcode
1664 }else if (mode == "lotserial") {
1665 testonproduct = product.Batch
1667 testonwarehouse = product.Warehouse;
1668 if (testonproduct == element && testonwarehouse == warehousetouse) {
1669 if (selectaddorreplace == "add") {
1670 productqty = parseInt(product.Qty,10);
1671 product.Qty = productqty + parseInt(barcodeproductqty,10);
1672 }else if (selectaddorreplace == "replace") {
1673 if (product.fetched == false) {
1674 product.Qty = barcodeproductqty
1675 product.fetched=true
1677 productqty = parseInt(product.Qty,10);
1678 product.Qty = productqty + parseInt(barcodeproductqty,10);
1681 BarcodeIsInProduct+=1;
1684 if (BarcodeIsInProduct==0 && newproductrow!=0) {
1685 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});
1688 if (BarcodeIsInProduct > 0) {
1696 include DOL_DOCUMENT_ROOT.
'/core/class/html.formother.class.php';
1698 print $formother->getHTMLScannerForm(
"barcodescannerjs",
'all', 1);
1702 print
'<script type="text/javascript">
1703 $(document).ready(function () {
1704 $("select[name=fk_default_warehouse]").change(function() {
1705 console.log("warehouse is modified");
1706 var fk_default_warehouse = $("option:selected", this).val();
1707 $("select[name^=entrepot]").val(fk_default_warehouse).change();
1710 $("#autoreset").click(function() {
1711 console.log("we click on autoreset");
1712 $(".autoresettr").each(function() {
1713 id = $(this).attr("name");
1714 idtab = id.split("_");
1715 console.log("we process line "+id+" "+idtab);
1716 if ($(this).data("remove") == "clear") { /* data-remove=clear means that line qty must be cleared but line must not be removed */
1717 console.log("We clear the object to expected value")
1718 var idlinetab = idtab[0].split("-");
1720 if (idlinetab.length > 0) {
1721 idline = idlinetab[1];
1723 $("#qty"+idline+"_"+idtab[1]+"_"+idtab[2]).val("");
1725 qtyexpected = $("#qty_"+idtab[1]+"_"+idtab[2]).data("expected")
1726 console.log(qtyexpected);
1727 $("#qty_"+idtab[1]+"_"+idtab[2]).val(qtyexpected);
1728 qtydispatched = $("#qty_dispatched_0_"+idtab[2]).data("dispatched")
1729 $("#qty_dispatched_0_"+idtab[2]).val(qtydispatched);
1731 } else { /* data-remove=remove means that line must be removed */
1732 console.log("We remove the object")
1734 $("tr[name^=\'"+idtab[0]+"_\'][name$=\'_"+idtab[2]+"\']:last .splitbutton").show();
1740 $("#resetalltoexpected").click(function() {
1741 $(".qtydispatchinput").each(function() {
1742 console.log("We reset to expected "+$(this).attr("id")+" qty to dispatch");
1743 $(this).val($(this).data("expected"));
1748 $(".resetline").on("click", function(event) {
1749 event.preventDefault();
1750 id = $(this).attr("id");
1751 id = id.split("reset");
1752 console.log("Reset trigger for id = qty"+id[1]);
1753 $("#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.