inventory.php 40 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066
  1. <?php
  2. /* Copyright (C) 2019 Laurent Destailleur <eldy@users.sourceforge.net>
  3. *
  4. * This program is free software; you can redistribute it and/or modify
  5. * it under the terms of the GNU General Public License as published by
  6. * the Free Software Foundation; either version 3 of the License, or
  7. * (at your option) any later version.
  8. *
  9. * This program is distributed in the hope that it will be useful,
  10. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  11. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  12. * GNU General Public License for more details.
  13. *
  14. * You should have received a copy of the GNU General Public License
  15. * along with this program. If not, see <https://www.gnu.org/licenses/>.
  16. */
  17. /**
  18. * \file htdocs/product/inventory/inventory.php
  19. * \ingroup inventory
  20. * \brief Tabe to enter counting
  21. */
  22. require '../../main.inc.php';
  23. include_once DOL_DOCUMENT_ROOT.'/core/class/html.formcompany.class.php';
  24. include_once DOL_DOCUMENT_ROOT.'/product/class/html.formproduct.class.php';
  25. include_once DOL_DOCUMENT_ROOT.'/product/class/product.class.php';
  26. include_once DOL_DOCUMENT_ROOT.'/product/inventory/class/inventory.class.php';
  27. include_once DOL_DOCUMENT_ROOT.'/product/inventory/lib/inventory.lib.php';
  28. include_once DOL_DOCUMENT_ROOT.'/product/stock/class/mouvementstock.class.php';
  29. // Load translation files required by the page
  30. $langs->loadLangs(array("stocks", "other", "productbatch"));
  31. // Get parameters
  32. $id = GETPOST('id', 'int');
  33. $ref = GETPOST('ref', 'alpha');
  34. $action = GETPOST('action', 'aZ09');
  35. $confirm = GETPOST('confirm', 'alpha');
  36. $cancel = GETPOST('cancel', 'aZ09');
  37. $contextpage = GETPOST('contextpage', 'aZ') ?GETPOST('contextpage', 'aZ') : 'inventorycard'; // To manage different context of search
  38. $backtopage = GETPOST('backtopage', 'alpha');
  39. $fk_warehouse = GETPOST('fk_warehouse', 'int');
  40. $fk_product = GETPOST('fk_product', 'int');
  41. $lineid = GETPOST('lineid', 'int');
  42. $batch = GETPOST('batch', 'alphanohtml');
  43. if (empty($conf->global->MAIN_USE_ADVANCED_PERMS)) {
  44. $result = restrictedArea($user, 'stock', $id);
  45. } else {
  46. $result = restrictedArea($user, 'stock', $id, '', 'inventory_advance');
  47. }
  48. // Initialize technical objects
  49. $object = new Inventory($db);
  50. $extrafields = new ExtraFields($db);
  51. $diroutputmassaction = $conf->stock->dir_output.'/temp/massgeneration/'.$user->id;
  52. $hookmanager->initHooks(array('inventorycard')); // Note that conf->hooks_modules contains array
  53. // Fetch optionals attributes and labels
  54. $extrafields->fetch_name_optionals_label($object->table_element);
  55. $search_array_options = $extrafields->getOptionalsFromPost($object->table_element, '', 'search_');
  56. // Initialize array of search criterias
  57. $search_all = GETPOST("search_all", 'alpha');
  58. $search = array();
  59. foreach ($object->fields as $key => $val) {
  60. if (GETPOST('search_'.$key, 'alpha')) {
  61. $search[$key] = GETPOST('search_'.$key, 'alpha');
  62. }
  63. }
  64. if (empty($action) && empty($id) && empty($ref)) {
  65. $action = 'view';
  66. }
  67. // Load object
  68. include DOL_DOCUMENT_ROOT.'/core/actions_fetchobject.inc.php'; // Must be include, not include_once.
  69. // Security check - Protection if external user
  70. //if ($user->socid > 0) accessforbidden();
  71. //if ($user->socid > 0) $socid = $user->socid;
  72. //$result = restrictedArea($user, 'mymodule', $id);
  73. if (empty($conf->global->MAIN_USE_ADVANCED_PERMS)) {
  74. $permissiontoadd = $user->rights->stock->creer;
  75. $permissiontodelete = $user->rights->stock->supprimer;
  76. } else {
  77. $permissiontoadd = $user->rights->stock->inventory_advance->write;
  78. $permissiontodelete = $user->rights->stock->inventory_advance->write;
  79. }
  80. $now = dol_now();
  81. /*
  82. * Actions
  83. */
  84. if ($cancel) {
  85. $action = '';
  86. }
  87. $parameters = array();
  88. $reshook = $hookmanager->executeHooks('doActions', $parameters, $object, $action); // Note that $action and $object may have been modified by some hooks
  89. if ($reshook < 0) {
  90. setEventMessages($hookmanager->error, $hookmanager->errors, 'errors');
  91. }
  92. if (empty($reshook)) {
  93. $error = 0;
  94. if ($action == 'cancel_record' && $permissiontoadd) {
  95. $object->setCanceled($user);
  96. }
  97. // Close inventory by recording the stock movements
  98. if ($action == 'update' && !empty($user->rights->stock->mouvement->creer)) {
  99. $stockmovment = new MouvementStock($db);
  100. $stockmovment->setOrigin($object->element, $object->id);
  101. $cacheOfProducts = array();
  102. $db->begin();
  103. $sql = 'SELECT id.rowid, id.datec as date_creation, id.tms as date_modification, id.fk_inventory, id.fk_warehouse,';
  104. $sql .= ' id.fk_product, id.batch, id.qty_stock, id.qty_view, id.qty_regulated';
  105. $sql .= ' FROM '.MAIN_DB_PREFIX.'inventorydet as id';
  106. $sql .= ' WHERE id.fk_inventory = '.((int) $object->id);
  107. $resql = $db->query($sql);
  108. if ($resql) {
  109. $num = $db->num_rows($resql);
  110. $i = 0;
  111. $totalarray = array();
  112. while ($i < $num) {
  113. $line = $db->fetch_object($resql);
  114. $qty_stock = $line->qty_stock;
  115. $qty_view = $line->qty_view; // The quantity viewed by inventorier, the qty we target
  116. // Load real stock we have now.
  117. if (isset($cacheOfProducts[$line->fk_product])) {
  118. $product_static = $cacheOfProducts[$line->fk_product];
  119. } else {
  120. $product_static = new Product($db);
  121. $result = $product_static->fetch($line->fk_product, '', '', '', 1, 1, 1);
  122. //$option = 'nobatch';
  123. $option .= ',novirtual';
  124. $product_static->load_stock($option); // Load stock_reel + stock_warehouse.
  125. $cacheOfProducts[$product_static->id] = $product_static;
  126. }
  127. // Get the real quantity in stock now, but before the stock move for inventory.
  128. $realqtynow = $product_static->stock_warehouse[$line->fk_warehouse]->real;
  129. if ($conf->productbatch->enabled && $product_static->hasbatch()) {
  130. $realqtynow = $product_static->stock_warehouse[$line->fk_warehouse]->detail_batch[$line->batch]->qty;
  131. }
  132. if (!is_null($qty_view)) {
  133. $stock_movement_qty = price2num($qty_view - $realqtynow, 'MS');
  134. if ($stock_movement_qty != 0) {
  135. if ($stock_movement_qty < 0) {
  136. $movement_type = 1;
  137. } else {
  138. $movement_type = 0;
  139. }
  140. $datemovement = '';
  141. //$inventorycode = 'INV'.$object->id;
  142. $inventorycode = 'INV-'.$object->ref;
  143. $idstockmove = $stockmovment->_create($user, $line->fk_product, $line->fk_warehouse, $stock_movement_qty, $movement_type, 0, $langs->trans('LabelOfInventoryMovemement', $object->ref), $inventorycode, $datemovement, '', '', $line->batch);
  144. if ($idstockmove < 0) {
  145. $error++;
  146. setEventMessages($stockmovment->error, $stockmovment->errors, 'errors');
  147. break;
  148. }
  149. // Update line with id of stock movement (and the start quantity if it has changed this last recording)
  150. $sqlupdate = "UPDATE ".MAIN_DB_PREFIX."inventorydet";
  151. $sqlupdate .= " SET fk_movement = ".((int) $idstockmove);
  152. if ($qty_stock != $realqtynow) {
  153. $sqlupdate .= ", qty_stock = ".((float) $realqtynow);
  154. }
  155. $sqlupdate .= " WHERE rowid = ".((int) $line->rowid);
  156. $resqlupdate = $db->query($sqlupdate);
  157. if (! $resqlupdate) {
  158. $error++;
  159. setEventMessages($db->lasterror(), null, 'errors');
  160. break;
  161. }
  162. }
  163. }
  164. $i++;
  165. }
  166. if (!$error) {
  167. $object->setRecorded($user);
  168. }
  169. } else {
  170. setEventMessages($db->lasterror, null, 'errors');
  171. $error++;
  172. }
  173. if (! $error) {
  174. $db->commit();
  175. } else {
  176. $db->rollback();
  177. }
  178. }
  179. // Save quantity found during inventory (when we click on Save button on inventory page)
  180. if ($action =='updateinventorylines' && $permissiontoadd) {
  181. $sql = 'SELECT id.rowid, id.datec as date_creation, id.tms as date_modification, id.fk_inventory, id.fk_warehouse,';
  182. $sql .= ' id.fk_product, id.batch, id.qty_stock, id.qty_view, id.qty_regulated';
  183. $sql .= ' FROM '.MAIN_DB_PREFIX.'inventorydet as id';
  184. $sql .= ' WHERE id.fk_inventory = '.((int) $object->id);
  185. $db->begin();
  186. $resql = $db->query($sql);
  187. if ($resql) {
  188. $num = $db->num_rows($resql);
  189. $i = 0;
  190. $totalarray = array();
  191. $inventoryline = new InventoryLine($db);
  192. while ($i < $num) {
  193. $line = $db->fetch_object($resql);
  194. $lineid = $line->rowid;
  195. $result = 0;
  196. $resultupdate = 0;
  197. if (GETPOST("id_".$lineid, 'alpha') != '') { // If a value was set ('0' or something else)
  198. $qtytoupdate = price2num(GETPOST("id_".$lineid, 'alpha'), 'MS');
  199. $result = $inventoryline->fetch($lineid);
  200. if ($qtytoupdate < 0) {
  201. $result = -1;
  202. setEventMessages($langs->trans("FieldCannotBeNegative", $langs->transnoentitiesnoconv("RealQty")), null, 'errors');
  203. }
  204. if ($result > 0) {
  205. $inventoryline->qty_stock = price2num(GETPOST('stock_qty_'.$lineid, 'alpha'), 'MS'); // The new value that was set in as hidden field
  206. $inventoryline->qty_view = $qtytoupdate; // The new value we want
  207. $resultupdate = $inventoryline->update($user);
  208. }
  209. } else {
  210. // Delete record
  211. $result = $inventoryline->fetch($lineid);
  212. if ($result > 0) {
  213. $inventoryline->qty_view = null; // The new value we want
  214. $resultupdate = $inventoryline->update($user);
  215. }
  216. }
  217. if ($result < 0 || $resultupdate < 0) {
  218. $error++;
  219. }
  220. $i++;
  221. }
  222. }
  223. // Update user that update quantities
  224. if (! $error) {
  225. $sqlupdate = "UPDATE ".MAIN_DB_PREFIX."inventory";
  226. $sqlupdate .= " SET fk_user_modif = ".((int) $user->id);
  227. $sqlupdate .= " WHERE rowid = ".((int) $object->id);
  228. $resqlupdate = $db->query($sqlupdate);
  229. if (! $resqlupdate) {
  230. $error++;
  231. setEventMessages($db->lasterror(), null, 'errors');
  232. }
  233. }
  234. if (!$error) {
  235. $db->commit();
  236. } else {
  237. $db->rollback();
  238. }
  239. }
  240. $backurlforlist = DOL_URL_ROOT.'/product/inventory/list.php';
  241. $backtopage = DOL_URL_ROOT.'/product/inventory/inventory.php?id='.$object->id;
  242. // Actions cancel, add, update, delete or clone
  243. include DOL_DOCUMENT_ROOT.'/core/actions_addupdatedelete.inc.php';
  244. // Actions when linking object each other
  245. include DOL_DOCUMENT_ROOT.'/core/actions_dellink.inc.php';
  246. // Actions when printing a doc from card
  247. include DOL_DOCUMENT_ROOT.'/core/actions_printing.inc.php';
  248. // Actions to send emails
  249. /*$triggersendname = 'MYOBJECT_SENTBYMAIL';
  250. $autocopy='MAIN_MAIL_AUTOCOPY_MYOBJECT_TO';
  251. $trackid='stockinv'.$object->id;
  252. include DOL_DOCUMENT_ROOT.'/core/actions_sendmails.inc.php';*/
  253. if (GETPOST('addline', 'alpha')) {
  254. $qty= (GETPOST('qtytoadd') != '' ? price2num(GETPOST('qtytoadd', 'MS')) : null);
  255. if ($fk_warehouse <= 0) {
  256. $error++;
  257. setEventMessages($langs->trans("ErrorFieldRequired", $langs->transnoentitiesnoconv("Warehouse")), null, 'errors');
  258. }
  259. if ($fk_product <= 0) {
  260. $error++;
  261. setEventMessages($langs->trans("ErrorFieldRequired", $langs->transnoentitiesnoconv("Product")), null, 'errors');
  262. }
  263. if (price2num(GETPOST('qtytoadd'), 'MS') < 0) {
  264. $error++;
  265. setEventMessages($langs->trans("FieldCannotBeNegative", $langs->transnoentitiesnoconv("RealQty")), null, 'errors');
  266. }
  267. if (!$error && !empty($conf->productbatch->enabled)) {
  268. $tmpproduct = new Product($db);
  269. $result = $tmpproduct->fetch($fk_product);
  270. if (empty($error) && $tmpproduct->status_batch>0 && empty($batch)) {
  271. $error++;
  272. $langs->load("errors");
  273. setEventMessages($langs->trans("ErrorProductNeedBatchNumber", $tmpproduct->ref), null, 'errors');
  274. }
  275. if (empty($error) && $tmpproduct->status_batch==2 && !empty($batch) && $qty>1) {
  276. $error++;
  277. $langs->load("errors");
  278. setEventMessages($langs->trans("TooManyQtyForSerialNumber", $tmpproduct->ref, $batch), null, 'errors');
  279. }
  280. if (empty($error) && empty($tmpproduct->status_batch) && !empty($batch)) {
  281. $error++;
  282. $langs->load("errors");
  283. setEventMessages($langs->trans("ErrorProductDoesNotNeedBatchNumber", $tmpproduct->ref), null, 'errors');
  284. }
  285. }
  286. if (!$error) {
  287. $tmp = new InventoryLine($db);
  288. $tmp->fk_inventory = $object->id;
  289. $tmp->fk_warehouse = $fk_warehouse;
  290. $tmp->fk_product = $fk_product;
  291. $tmp->batch = $batch;
  292. $tmp->datec = $now;
  293. $tmp->qty_view = $qty;
  294. $result = $tmp->create($user);
  295. if ($result < 0) {
  296. if ($db->lasterrno() == 'DB_ERROR_RECORD_ALREADY_EXISTS') {
  297. $langs->load("errors");
  298. setEventMessages($langs->trans("ErrorRecordAlreadyExists"), null, 'errors');
  299. } else {
  300. dol_print_error($db, $tmp->error, $tmp->errors);
  301. }
  302. } else {
  303. // Clear var
  304. $_POST['batch'] = '';
  305. $_POST['qtytoadd'] = '';
  306. }
  307. }
  308. }
  309. }
  310. /*
  311. * View
  312. */
  313. $form = new Form($db);
  314. $formproduct = new FormProduct($db);
  315. $help_url = '';
  316. llxHeader('', $langs->trans('Inventory'), $help_url);
  317. // Disable button Generate movement if data were modified and not saved
  318. print '<script type="text/javascript">
  319. function disablebuttonmakemovementandclose() {
  320. console.log("Disable button idbuttonmakemovementandclose until we save");
  321. jQuery("#idbuttonmakemovementandclose").attr(\'disabled\',\'disabled\');
  322. jQuery("#idbuttonmakemovementandclose").attr(\'onclick\', \'return false;\');
  323. jQuery("#idbuttonmakemovementandclose").attr(\'title\',\''.dol_escape_js($langs->trans("SaveQtyFirst")).'\');
  324. jQuery("#idbuttonmakemovementandclose").attr(\'class\',\'butActionRefused classfortooltip\');
  325. };
  326. jQuery(document).ready(function() {
  327. jQuery(".realqty").keyup(function() {
  328. console.log("keyup on realqty");
  329. disablebuttonmakemovementandclose();
  330. });
  331. jQuery(".realqty").change(function() {
  332. console.log("change on realqty");
  333. disablebuttonmakemovementandclose();
  334. });
  335. });
  336. </script>';
  337. // Part to show record
  338. if ($object->id > 0) {
  339. $res = $object->fetch_optionals();
  340. $head = inventoryPrepareHead($object);
  341. print dol_get_fiche_head($head, 'inventory', $langs->trans("Inventory"), -1, 'stock');
  342. $formconfirm = '';
  343. // Confirmation to delete
  344. if ($action == 'delete') {
  345. $formconfirm = $form->formconfirm($_SERVER["PHP_SELF"].'?id='.$object->id, $langs->trans('DeleteInventory'), $langs->trans('ConfirmDeleteOrder'), 'confirm_delete', '', 0, 1);
  346. }
  347. // Confirmation to delete line
  348. if ($action == 'deleteline') {
  349. $formconfirm = $form->formconfirm($_SERVER["PHP_SELF"].'?id='.$object->id.'&lineid='.$lineid, $langs->trans('DeleteLine'), $langs->trans('ConfirmDeleteLine'), 'confirm_deleteline', '', 0, 1);
  350. }
  351. // Clone confirmation
  352. if ($action == 'clone') {
  353. // Create an array for form
  354. $formquestion = array();
  355. $formconfirm = $form->formconfirm($_SERVER["PHP_SELF"].'?id='.$object->id, $langs->trans('ToClone'), $langs->trans('ConfirmCloneMyObject', $object->ref), 'confirm_clone', $formquestion, 'yes', 1);
  356. }
  357. // Confirmation to close
  358. if ($action == 'record') {
  359. $formconfirm = $form->formconfirm($_SERVER["PHP_SELF"].'?id='.$object->id, $langs->trans('Close'), $langs->trans('ConfirmFinish'), 'update', '', 0, 1);
  360. $action = 'view';
  361. }
  362. // Confirmation to close
  363. if ($action == 'confirm_cancel') {
  364. $formconfirm = $form->formconfirm($_SERVER["PHP_SELF"].'?id='.$object->id, $langs->trans('Cancel'), $langs->trans('ConfirmCancel'), 'cancel_record', '', 0, 1);
  365. $action = 'view';
  366. }
  367. // Call Hook formConfirm
  368. $parameters = array('formConfirm' => $formconfirm, 'lineid' => $lineid);
  369. $reshook = $hookmanager->executeHooks('formConfirm', $parameters, $object, $action); // Note that $action and $object may have been modified by hook
  370. if (empty($reshook)) {
  371. $formconfirm .= $hookmanager->resPrint;
  372. } elseif ($reshook > 0) {
  373. $formconfirm = $hookmanager->resPrint;
  374. }
  375. // Print form confirm
  376. print $formconfirm;
  377. // Object card
  378. // ------------------------------------------------------------
  379. $linkback = '<a href="'.DOL_URL_ROOT.'/product/inventory/list.php">'.$langs->trans("BackToList").'</a>';
  380. $morehtmlref = '<div class="refidno">';
  381. /*
  382. // Ref bis
  383. $morehtmlref.=$form->editfieldkey("RefBis", 'ref_client', $object->ref_client, $object, $user->rights->inventory->creer, 'string', '', 0, 1);
  384. $morehtmlref.=$form->editfieldval("RefBis", 'ref_client', $object->ref_client, $object, $user->rights->inventory->creer, 'string', '', null, null, '', 1);
  385. // Thirdparty
  386. $morehtmlref.='<br>'.$langs->trans('ThirdParty') . ' : ' . $soc->getNomUrl(1);
  387. // Project
  388. if (! empty($conf->projet->enabled))
  389. {
  390. $langs->load("projects");
  391. $morehtmlref.='<br>'.$langs->trans('Project') . ' ';
  392. if ($user->rights->inventory->creer)
  393. {
  394. if ($action != 'classify')
  395. {
  396. $morehtmlref.='<a class="editfielda" href="' . $_SERVER['PHP_SELF'] . '?action=classify&token='.newToken().'&id=' . $object->id . '">' . img_edit($langs->transnoentitiesnoconv('SetProject')) . '</a> : ';
  397. if ($action == 'classify') {
  398. //$morehtmlref.=$form->form_project($_SERVER['PHP_SELF'] . '?id=' . $object->id, $object->socid, $object->fk_project, 'projectid', 0, 0, 1, 1);
  399. $morehtmlref.='<form method="post" action="'.$_SERVER['PHP_SELF'].'?id='.$object->id.'">';
  400. $morehtmlref.='<input type="hidden" name="action" value="classin">';
  401. $morehtmlref.='<input type="hidden" name="token" value="'.newToken().'">';
  402. $morehtmlref.=$formproject->select_projects($object->socid, $object->fk_project, 'projectid', $maxlength, 0, 1, 0, 1, 0, 0, '', 1);
  403. $morehtmlref.='<input type="submit" class="button valignmiddle" value="'.$langs->trans("Modify").'">';
  404. $morehtmlref.='</form>';
  405. } else {
  406. $morehtmlref.=$form->form_project($_SERVER['PHP_SELF'] . '?id=' . $object->id, $object->socid, $object->fk_project, 'none', 0, 0, 0, 1);
  407. }
  408. }
  409. } else {
  410. if (! empty($object->fk_project)) {
  411. $proj = new Project($db);
  412. $proj->fetch($object->fk_project);
  413. $morehtmlref.=$proj->getNomUrl();
  414. } else {
  415. $morehtmlref.='';
  416. }
  417. }
  418. }
  419. */
  420. $morehtmlref .= '</div>';
  421. dol_banner_tab($object, 'ref', $linkback, 1, 'ref', 'ref', $morehtmlref);
  422. print '<div class="fichecenter">';
  423. print '<div class="fichehalfleft">';
  424. print '<div class="underbanner clearboth"></div>';
  425. print '<table class="border centpercent tableforfield">'."\n";
  426. // Common attributes
  427. include DOL_DOCUMENT_ROOT.'/core/tpl/commonfields_view.tpl.php';
  428. // Other attributes. Fields from hook formObjectOptions and Extrafields.
  429. include DOL_DOCUMENT_ROOT.'/core/tpl/extrafields_view.tpl.php';
  430. //print '<tr><td class="titlefield fieldname_invcode">'.$langs->trans("InventoryCode").'</td><td>INV'.$object->id.'</td></tr>';
  431. print '</table>';
  432. print '</div>';
  433. print '</div>';
  434. print '<div class="clearboth"></div>';
  435. print dol_get_fiche_end();
  436. print '<form id="formrecord" name="formrecord" method="POST" action="'.$_SERVER["PHP_SELF"].'">';
  437. print '<input type="hidden" name="token" value="'.newToken().'">';
  438. print '<input type="hidden" name="action" value="updateinventorylines">';
  439. print '<input type="hidden" name="id" value="'.$object->id.'">';
  440. if ($backtopage) {
  441. print '<input type="hidden" name="backtopage" value="'.$backtopage.'">';
  442. }
  443. // Buttons for actions
  444. if ($action != 'record') {
  445. print '<div class="tabsAction">'."\n";
  446. $parameters = array();
  447. $reshook = $hookmanager->executeHooks('addMoreActionsButtons', $parameters, $object, $action); // Note that $action and $object may have been modified by hook
  448. if ($reshook < 0) {
  449. setEventMessages($hookmanager->error, $hookmanager->errors, 'errors');
  450. }
  451. if (empty($reshook)) {
  452. if ($object->status == Inventory::STATUS_DRAFT) {
  453. if ($permissiontoadd) {
  454. print '<a class="butAction" href="'.$_SERVER["PHP_SELF"].'?id='.$object->id.'&action=confirm_validate&confirm=yes&token='.newToken().'">'.$langs->trans("Validate").' ('.$langs->trans("Start").')</a>'."\n";
  455. } else {
  456. print '<a class="butActionRefused classfortooltip" href="#" title="'.dol_escape_htmltag($langs->trans("NotEnoughPermissions")).'">'.$langs->trans('Validate').' ('.$langs->trans("Start").')</a>'."\n";
  457. }
  458. }
  459. // Save
  460. if ($object->status == $object::STATUS_VALIDATED) {
  461. if ($permissiontoadd) {
  462. print '<a class="butAction classfortooltip" id="idbuttonmakemovementandclose" href="'.$_SERVER["PHP_SELF"].'?id='.$object->id.'&action=record&token='.newToken().'" title="'.dol_escape_htmltag($langs->trans("MakeMovementsAndClose")).'">'.$langs->trans("MakeMovementsAndClose").'</a>'."\n";
  463. } else {
  464. print '<a class="butActionRefused classfortooltip" href="#" title="'.dol_escape_htmltag($langs->trans("NotEnoughPermissions")).'">'.$langs->trans('MakeMovementsAndClose').'</a>'."\n";
  465. }
  466. if ($permissiontoadd) {
  467. print '<a class="butActionDelete" href="'.$_SERVER["PHP_SELF"].'?id='.$object->id.'&action=confirm_cancel&token='.newToken().'">'.$langs->trans("Cancel").'</a>'."\n";
  468. }
  469. }
  470. }
  471. print '</div>'."\n";
  472. if ($object->status != Inventory::STATUS_DRAFT && $object->status != Inventory::STATUS_VALIDATED) {
  473. print '<br><br>';
  474. }
  475. }
  476. if ($object->status == Inventory::STATUS_VALIDATED) {
  477. print '<center>';
  478. if (!empty($conf->use_javascript_ajax)) {
  479. if ($permissiontoadd) {
  480. // Link to launch scan tool
  481. if (!empty($conf->barcode->enabled) || !empty($conf->productbatch->enabled)) {
  482. print '<a href="'.$_SERVER["PHP_SELF"].'?id='.$object->id.'&action=updatebyscaning" class="marginrightonly paddingright marginleftonly paddingleft">'.img_picto('', 'barcode', 'class="paddingrightonly"').$langs->trans("UpdateByScaning").'</a>';
  483. }
  484. // Link to autofill
  485. print '<a id="fillwithexpected" class="marginrightonly paddingright marginleftonly paddingleft" href="#">'.img_picto('', 'autofill', 'class="paddingrightonly"').$langs->trans('AutofillWithExpected').'</a>';
  486. print '<script>';
  487. print '$( document ).ready(function() {';
  488. print ' $("#fillwithexpected").on("click",function fillWithExpected(){
  489. $(".expectedqty").each(function(){
  490. var object = $(this)[0];
  491. var objecttofill = $("#"+object.id+"_input")[0];
  492. objecttofill.value = object.innerText;
  493. })
  494. console.log("Values filled (after click on fillwithexpected)");
  495. disablebuttonmakemovementandclose();
  496. return false;
  497. });';
  498. print '});';
  499. print '</script>';
  500. // Link to reset qty
  501. print '<a href="#" id="clearqty" class="marginrightonly paddingright marginleftonly paddingleft">'.img_picto('', 'eraser', 'class="paddingrightonly"').$langs->trans("ClearQtys").'</a>';
  502. } else {
  503. print '<a class="classfortooltip marginrightonly paddingright marginleftonly paddingleft" href="#" title="'.dol_escape_htmltag($langs->trans("NotEnoughPermissions")).'">'.$langs->trans("Save").'</a>'."\n";
  504. }
  505. }
  506. print '<br>';
  507. print '<br>';
  508. print '</center>';
  509. }
  510. // Popup for mass barcode scanning
  511. if ($action == 'updatebyscaning') {
  512. if ($permissiontoadd) {
  513. // Output the javascript to manage the scanner tool.
  514. print '<script>';
  515. print '
  516. var duplicatedbatchcode = [];
  517. var errortab1 = [];
  518. var errortab2 = [];
  519. var errortab3 = [];
  520. var errortab4 = [];
  521. function barcodescannerjs(){
  522. console.log("We catch inputs in scanner box");
  523. jQuery("#scantoolmessage").text();
  524. var selectaddorreplace = $("select[name=selectaddorreplace]").val();
  525. var barcodemode = $("input[name=barcodemode]:checked").val();
  526. var barcodeproductqty = $("input[name=barcodeproductqty]").val();
  527. var textarea = $("textarea[name=barcodelist]").val();
  528. var textarray = textarea.split(/[\s,;]+/);
  529. var tabproduct = [];
  530. duplicatedbatchcode = [];
  531. errortab1 = [];
  532. errortab2 = [];
  533. errortab3 = [];
  534. errortab4 = [];
  535. textarray = textarray.filter(function(value){
  536. return value != "";
  537. });
  538. if(textarray.some((element) => element != "")){
  539. $(".expectedqty").each(function(){
  540. id = this.id;
  541. console.log("Analyze the line "+id+" in inventory, barcodemode="+barcodemode);
  542. warehouse = $("#"+id+"_warehouse").attr(\'data-ref\');
  543. //console.log(warehouse);
  544. productbarcode = $("#"+id+"_product").attr(\'data-barcode\');
  545. //console.log(productbarcode);
  546. productbatchcode = $("#"+id+"_batch").attr(\'data-batch\');
  547. //console.log(productbatchcode);
  548. if (barcodemode != "barcodeforproduct") {
  549. tabproduct.forEach(product=>{
  550. console.log("product.Batch="+product.Batch+" productbatchcode="+productbatchcode);
  551. if(product.Batch != "" && product.Batch == productbatchcode){
  552. console.log("duplicate batch code found for batch code "+productbatchcode);
  553. duplicatedbatchcode.push(productbatchcode);
  554. }
  555. })
  556. }
  557. productinput = $("#"+id+"_input").val();
  558. if(productinput == ""){
  559. productinput = 0
  560. }
  561. tabproduct.push({\'Id\':id,\'Warehouse\':warehouse,\'Barcode\':productbarcode,\'Batch\':productbatchcode,\'Qty\':productinput,\'fetched\':false});
  562. });
  563. console.log("Loop on each record entered in the textarea");
  564. textarray.forEach(function(element,index){
  565. console.log("Process record element="+element+" id="+id);
  566. var verify_batch = false;
  567. var verify_barcode = false;
  568. switch(barcodemode){
  569. case "barcodeforautodetect":
  570. verify_barcode = barcodeserialforproduct(tabproduct,index,element,barcodeproductqty,selectaddorreplace,"barcode",true);
  571. verify_batch = barcodeserialforproduct(tabproduct,index,element,barcodeproductqty,selectaddorreplace,"lotserial",true);
  572. break;
  573. case "barcodeforproduct":
  574. verify_barcode = barcodeserialforproduct(tabproduct,index,element,barcodeproductqty,selectaddorreplace,"barcode");
  575. break;
  576. case "barcodeforlotserial":
  577. verify_batch = barcodeserialforproduct(tabproduct,index,element,barcodeproductqty,selectaddorreplace,"lotserial");
  578. break;
  579. default:
  580. alert(\''.dol_escape_js($langs->trans("ErrorWrongBarcodemode")).' "\'+barcodemode+\'"\');
  581. throw \''.dol_escape_js($langs->trans('ErrorWrongBarcodemode')).' "\'+barcodemode+\'"\';
  582. }
  583. if (verify_batch == false && verify_barcode == false) { /* If the 2 flags are false, not found error */
  584. errortab2.push(element);
  585. } else if (verify_batch == true && verify_barcode == true) { /* If the 2 flags are true, error: we don t know which one to take */
  586. errortab3.push(element);
  587. } else if (verify_batch == true) {
  588. console.log("element="+element);
  589. console.log(duplicatedbatchcode);
  590. if (duplicatedbatchcode.includes(element)) {
  591. errortab1.push(element);
  592. }
  593. }
  594. });
  595. if (Object.keys(errortab1).length < 1 && Object.keys(errortab2).length < 1 && Object.keys(errortab3).length < 1) {
  596. tabproduct.forEach(product => {
  597. if(product.Qty!=0){
  598. console.log("We change #"+product.Id+"_input to match input in scanner box");
  599. if(product.hasOwnProperty("reelqty")){
  600. $.ajax({ url: \''.DOL_URL_ROOT.'/product/inventory/ajax/searchfrombarcode.php\',
  601. data: { "token":"'.newToken().'", "action":"addnewlineproduct", "fk_entrepot":product.Warehouse, "batch":product.Batch, "fk_inventory":'.dol_escape_js($object->id).', "fk_product":product.fk_product, "reelqty":product.reelqty},
  602. type: \'POST\',
  603. async: false,
  604. success: function(response) {
  605. response = JSON.parse(response);
  606. if(response.status == "success"){
  607. console.log(response.message);
  608. $("<input type=\'text\' value=\'"+product.Qty+"\' />")
  609. .attr("id", "id_"+response.id_line+"_input")
  610. .attr("name", "id_"+response.id_line)
  611. .appendTo("#formrecord");
  612. }else{
  613. console.error(response.message);
  614. }
  615. },
  616. error : function(output) {
  617. console.error("Error on line creation function");
  618. },
  619. });
  620. } else {
  621. $("#"+product.Id+"_input").val(product.Qty);
  622. }
  623. }
  624. });
  625. jQuery("#scantoolmessage").text("'.dol_escape_js($langs->transnoentities("QtyWasAddedToTheScannedBarcode")).'\n");
  626. /* document.forms["formrecord"].submit(); */
  627. } else {
  628. let stringerror = "";
  629. if (Object.keys(errortab1).length > 0) {
  630. stringerror += "<br>'.dol_escape_js($langs->transnoentities('ErrorSameBatchNumber')).': ";
  631. errortab1.forEach(element => {
  632. stringerror += (element + ", ")
  633. });
  634. stringerror = stringerror.slice(0, -2); /* Remove last ", " */
  635. }
  636. if (Object.keys(errortab2).length > 0) {
  637. stringerror += "<br>'.dol_escape_js($langs->transnoentities('ErrorCantFindCodeInInventory')).': ";
  638. errortab2.forEach(element => {
  639. stringerror += (element + ", ")
  640. });
  641. stringerror = stringerror.slice(0, -2); /* Remove last ", " */
  642. }
  643. if (Object.keys(errortab3).length > 0) {
  644. stringerror += "<br>'.dol_escape_js($langs->transnoentities('ErrorCodeScannedIsBothProductAndSerial')).': ";
  645. errortab3.forEach(element => {
  646. stringerror += (element + ", ")
  647. });
  648. stringerror = stringerror.slice(0, -2); /* Remove last ", " */
  649. }
  650. if (Object.keys(errortab4).length > 0) {
  651. stringerror += "<br>'.dol_escape_js($langs->transnoentities('ErrorBarcodeNotFoundForProductWarehouse')).': ";
  652. errortab4.forEach(element => {
  653. stringerror += (element + ", ")
  654. });
  655. stringerror = stringerror.slice(0, -2); /* Remove last ", " */
  656. }
  657. jQuery("#scantoolmessage").html(\''.dol_escape_js($langs->transnoentities("ErrorOnElementsInventory")).'\' + stringerror);
  658. //alert("'.dol_escape_js($langs->trans("ErrorOnElementsInventory")).' :\n" + stringerror);
  659. }
  660. }
  661. }
  662. /* This methode is called by parent barcodescannerjs() */
  663. function barcodeserialforproduct(tabproduct,index,element,barcodeproductqty,selectaddorreplace,mode,autodetect=false){
  664. BarcodeIsInProduct=0;
  665. newproductrow=0
  666. result=false;
  667. tabproduct.forEach(product => {
  668. $.ajax({ url: \''.DOL_URL_ROOT.'/product/inventory/ajax/searchfrombarcode.php\',
  669. data: { "token":"'.newToken().'", "action":"existbarcode", '.(!empty($object->fk_warehouse)?'"fk_entrepot":'.$object->fk_warehouse.', ':'').(!empty($object->fk_product)?'"fk_product":'.$object->fk_product.', ':'').'"barcode":element, "product":product, "mode":mode},
  670. type: \'POST\',
  671. async: false,
  672. success: function(response) {
  673. response = JSON.parse(response);
  674. if (response.status == "success"){
  675. console.log(response.message);
  676. if(!newproductrow){
  677. newproductrow = response.object;
  678. }
  679. }else{
  680. if (mode!="lotserial" && autodetect==false && !errortab4.includes(element)){
  681. errortab4.push(element);
  682. console.error(response.message);
  683. }
  684. }
  685. },
  686. error : function(output) {
  687. console.error("Error on barcodeserialforproduct function");
  688. },
  689. });
  690. console.log("Product "+(index+=1)+": "+element);
  691. if(mode == "barcode"){
  692. testonproduct = product.Barcode
  693. }else if (mode == "lotserial"){
  694. testonproduct = product.Batch
  695. }
  696. if(testonproduct == element){
  697. if(selectaddorreplace == "add"){
  698. productqty = parseInt(product.Qty,10);
  699. product.Qty = productqty + parseInt(barcodeproductqty,10);
  700. }else if(selectaddorreplace == "replace"){
  701. if(product.fetched == false){
  702. product.Qty = barcodeproductqty
  703. product.fetched=true
  704. }else{
  705. productqty = parseInt(product.Qty,10);
  706. product.Qty = productqty + parseInt(barcodeproductqty,10);
  707. }
  708. }
  709. BarcodeIsInProduct+=1;
  710. }
  711. })
  712. if(BarcodeIsInProduct==0 && newproductrow!=0){
  713. 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});
  714. result = true;
  715. }
  716. if(BarcodeIsInProduct > 0){
  717. result = true;
  718. }
  719. return result;
  720. }
  721. ';
  722. print '</script>';
  723. }
  724. include DOL_DOCUMENT_ROOT.'/core/class/html.formother.class.php';
  725. $formother = new FormOther($db);
  726. print $formother->getHTMLScannerForm("barcodescannerjs", 'all');
  727. }
  728. //Call method to undo changes in real qty
  729. print '<script>';
  730. print 'jQuery(document).ready(function() {
  731. $("#clearqty").on("click", function() {
  732. console.log("Clear all values");
  733. disablebuttonmakemovementandclose();
  734. jQuery(".realqty").val("");
  735. return false; /* disable submit */
  736. });
  737. $(".undochangesqty").on("click", function undochangesqty() {
  738. console.log("Clear value of inventory line");
  739. id = this.id;
  740. id = id.split("_")[1];
  741. tmpvalue = $("#id_"+id+"_input_tmp").val()
  742. $("#id_"+id+"_input")[0].value = tmpvalue;
  743. disablebuttonmakemovementandclose();
  744. return false; /* disable submit */
  745. });
  746. });';
  747. print '</script>';
  748. print '<div class="fichecenter">';
  749. //print '<div class="fichehalfleft">';
  750. print '<div class="clearboth"></div>';
  751. //print load_fiche_titre($langs->trans('Consumption'), '', '');
  752. print '<div class="div-table-responsive-no-min">';
  753. print '<table id="tablelines" class="noborder noshadow centpercent">';
  754. print '<tr class="liste_titre">';
  755. print '<td>'.$langs->trans("Warehouse").'</td>';
  756. print '<td>'.$langs->trans("Product").'</td>';
  757. if (!empty($conf->productbatch->enabled)) {
  758. print '<td>';
  759. print $langs->trans("Batch");
  760. print '</td>';
  761. }
  762. print '<td class="right">'.$langs->trans("ExpectedQty").'</td>';
  763. print '<td class="right">';
  764. print $form->textwithpicto($langs->trans("RealQty"), $langs->trans("InventoryRealQtyHelp"));
  765. print '</td>';
  766. if ($object->status == $object::STATUS_DRAFT || $object->status == $object::STATUS_VALIDATED) {
  767. // Actions or link to stock movement
  768. print '<td class="center">';
  769. print '</td>';
  770. } else {
  771. // Actions or link to stock movement
  772. print '<td class="right">';
  773. //print $langs->trans("StockMovement");
  774. print '</td>';
  775. }
  776. print '</tr>';
  777. // Line to add a new line in inventory
  778. if ($object->status == $object::STATUS_DRAFT || $object->status == $object::STATUS_VALIDATED) {
  779. print '<tr>';
  780. print '<td>';
  781. print $formproduct->selectWarehouses((GETPOSTISSET('fk_warehouse') ? GETPOST('fk_warehouse', 'int') : $object->fk_warehouse), 'fk_warehouse', 'warehouseopen', 1, 0, 0, '', 0, 0, array(), 'maxwidth300');
  782. print '</td>';
  783. print '<td>';
  784. print $form->select_produits((GETPOSTISSET('fk_product') ? GETPOST('fk_product', 'int') : $object->fk_product), 'fk_product', '', 0, 0, -1, 2, '', 0, null, 0, '1', 0, 'maxwidth300');
  785. print '</td>';
  786. if (!empty($conf->productbatch->enabled)) {
  787. print '<td>';
  788. print '<input type="text" name="batch" class="maxwidth100" value="'.(GETPOSTISSET('batch') ? GETPOST('batch') : '').'">';
  789. print '</td>';
  790. }
  791. print '<td class="right"></td>';
  792. print '<td class="right">';
  793. print '<input type="text" name="qtytoadd" class="maxwidth75" value="">';
  794. print '</td>';
  795. // Actions
  796. print '<td class="center">';
  797. print '<input type="submit" class="button paddingright" name="addline" value="'.$langs->trans("Add").'">';
  798. print '</td>';
  799. print '</tr>';
  800. }
  801. // Request to show lines of inventory (prefilled after start/validate step)
  802. $sql = 'SELECT id.rowid, id.datec as date_creation, id.tms as date_modification, id.fk_inventory, id.fk_warehouse,';
  803. $sql .= ' id.fk_product, id.batch, id.qty_stock, id.qty_view, id.qty_regulated, id.fk_movement';
  804. $sql .= ' FROM '.MAIN_DB_PREFIX.'inventorydet as id';
  805. $sql .= ' WHERE id.fk_inventory = '.((int) $object->id);
  806. $cacheOfProducts = array();
  807. $cacheOfWarehouses = array();
  808. //$sql = '';
  809. $resql = $db->query($sql);
  810. if ($resql) {
  811. $num = $db->num_rows($resql);
  812. $i = 0;
  813. $hasinput = false;
  814. $totalarray = array();
  815. while ($i < $num) {
  816. $obj = $db->fetch_object($resql);
  817. if (isset($cacheOfWarehouses[$obj->fk_warehouse])) {
  818. $warehouse_static = $cacheOfWarehouses[$obj->fk_warehouse];
  819. } else {
  820. $warehouse_static = new Entrepot($db);
  821. $warehouse_static->fetch($obj->fk_warehouse);
  822. $cacheOfWarehouses[$warehouse_static->id] = $warehouse_static;
  823. }
  824. // Load real stock we have now
  825. $option = '';
  826. if (isset($cacheOfProducts[$obj->fk_product])) {
  827. $product_static = $cacheOfProducts[$obj->fk_product];
  828. } else {
  829. $product_static = new Product($db);
  830. $result = $product_static->fetch($obj->fk_product, '', '', '', 1, 1, 1);
  831. //$option = 'nobatch';
  832. $option .= ',novirtual';
  833. $product_static->load_stock($option); // Load stock_reel + stock_warehouse.
  834. $cacheOfProducts[$product_static->id] = $product_static;
  835. }
  836. print '<tr class="oddeven">';
  837. print '<td id="id_'.$obj->rowid.'_warehouse" data-ref="'.dol_escape_htmltag($warehouse_static->ref).'">';
  838. print $warehouse_static->getNomUrl(1);
  839. print '</td>';
  840. print '<td id="id_'.$obj->rowid.'_product" data-ref="'.dol_escape_htmltag($product_static->ref).'" data-barcode="'.dol_escape_htmltag($product_static->barcode).'">';
  841. print $product_static->getNomUrl(1).' - '.$product_static->label;
  842. print '</td>';
  843. if (!empty($conf->productbatch->enabled)) {
  844. print '<td id="id_'.$obj->rowid.'_batch" data-batch="'.dol_escape_htmltag($obj->batch).'">';
  845. print dol_escape_htmltag($obj->batch);
  846. print '</td>';
  847. }
  848. // Expected quantity = Quantity in stock when we start inventory
  849. print '<td class="right expectedqty" id="id_'.$obj->rowid.'" title="Stock viewed at last update: '.$obj->qty_stock.'">';
  850. $valuetoshow = $obj->qty_stock;
  851. // For inventory not yet close, we overwrite with the real value in stock now
  852. if ($object->status == $object::STATUS_DRAFT || $object->status == $object::STATUS_VALIDATED) {
  853. if (!empty($conf->productbatch->enabled) && $product_static->hasbatch()) {
  854. $valuetoshow = $product_static->stock_warehouse[$obj->fk_warehouse]->detail_batch[$obj->batch]->qty;
  855. } else {
  856. $valuetoshow = $product_static->stock_warehouse[$obj->fk_warehouse]->real;
  857. }
  858. }
  859. print price2num($valuetoshow, 'MS');
  860. print '<input type="hidden" name="stock_qty_'.$obj->rowid.'" value="'.$valuetoshow.'">';
  861. print '</td>';
  862. // Real quantity
  863. if ($object->status == $object::STATUS_DRAFT || $object->status == $object::STATUS_VALIDATED) {
  864. print '<td class="right">';
  865. $qty_view = GETPOST("id_".$obj->rowid) && price2num(GETPOST("id_".$obj->rowid), 'MS') >= 0 ? GETPOST("id_".$obj->rowid) : $obj->qty_view;
  866. //if (!$hasinput && $qty_view !== null && $obj->qty_stock != $qty_view) {
  867. if ($qty_view != '') {
  868. $hasinput = true;
  869. }
  870. print '<a id="undochangesqty_'.$obj->rowid.'" href="#" class="undochangesqty reposition marginrightonly" title="'.dol_escape_htmltag($langs->trans("Clear")).'">';
  871. print img_picto('', 'eraser', 'class="opacitymedium"');
  872. print '</a>';
  873. print '<input type="text" class="maxwidth75 right realqty" name="id_'.$obj->rowid.'" id="id_'.$obj->rowid.'_input" value="'.$qty_view.'">';
  874. print '</td>';
  875. // Picto delete line
  876. print '<td class="right">';
  877. print '<a class="reposition" href="'.DOL_URL_ROOT.'/product/inventory/inventory.php?id='.$object->id.'&lineid='.$obj->rowid.'&action=deleteline&token='.newToken().'">'.img_delete().'</a>';
  878. $qty_tmp = price2num(GETPOST("id_".$obj->rowid."_input_tmp", 'MS')) >= 0 ? GETPOST("id_".$obj->rowid."_input_tmp") : $qty_view;
  879. print '<input type="hidden" class="maxwidth75 right realqty" name="id_'.$obj->rowid.'_input_tmp" id="id_'.$obj->rowid.'_input_tmp" value="'.$qty_tmp.'">';
  880. print '</td>';
  881. } else {
  882. print '<td class="right nowraponall">';
  883. print $obj->qty_view; // qty found
  884. print '</td>';
  885. print '<td class="nowraponall right">';
  886. if ($obj->fk_movement > 0) {
  887. $stockmovment = new MouvementStock($db);
  888. $stockmovment->fetch($obj->fk_movement);
  889. print $stockmovment->getNomUrl(1, 'movements');
  890. }
  891. print '</td>';
  892. }
  893. print '</tr>';
  894. $i++;
  895. }
  896. } else {
  897. dol_print_error($db);
  898. }
  899. print '</table>';
  900. print '</div>';
  901. if ($object->status == $object::STATUS_VALIDATED) {
  902. print '<center><input id="submitrecord" type="submit" class="button button-save" name="save" value="'.$langs->trans("Save").'"></center>';
  903. }
  904. print '</div>';
  905. // Call method to disable the button if no qty entered yet for inventory
  906. if ($object->status != $object::STATUS_VALIDATED || !$hasinput) {
  907. print '<script type="text/javascript">
  908. jQuery(document).ready(function() {
  909. console.log("Call disablebuttonmakemovementandclose because status = '.((int) $object->status).' or $hasinput = '.((int) $hasinput).'");
  910. disablebuttonmakemovementandclose();
  911. });
  912. </script>';
  913. }
  914. print '</form>';
  915. }
  916. // End of page
  917. llxFooter();
  918. $db->close();