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 <http://www.gnu.org/licenses/>.
16 */
17
18/**
19 *   	\file       mo_movements.php
20 *		\ingroup    mrp
21 *		\brief      Page to show tock movements of a MO
22 */
23
24// Load Dolibarr environment
25require '../main.inc.php';
26
27require_once DOL_DOCUMENT_ROOT.'/core/class/html.formcompany.class.php';
28require_once DOL_DOCUMENT_ROOT.'/core/class/html.formfile.class.php';
29require_once DOL_DOCUMENT_ROOT.'/core/class/html.formprojet.class.php';
30require_once DOL_DOCUMENT_ROOT.'/product/class/html.formproduct.class.php';
31require_once DOL_DOCUMENT_ROOT.'/product/class/product.class.php';
32require_once DOL_DOCUMENT_ROOT.'/product/stock/class/entrepot.class.php';
33require_once DOL_DOCUMENT_ROOT.'/product/stock/class/mouvementstock.class.php';
34require_once DOL_DOCUMENT_ROOT.'/product/stock/class/productlot.class.php';
35dol_include_once('/mrp/class/mo.class.php');
36dol_include_once('/mrp/lib/mrp_mo.lib.php');
37
38// Load translation files required by the page
39$langs->loadLangs(array("mrp", "stocks", "other"));
40
41// Get parameters
42$id = GETPOST('id', 'int');
43$ref        = GETPOST('ref', 'alpha');
44$action = GETPOST('action', 'aZ09');
45$confirm    = GETPOST('confirm', 'alpha');
46$cancel     = GETPOST('cancel', 'aZ09');
47$contextpage = GETPOST('contextpage', 'aZ') ?GETPOST('contextpage', 'aZ') : 'mostockmovement'; // To manage different context of search
48$backtopage = GETPOST('backtopage', 'alpha');
49//$lineid   = GETPOST('lineid', 'int');
50
51$msid = GETPOST('msid', 'int');
52$year = GETPOST("year", 'int');
53$month = GETPOST("month", 'int');
54$search_ref = GETPOST('search_ref', 'alpha');
55$search_movement = GETPOST("search_movement", 'alpha');
56$search_product_ref = trim(GETPOST("search_product_ref", 'alpha'));
57$search_product = trim(GETPOST("search_product", 'alpha'));
58$search_warehouse = trim(GETPOST("search_warehouse", 'alpha'));
59$search_inventorycode = trim(GETPOST("search_inventorycode", 'alpha'));
60$search_user = trim(GETPOST("search_user", 'alpha'));
61$search_batch = trim(GETPOST("search_batch", 'alpha'));
62$search_qty = trim(GETPOST("search_qty", 'alpha'));
63$search_type_mouvement = GETPOST('search_type_mouvement', 'int');
64
65$limit = GETPOST('limit', 'int') ?GETPOST('limit', 'int') : $conf->liste_limit;
66$page = GETPOSTISSET('pageplusone') ? (GETPOST('pageplusone') - 1) : GETPOST("page", 'int');
67$sortfield = GETPOST("sortfield", 'alpha');
68$sortorder = GETPOST("sortorder", 'alpha');
69if (empty($page) || $page == -1) { $page = 0; }     // If $page is not defined, or '' or -1
70$offset = $limit * $page;
71if (!$sortfield) $sortfield = "m.datem";
72if (!$sortorder) $sortorder = "DESC";
73
74// Initialize technical objects
75$object = new Mo($db);
76$extrafields = new ExtraFields($db);
77$diroutputmassaction = $conf->mrp->dir_output.'/temp/massgeneration/'.$user->id;
78$hookmanager->initHooks(array('mocard', 'globalcard')); // Note that conf->hooks_modules contains array
79
80// Fetch optionals attributes and labels
81$extrafields->fetch_name_optionals_label($object->table_element);
82
83$search_array_options = $extrafields->getOptionalsFromPost($object->table_element, '', 'search_');
84
85// Initialize array of search criterias
86$search_all = trim(GETPOST("search_all", 'alpha'));
87$search = array();
88foreach ($object->fields as $key => $val)
89{
90	if (GETPOST('search_'.$key, 'alpha')) $search[$key] = GETPOST('search_'.$key, 'alpha');
91}
92
93if (empty($action) && empty($id) && empty($ref)) $action = 'view';
94
95// Load object
96include DOL_DOCUMENT_ROOT.'/core/actions_fetchobject.inc.php'; // Must be include, not include_once.
97
98// Security check - Protection if external user
99//if ($user->socid > 0) accessforbidden();
100//if ($user->socid > 0) $socid = $user->socid;
101$isdraft = (($object->status == $object::STATUS_DRAFT) ? 1 : 0);
102$result = restrictedArea($user, 'mrp', $object->id, 'mrp_mo', '', 'fk_soc', 'rowid', $isdraft);
103
104$objectlist = new MouvementStock($db);
105
106// Definition of fields for list
107$arrayfields = array(
108	'm.rowid'=>array('label'=>$langs->trans("Ref"), 'checked'=>1, 'position'=>1),
109	'm.datem'=>array('label'=>$langs->trans("Date"), 'checked'=>1, 'position'=>2),
110	'p.ref'=>array('label'=>$langs->trans("ProductRef"), 'checked'=>1, 'css'=>'maxwidth100', 'position'=>10),
111	'p.label'=>array('label'=>$langs->trans("ProductLabel"), 'checked'=>1, 'position'=>15),
112	'm.batch'=>array('label'=>$langs->trans("BatchNumberShort"), 'checked'=>1, 'enabled'=>(!empty($conf->productbatch->enabled)), 'position'=>20),
113	'pl.eatby'=>array('label'=>$langs->trans("EatByDate"), 'checked'=>0, 'position'=>10, 'enabled'=>(!empty($conf->productbatch->enabled)), 'position'=>21),
114	'pl.sellby'=>array('label'=>$langs->trans("SellByDate"), 'checked'=>0, 'position'=>10, 'enabled'=>(!empty($conf->productbatch->enabled)), 'position'=>22),
115	'e.ref'=>array('label'=>$langs->trans("Warehouse"), 'checked'=>1, 'position'=>30),
116	'm.fk_user_author'=>array('label'=>$langs->trans("Author"), 'checked'=>0, 'position'=>40),
117	'm.inventorycode'=>array('label'=>$langs->trans("InventoryCodeShort"), 'checked'=>1, 'position'=>42),
118	'm.label'=>array('label'=>$langs->trans("MovementLabel"), 'checked'=>1, 'position'=>45),
119	'm.type_mouvement'=>array('label'=>$langs->trans("TypeMovement"), 'checked'=>1, 'position'=>48),
120	'origin'=>array('label'=>$langs->trans("Origin"), 'enabled'=>0, 'checked'=>0, 'position'=>50),
121	'm.value'=>array('label'=>$langs->trans("Qty"), 'checked'=>1, 'position'=>60),
122	'm.price'=>array('label'=>$langs->trans("UnitPurchaseValue"), 'enabled'=>0, 'checked'=>0, 'position'=>62),
123	//'m.datec'=>array('label'=>$langs->trans("DateCreation"), 'checked'=>0, 'position'=>500),
124	//'m.tms'=>array('label'=>$langs->trans("DateModificationShort"), 'checked'=>0, 'position'=>500)
125);
126if (!empty($conf->global->PRODUCT_DISABLE_SELLBY)) {
127	unset($arrayfields['pl.sellby']);
128}
129if (!empty($conf->global->PRODUCT_DISABLE_EATBY)) {
130	unset($arrayfields['pl.eatby']);
131}
132$objectlist->fields = dol_sort_array($objectlist->fields, 'position');
133$arrayfields = dol_sort_array($arrayfields, 'position');
134
135$permissionnote = $user->rights->mrp->write; // Used by the include of actions_setnotes.inc.php
136$permissiondellink = $user->rights->mrp->write; // Used by the include of actions_dellink.inc.php
137$permissiontoadd = $user->rights->mrp->write; // Used by the include of actions_addupdatedelete.inc.php and actions_lineupdown.inc.php
138$permissiontodelete = $user->rights->mrp->delete || ($permissiontoadd && isset($object->status) && $object->status == $object::STATUS_DRAFT);
139$upload_dir = $conf->mrp->multidir_output[isset($object->entity) ? $object->entity : 1];
140
141$permissiontoproduce = $permissiontoadd;
142
143
144/*
145 * Actions
146 */
147
148if (GETPOST('cancel', 'alpha')) { $action = 'list'; $massaction = ''; }
149if (!GETPOST('confirmmassaction', 'alpha') && $massaction != 'presend' && $massaction != 'confirm_presend') { $massaction = ''; }
150
151$parameters = array();
152$reshook = $hookmanager->executeHooks('doActions', $parameters, $object, $action); // Note that $action and $object may have been modified by some hooks
153if ($reshook < 0) setEventMessages($hookmanager->error, $hookmanager->errors, 'errors');
154
155include DOL_DOCUMENT_ROOT.'/core/actions_changeselectedfields.inc.php';
156
157// Do we click on purge search criteria ?
158if (GETPOST('button_removefilter_x', 'alpha') || GETPOST('button_removefilter.x', 'alpha') || GETPOST('button_removefilter', 'alpha')) // Both test are required to be compatible with all browsers
159{
160	$year = '';
161	$month = '';
162	$search_ref = '';
163	$search_movement = "";
164	$search_type_mouvement = "";
165	$search_inventorycode = "";
166	$search_product_ref = "";
167	$search_product = "";
168	$search_warehouse = "";
169	$search_user = "";
170	$search_batch = "";
171	$search_qty = '';
172	$sall = "";
173	$toselect = '';
174	$search_array_options = array();
175}
176
177if (empty($reshook))
178{
179	$error = 0;
180
181	$backurlforlist = dol_buildpath('/mrp/mo_list.php', 1);
182
183	if (empty($backtopage) || ($cancel && empty($id))) {
184		//var_dump($backurlforlist);exit;
185		if (empty($id) && (($action != 'add' && $action != 'create') || $cancel)) $backtopage = $backurlforlist;
186		else $backtopage = DOL_URL_ROOT.'/mrp/mo_production.php?id='.($id > 0 ? $id : '__ID__');
187	}
188	$triggermodname = 'MRP_MO_MODIFY'; // Name of trigger action code to execute when we modify record
189
190	// Actions cancel, add, update, delete or clone
191	include DOL_DOCUMENT_ROOT.'/core/actions_addupdatedelete.inc.php';
192
193	// Actions when linking object each other
194	include DOL_DOCUMENT_ROOT.'/core/actions_dellink.inc.php';
195
196	// Actions when printing a doc from card
197	include DOL_DOCUMENT_ROOT.'/core/actions_printing.inc.php';
198
199	// Actions to send emails
200	$triggersendname = 'MO_SENTBYMAIL';
201	$autocopy = 'MAIN_MAIL_AUTOCOPY_MO_TO';
202	$trackid = 'mo'.$object->id;
203	include DOL_DOCUMENT_ROOT.'/core/actions_sendmails.inc.php';
204
205	// Action to move up and down lines of object
206	//include DOL_DOCUMENT_ROOT.'/core/actions_lineupdown.inc.php';	// Must be include, not include_once
207
208	if ($action == 'set_thirdparty' && $permissiontoadd)
209	{
210		$object->setValueFrom('fk_soc', GETPOST('fk_soc', 'int'), '', '', 'date', '', $user, 'MO_MODIFY');
211	}
212	if ($action == 'classin' && $permissiontoadd)
213	{
214		$object->setProject(GETPOST('projectid', 'int'));
215	}
216
217	if ($action == 'confirm_reopen') {
218		$result = $object->setStatut($object::STATUS_INPROGRESS, 0, '', 'MRP_REOPEN');
219	}
220}
221
222
223
224/*
225 * View
226 */
227
228$form = new Form($db);
229$formproject = new FormProjets($db);
230$formproduct = new FormProduct($db);
231$productstatic = new Product($db);
232$productlot = new ProductLot($db);
233$warehousestatic = new Entrepot($db);
234$userstatic = new User($db);
235
236llxHeader('', $langs->trans('Mo'), '');
237
238// Part to show record
239if ($object->id > 0 && (empty($action) || ($action != 'edit' && $action != 'create')))
240{
241	$res = $object->fetch_thirdparty();
242	$res = $object->fetch_optionals();
243
244	$head = moPrepareHead($object);
245
246	print dol_get_fiche_head($head, 'stockmovement', $langs->trans("ManufacturingOrder"), -1, $object->picto);
247
248	$formconfirm = '';
249
250	// Confirmation to delete
251	if ($action == 'delete')
252	{
253		$formconfirm = $form->formconfirm($_SERVER["PHP_SELF"].'?id='.$object->id, $langs->trans('DeleteMo'), $langs->trans('ConfirmDeleteMo'), 'confirm_delete', '', 0, 1);
254	}
255	// Confirmation to delete line
256	if ($action == 'deleteline')
257	{
258		$formconfirm = $form->formconfirm($_SERVER["PHP_SELF"].'?id='.$object->id.'&lineid='.$lineid, $langs->trans('DeleteLine'), $langs->trans('ConfirmDeleteLine'), 'confirm_deleteline', '', 0, 1);
259	}
260	// Clone confirmation
261	if ($action == 'clone') {
262		// Create an array for form
263		$formquestion = array();
264		$formconfirm = $form->formconfirm($_SERVER["PHP_SELF"].'?id='.$object->id, $langs->trans('ToClone'), $langs->trans('ConfirmCloneMo', $object->ref), 'confirm_clone', $formquestion, 'yes', 1);
265	}
266
267	// Confirmation of action xxxx
268	if ($action == 'xxx')
269	{
270		$formquestion = array();
271		/*
272		$forcecombo=0;
273		if ($conf->browser->name == 'ie') $forcecombo = 1;	// There is a bug in IE10 that make combo inside popup crazy
274	    $formquestion = array(
275	        // 'text' => $langs->trans("ConfirmClone"),
276	        // array('type' => 'checkbox', 'name' => 'clone_content', 'label' => $langs->trans("CloneMainAttributes"), 'value' => 1),
277	        // array('type' => 'checkbox', 'name' => 'update_prices', 'label' => $langs->trans("PuttingPricesUpToDate"), 'value' => 1),
278	        // array('type' => 'other',    'name' => 'idwarehouse',   'label' => $langs->trans("SelectWarehouseForStockDecrease"), 'value' => $formproduct->selectWarehouses(GETPOST('idwarehouse')?GETPOST('idwarehouse'):'ifone', 'idwarehouse', '', 1, 0, 0, '', 0, $forcecombo))
279        );
280	    */
281		$formconfirm = $form->formconfirm($_SERVER["PHP_SELF"].'?id='.$object->id, $langs->trans('XXX'), $text, 'confirm_xxx', $formquestion, 0, 1, 220);
282	}
283
284	// Call Hook formConfirm
285	$parameters = array('formConfirm' => $formconfirm, 'lineid' => $lineid);
286	$reshook = $hookmanager->executeHooks('formConfirm', $parameters, $object, $action); // Note that $action and $object may have been modified by hook
287	if (empty($reshook)) $formconfirm .= $hookmanager->resPrint;
288	elseif ($reshook > 0) $formconfirm = $hookmanager->resPrint;
289
290	// Print form confirm
291	print $formconfirm;
292
293
294	// Object card
295	// ------------------------------------------------------------
296	$linkback = '<a href="'.dol_buildpath('/mrp/mo_list.php', 1).'?restore_lastsearch_values=1'.(!empty($socid) ? '&socid='.$socid : '').'">'.$langs->trans("BackToList").'</a>';
297
298	$morehtmlref = '<div class="refidno">';
299	/*
300	// Ref bis
301	$morehtmlref.=$form->editfieldkey("RefBis", 'ref_client', $object->ref_client, $object, $user->rights->mrp->creer, 'string', '', 0, 1);
302	$morehtmlref.=$form->editfieldval("RefBis", 'ref_client', $object->ref_client, $object, $user->rights->mrp->creer, 'string', '', null, null, '', 1);*/
303	// Thirdparty
304	$morehtmlref .= $langs->trans('ThirdParty').' : '.(is_object($object->thirdparty) ? $object->thirdparty->getNomUrl(1) : '');
305	// Project
306	if (!empty($conf->projet->enabled))
307	{
308		$langs->load("projects");
309		$morehtmlref .= '<br>'.$langs->trans('Project').' ';
310		if ($permissiontoadd)
311		{
312			if ($action != 'classify')
313				$morehtmlref .= '<a class="editfielda" href="'.$_SERVER['PHP_SELF'].'?action=classify&amp;id='.$object->id.'">'.img_edit($langs->transnoentitiesnoconv('SetProject')).'</a> : ';
314			if ($action == 'classify') {
315				//$morehtmlref.=$form->form_project($_SERVER['PHP_SELF'] . '?id=' . $object->id, $object->fk_soc, $object->fk_project, 'projectid', 0, 0, 1, 1);
316				$morehtmlref .= '<form method="post" action="'.$_SERVER['PHP_SELF'].'?id='.$object->id.'">';
317				$morehtmlref .= '<input type="hidden" name="action" value="classin">';
318				$morehtmlref .= '<input type="hidden" name="token" value="'.newToken().'">';
319				$morehtmlref .= $formproject->select_projects($object->fk_soc, $object->fk_project, 'projectid', 0, 0, 1, 0, 1, 0, 0, '', 1);
320				$morehtmlref .= '<input type="submit" class="button valignmiddle" value="'.$langs->trans("Modify").'">';
321				$morehtmlref .= '</form>';
322			} else {
323				$morehtmlref .= $form->form_project($_SERVER['PHP_SELF'].'?id='.$object->id, $object->fk_soc, $object->fk_project, 'none', 0, 0, 0, 1);
324			}
325		} else {
326			if (!empty($object->fk_project)) {
327				$proj = new Project($db);
328				$proj->fetch($object->fk_project);
329				$morehtmlref .= $proj->getNomUrl();
330			} else {
331				$morehtmlref .= '';
332			}
333		}
334	}
335	$morehtmlref .= '</div>';
336
337
338	dol_banner_tab($object, 'ref', $linkback, 1, 'ref', 'ref', $morehtmlref);
339
340
341	print '<div class="fichecenter">';
342	print '<div class="fichehalfleft">';
343	print '<div class="underbanner clearboth"></div>';
344	print '<table class="border centpercent tableforfield">'."\n";
345
346	// Common attributes
347	$keyforbreak = 'fk_warehouse';
348	unset($object->fields['fk_project']);
349	unset($object->fields['fk_soc']);
350	include DOL_DOCUMENT_ROOT.'/core/tpl/commonfields_view.tpl.php';
351
352	// Other attributes
353	include DOL_DOCUMENT_ROOT.'/core/tpl/extrafields_view.tpl.php';
354
355	print '</table>';
356	print '</div>';
357	print '</div>';
358
359	print '<div class="clearboth"></div>';
360
361	print dol_get_fiche_end();
362
363	/*
364		print '<div class="tabsAction">';
365
366		$parameters = array();
367		// Note that $action and $object may be modified by hook
368		$reshook = $hookmanager->executeHooks('addMoreActionsButtons', $parameters, $object, $action);
369		if (empty($reshook)) {
370			// Cancel - Reopen
371			if ($permissiontoadd)
372			{
373				if ($object->status == $object::STATUS_VALIDATED || $object->status == $object::STATUS_INPROGRESS)
374				{
375					print '<a class="butActionDelete" href="'.$_SERVER["PHP_SELF"].'?id='.$object->id.'&action=confirm_close&confirm=yes">'.$langs->trans("Cancel").'</a>'."\n";
376				}
377
378				if ($object->status == $object::STATUS_CANCELED)
379				{
380					print '<a class="butAction" href="'.$_SERVER["PHP_SELF"].'?id='.$object->id.'&action=confirm_reopen&confirm=yes">'.$langs->trans("Re-Open").'</a>'."\n";
381				}
382
383				if ($object->status == $object::STATUS_PRODUCED) {
384					if ($permissiontoproduce) {
385						print '<a class="butAction" href="'.$_SERVER["PHP_SELF"].'?id='.$object->id.'&action=confirm_reopen">'.$langs->trans('ReOpen').'</a>';
386					} else {
387						print '<a class="butActionRefused classfortooltip" href="#" title="'.$langs->trans("NotEnoughPermissions").'">'.$langs->trans('ReOpen').'</a>';
388					}
389				}
390			}
391		}
392
393		print '</div>';
394	*/
395
396
397	$sql = "SELECT p.rowid, p.ref as product_ref, p.label as produit, p.tobatch, p.fk_product_type as type, p.entity,";
398	$sql .= " e.ref as warehouse_ref, e.rowid as entrepot_id, e.lieu,";
399	$sql .= " m.rowid as mid, m.value as qty, m.datem, m.fk_user_author, m.label, m.inventorycode, m.fk_origin, m.origintype,";
400	$sql .= " m.batch, m.price,";
401	$sql .= " m.type_mouvement,";
402	$sql .= " pl.rowid as lotid, pl.eatby, pl.sellby,";
403	$sql .= " u.login, u.photo, u.lastname, u.firstname";
404	// Add fields from extrafields
405	if (!empty($extrafields->attributes[$objectlist->table_element]['label'])) {
406		foreach ($extrafields->attributes[$objectlist->table_element]['label'] as $key => $val) $sql .= ($extrafields->attributes[$objectlist->table_element]['type'][$key] != 'separate' ? ", ef.".$key.' as options_'.$key : '');
407	}
408	// Add fields from hooks
409	$parameters = array();
410	$reshook = $hookmanager->executeHooks('printFieldListSelect', $parameters); // Note that $action and $objectlist may have been modified by hook
411	$sql .= $hookmanager->resPrint;
412	$sql .= " FROM ".MAIN_DB_PREFIX."entrepot as e,";
413	$sql .= " ".MAIN_DB_PREFIX."product as p,";
414	$sql .= " ".MAIN_DB_PREFIX."stock_mouvement as m";
415	if (is_array($extrafields->attributes[$objectlist->table_element]['label']) && count($extrafields->attributes[$objectlist->table_element]['label'])) $sql .= " LEFT JOIN ".MAIN_DB_PREFIX.$objectlist->table_element."_extrafields as ef on (m.rowid = ef.fk_object)";
416	$sql .= " LEFT JOIN ".MAIN_DB_PREFIX."user as u ON m.fk_user_author = u.rowid";
417	$sql .= " LEFT JOIN ".MAIN_DB_PREFIX."product_lot as pl ON m.batch = pl.batch AND m.fk_product = pl.fk_product";
418	$sql .= " WHERE m.fk_product = p.rowid";
419	$sql .= " AND m.origintype = 'mo' AND m.fk_origin = ".(int) $object->id;
420	if ($msid > 0) $sql .= " AND m.rowid = ".$msid;
421	$sql .= " AND m.fk_entrepot = e.rowid";
422	$sql .= " AND e.entity IN (".getEntity('stock').")";
423	if (empty($conf->global->STOCK_SUPPORTS_SERVICES)) $sql .= " AND p.fk_product_type = 0";
424	$sql .= dolSqlDateFilter('m.datem', 0, $month, $year);
425	if (!empty($search_ref))			$sql .= natural_search('m.rowid', $search_ref, 1);
426	if (!empty($search_movement))      $sql .= natural_search('m.label', $search_movement);
427	if (!empty($search_inventorycode)) $sql .= natural_search('m.inventorycode', $search_inventorycode);
428	if (!empty($search_product_ref))   $sql .= natural_search('p.ref', $search_product_ref);
429	if (!empty($search_product))       $sql .= natural_search('p.label', $search_product);
430	if ($search_warehouse != '' && $search_warehouse != '-1')          $sql .= natural_search('e.rowid', $search_warehouse, 2);
431	if (!empty($search_user))          $sql .= natural_search('u.login', $search_user);
432	if (!empty($search_batch))         $sql .= natural_search('m.batch', $search_batch);
433	if ($search_qty != '')				$sql .= natural_search('m.value', $search_qty, 1);
434	if ($search_type_mouvement != '' && $search_type_mouvement != '-1')	$sql .= natural_search('m.type_mouvement', $search_type_mouvement, 2);
435	// Add where from extra fields
436	include DOL_DOCUMENT_ROOT.'/core/tpl/extrafields_list_search_sql.tpl.php';
437	// Add where from hooks
438	$parameters = array();
439	$reshook = $hookmanager->executeHooks('printFieldListWhere', $parameters); // Note that $action and $objectlist may have been modified by hook
440	$sql .= $hookmanager->resPrint;
441	$sql .= $db->order($sortfield, $sortorder);
442
443	$nbtotalofrecords = '';
444	if (empty($conf->global->MAIN_DISABLE_FULL_SCANLIST))
445	{
446		$result = $db->query($sql);
447		$nbtotalofrecords = $db->num_rows($result);
448		if (($page * $limit) > $nbtotalofrecords)	// if total resultset is smaller then paging size (filtering), goto and load page 0
449		{
450			$page = 0;
451			$offset = 0;
452		}
453	}
454	$sql .= $db->plimit($limit + 1, $offset);
455
456	$param = '';
457	if (!empty($contextpage) && $contextpage != $_SERVER["PHP_SELF"]) $param .= '&contextpage='.urlencode($contextpage);
458	if ($limit > 0 && $limit != $conf->liste_limit) $param .= '&limit='.urlencode($limit);
459	if ($id > 0)                 $param .= '&id='.urlencode($id);
460	if ($search_movement)        $param .= '&search_movement='.urlencode($search_movement);
461	if ($search_inventorycode)   $param .= '&search_inventorycode='.urlencode($search_inventorycode);
462	if ($search_type_mouvement)	 $param .= '&search_type_mouvement='.urlencode($search_type_mouvement);
463	if ($search_product_ref)     $param .= '&search_product_ref='.urlencode($search_product_ref);
464	if ($search_product)         $param .= '&search_product='.urlencode($search_product);
465	if ($search_batch)           $param .= '&search_batch='.urlencode($search_batch);
466	if ($search_warehouse > 0)   $param .= '&search_warehouse='.urlencode($search_warehouse);
467	if ($search_user)            $param .= '&search_user='.urlencode($search_user);
468
469	// Add $param from extra fields
470	include DOL_DOCUMENT_ROOT.'/core/tpl/extrafields_list_search_param.tpl.php';
471
472	// List of mass actions available
473	$arrayofmassactions = array(
474		//    'presend'=>$langs->trans("SendByMail"),
475		//    'builddoc'=>$langs->trans("PDFMerge"),
476	);
477	//if ($user->rights->stock->supprimer) $arrayofmassactions['predelete']='<span class="fa fa-trash paddingrightonly"></span>'.$langs->trans("Delete");
478	if (in_array($massaction, array('presend', 'predelete'))) $arrayofmassactions = array();
479	$massactionbutton = $form->selectMassAction('', $arrayofmassactions);
480
481	print '<form method="POST" action="'.$_SERVER["PHP_SELF"].'">';
482	if ($optioncss != '') print '<input type="hidden" name="optioncss" value="'.$optioncss.'">';
483	print '<input type="hidden" name="token" value="'.newToken().'">';
484	print '<input type="hidden" name="formfilteraction" id="formfilteraction" value="list">';
485	print '<input type="hidden" name="action" value="list">';
486	print '<input type="hidden" name="sortfield" value="'.$sortfield.'">';
487	print '<input type="hidden" name="sortorder" value="'.$sortorder.'">';
488	print '<input type="hidden" name="page" value="'.$page.'">';
489	print '<input type="hidden" name="contextpage" value="'.$contextpage.'">';
490	if ($id > 0) print '<input type="hidden" name="id" value="'.$id.'">';
491
492	if ($id > 0) print_barre_liste($texte, $page, $_SERVER["PHP_SELF"], $param, $sortfield, $sortorder, $massactionbutton, $num, $nbtotalofrecords, '', 0, '', '', $limit);
493	else print_barre_liste($texte, $page, $_SERVER["PHP_SELF"], $param, $sortfield, $sortorder, $massactionbutton, $num, $nbtotalofrecords, 'generic', 0, '', '', $limit);
494
495	$moreforfilter = '';
496
497	$parameters = array();
498	$reshook = $hookmanager->executeHooks('printFieldPreListTitle', $parameters); // Note that $action and $object may have been modified by hook
499	if (empty($reshook)) $moreforfilter .= $hookmanager->resPrint;
500	else $moreforfilter = $hookmanager->resPrint;
501
502	if (!empty($moreforfilter))
503	{
504		print '<div class="liste_titre liste_titre_bydiv centpercent">';
505		print $moreforfilter;
506		print '</div>';
507	}
508
509	$varpage = empty($contextpage) ? $_SERVER["PHP_SELF"] : $contextpage;
510	$selectedfields = $form->multiSelectArrayWithCheckbox('selectedfields', $arrayfields, $varpage); // This also change content of $arrayfields
511
512	print '<div class="div-table-responsive">';
513	print '<table class="tagtable liste'.($moreforfilter ? " listwithfilterbefore" : "").'">'."\n";
514
515	// Fields title search
516	print '<tr class="liste_titre_filter">';
517	if (!empty($arrayfields['m.rowid']['checked']))
518	{
519		// Ref
520		print '<td class="liste_titre left">';
521		print '<input class="flat maxwidth25" type="text" name="search_ref" value="'.dol_escape_htmltag($search_ref).'">';
522		print '</td>';
523	}
524	if (!empty($arrayfields['m.datem']['checked']))
525	{
526		print '<td class="liste_titre nowraponall">';
527		print '<input class="flat" type="text" size="2" maxlength="2" placeholder="'.dol_escape_htmltag($langs->trans("Month")).'" name="month" value="'.$month.'">';
528		if (empty($conf->productbatch->enabled)) print '&nbsp;';
529		//else print '<br>';
530		$syear = $year ? $year : -1;
531		print '<input class="flat maxwidth50" type="text" maxlength="4" placeholder="'.dol_escape_htmltag($langs->trans("Year")).'" name="year" value="'.($syear > 0 ? $syear : '').'">';
532		//print $formother->selectyear($syear,'year',1, 20, 5);
533		print '</td>';
534	}
535	if (!empty($arrayfields['p.ref']['checked']))
536	{
537		// Product Ref
538		print '<td class="liste_titre left">';
539		print '<input class="flat maxwidth75" type="text" name="search_product_ref" value="'.dol_escape_htmltag($idproduct ? $product->ref : $search_product_ref).'">';
540		print '</td>';
541	}
542	if (!empty($arrayfields['p.label']['checked']))
543	{
544		// Product label
545		print '<td class="liste_titre left">';
546		print '<input class="flat maxwidth100" type="text" name="search_product" value="'.dol_escape_htmltag($idproduct ? $product->label : $search_product).'">';
547		print '</td>';
548	}
549	// Batch
550	if (!empty($arrayfields['m.batch']['checked']))
551	{
552		print '<td class="liste_titre center"><input class="flat maxwidth75" type="text" name="search_batch" value="'.dol_escape_htmltag($search_batch).'"></td>';
553	}
554	if (!empty($arrayfields['pl.eatby']['checked']))
555	{
556		print '<td class="liste_titre left">';
557		print '</td>';
558	}
559	if (!empty($arrayfields['pl.sellby']['checked']))
560	{
561		print '<td class="liste_titre left">';
562		print '</td>';
563	}
564	// Warehouse
565	if (!empty($arrayfields['e.ref']['checked']))
566	{
567		print '<td class="liste_titre maxwidthonsmartphone left">';
568		//print '<input class="flat" type="text" size="8" name="search_warehouse" value="'.($search_warehouse).'">';
569		print $formproduct->selectWarehouses($search_warehouse, 'search_warehouse', 'warehouseopen,warehouseinternal', 1, 0, 0, '', 0, 0, null, 'maxwidth200');
570		print '</td>';
571	}
572	if (!empty($arrayfields['m.fk_user_author']['checked']))
573	{
574		// Author
575		print '<td class="liste_titre left">';
576		print '<input class="flat" type="text" size="6" name="search_user" value="'.dol_escape_htmltag($search_user).'">';
577		print '</td>';
578	}
579	if (!empty($arrayfields['m.inventorycode']['checked']))
580	{
581		// Inventory code
582		print '<td class="liste_titre left">';
583		print '<input class="flat" type="text" size="4" name="search_inventorycode" value="'.dol_escape_htmltag($search_inventorycode).'">';
584		print '</td>';
585	}
586	if (!empty($arrayfields['m.label']['checked']))
587	{
588		// Label of movement
589		print '<td class="liste_titre left">';
590		print '<input class="flat" type="text" size="8" name="search_movement" value="'.dol_escape_htmltag($search_movement).'">';
591		print '</td>';
592	}
593	if (!empty($arrayfields['m.type_mouvement']['checked']))
594	{
595		// Type of movement
596		print '<td class="liste_titre center">';
597		//print '<input class="flat" type="text" size="3" name="search_type_mouvement" value="'.dol_escape_htmltag($search_type_mouvement).'">';
598		print '<select id="search_type_mouvement" name="search_type_mouvement" class="maxwidth150">';
599		print '<option value="" '.(($search_type_mouvement == "") ? 'selected="selected"' : '').'>&nbsp;</option>';
600		print '<option value="0" '.(($search_type_mouvement == "0") ? 'selected="selected"' : '').'>'.$langs->trans('StockIncreaseAfterCorrectTransfer').'</option>';
601		print '<option value="1" '.(($search_type_mouvement == "1") ? 'selected="selected"' : '').'>'.$langs->trans('StockDecreaseAfterCorrectTransfer').'</option>';
602		print '<option value="2" '.(($search_type_mouvement == "2") ? 'selected="selected"' : '').'>'.$langs->trans('StockDecrease').'</option>';
603		print '<option value="3" '.(($search_type_mouvement == "3") ? 'selected="selected"' : '').'>'.$langs->trans('StockIncrease').'</option>';
604		print '</select>';
605		print ajax_combobox('search_type_mouvement');
606		// TODO: add new function $formentrepot->selectTypeOfMovement(...) like
607		// print $formproduct->selectWarehouses($search_warehouse, 'search_warehouse', 'warehouseopen,warehouseinternal', 1, 0, 0, '', 0, 0, null, 'maxwidth200');
608		print '</td>';
609	}
610	if (!empty($arrayfields['origin']['checked']))
611	{
612		// Origin of movement
613		print '<td class="liste_titre left">';
614		print '&nbsp; ';
615		print '</td>';
616	}
617	if (!empty($arrayfields['m.value']['checked']))
618	{
619		// Qty
620		print '<td class="liste_titre right">';
621		print '<input class="flat" type="text" size="4" name="search_qty" value="'.dol_escape_htmltag($search_qty).'">';
622		print '</td>';
623	}
624	if (!empty($arrayfields['m.price']['checked']))
625	{
626		// Price
627		print '<td class="liste_titre left">';
628		print '&nbsp; ';
629		print '</td>';
630	}
631
632
633	// Extra fields
634	include DOL_DOCUMENT_ROOT.'/core/tpl/extrafields_list_search_input.tpl.php';
635
636	// Fields from hook
637	$parameters = array('arrayfields'=>$arrayfields);
638	$reshook = $hookmanager->executeHooks('printFieldListOption', $parameters); // Note that $action and $object may have been modified by hook
639	print $hookmanager->resPrint;
640	// Date creation
641	if (!empty($arrayfields['m.datec']['checked']))
642	{
643		print '<td class="liste_titre">';
644		print '</td>';
645	}
646	// Date modification
647	if (!empty($arrayfields['m.tms']['checked']))
648	{
649		print '<td class="liste_titre">';
650		print '</td>';
651	}
652	// Actions
653	print '<td class="liste_titre maxwidthsearch">';
654	$searchpicto = $form->showFilterAndCheckAddButtons(0);
655	print $searchpicto;
656	print '</td>';
657	print "</tr>\n";
658
659	print '<tr class="liste_titre">';
660	if (!empty($arrayfields['m.rowid']['checked']))
661		print_liste_field_titre($arrayfields['m.rowid']['label'], $_SERVER["PHP_SELF"], 'm.rowid', '', $param, '', $sortfield, $sortorder);
662	if (!empty($arrayfields['m.datem']['checked']))
663		print_liste_field_titre($arrayfields['m.datem']['label'], $_SERVER["PHP_SELF"], 'm.datem', '', $param, '', $sortfield, $sortorder);
664	if (!empty($arrayfields['p.ref']['checked']))
665		print_liste_field_titre($arrayfields['p.ref']['label'], $_SERVER["PHP_SELF"], 'p.ref', '', $param, '', $sortfield, $sortorder);
666	if (!empty($arrayfields['p.label']['checked']))
667		print_liste_field_titre($arrayfields['p.label']['label'], $_SERVER["PHP_SELF"], 'p.label', '', $param, '', $sortfield, $sortorder);
668	if (!empty($arrayfields['m.batch']['checked']))
669		print_liste_field_titre($arrayfields['m.batch']['label'], $_SERVER["PHP_SELF"], 'm.batch', '', $param, '', $sortfield, $sortorder, 'center ');
670	if (!empty($arrayfields['pl.eatby']['checked']))
671		print_liste_field_titre($arrayfields['pl.eatby']['label'], $_SERVER["PHP_SELF"], 'pl.eatby', '', $param, '', $sortfield, $sortorder, 'center ');
672	if (!empty($arrayfields['pl.sellby']['checked']))
673		print_liste_field_titre($arrayfields['pl.sellby']['label'], $_SERVER["PHP_SELF"], 'pl.sellby', '', $param, '', $sortfield, $sortorder, 'center ');
674	if (!empty($arrayfields['e.ref']['checked'])) {
675		// We are on a specific warehouse card, no filter on other should be possible
676		print_liste_field_titre($arrayfields['e.ref']['label'], $_SERVER["PHP_SELF"], "e.ref", "", $param, "", $sortfield, $sortorder);
677	}
678	if (!empty($arrayfields['m.fk_user_author']['checked']))
679		print_liste_field_titre($arrayfields['m.fk_user_author']['label'], $_SERVER["PHP_SELF"], "m.fk_user_author", "", $param, "", $sortfield, $sortorder);
680	if (!empty($arrayfields['m.inventorycode']['checked']))
681		print_liste_field_titre($arrayfields['m.inventorycode']['label'], $_SERVER["PHP_SELF"], "m.inventorycode", "", $param, "", $sortfield, $sortorder);
682	if (!empty($arrayfields['m.label']['checked']))
683		print_liste_field_titre($arrayfields['m.label']['label'], $_SERVER["PHP_SELF"], "m.label", "", $param, "", $sortfield, $sortorder);
684	if (!empty($arrayfields['m.type_mouvement']['checked']))
685		print_liste_field_titre($arrayfields['m.type_mouvement']['label'], $_SERVER["PHP_SELF"], "m.type_mouvement", "", $param, '', $sortfield, $sortorder, 'center ');
686	if (!empty($arrayfields['origin']['checked']))
687		print_liste_field_titre($arrayfields['origin']['label'], $_SERVER["PHP_SELF"], "", "", $param, "", $sortfield, $sortorder);
688	if (!empty($arrayfields['m.value']['checked']))
689		print_liste_field_titre($arrayfields['m.value']['label'], $_SERVER["PHP_SELF"], "m.value", "", $param, '', $sortfield, $sortorder, 'right ');
690	if (!empty($arrayfields['m.price']['checked']))
691		print_liste_field_titre($arrayfields['m.price']['label'], $_SERVER["PHP_SELF"], "m.price", "", $param, '', $sortfield, $sortorder, 'right ');
692
693	// Extra fields
694	include DOL_DOCUMENT_ROOT.'/core/tpl/extrafields_list_search_title.tpl.php';
695
696	// Hook fields
697	$parameters = array('arrayfields' => $arrayfields, 'param' => $param, 'sortfield' => $sortfield, 'sortorder' => $sortorder);
698	$reshook = $hookmanager->executeHooks('printFieldListTitle', $parameters); // Note that $action and $object may have been modified by hook
699	print $hookmanager->resPrint;
700	if (!empty($arrayfields['m.datec']['checked'])) {
701		print_liste_field_titre($arrayfields['p.datec']['label'], $_SERVER["PHP_SELF"], "p.datec", "", $param, '', $sortfield, $sortorder, 'center nowrap ');
702	}
703	if (!empty($arrayfields['m.tms']['checked'])) {
704		print_liste_field_titre($arrayfields['p.tms']['label'], $_SERVER["PHP_SELF"], "p.tms", "", $param, '', $sortfield, $sortorder, 'center nowrap ');
705	}
706	print_liste_field_titre($selectedfields, $_SERVER["PHP_SELF"], "", '', '', '', $sortfield, $sortorder, 'center maxwidthsearch ');
707	print "</tr>\n";
708
709	$resql = $db->query($sql);
710	if (!$resql) {
711		dol_print_error($db);
712	}
713	$num = $db->num_rows($resql);
714
715	$i = 0;
716	while ($i < ($limit ? min($num, $limit) : $num)) {
717		$objp = $db->fetch_object($resql);
718
719		$userstatic->id = $objp->fk_user_author;
720		$userstatic->login = $objp->login;
721		$userstatic->lastname = $objp->lastname;
722		$userstatic->firstname = $objp->firstname;
723		$userstatic->photo = $objp->photo;
724
725		$productstatic->id = $objp->rowid;
726		$productstatic->ref = $objp->product_ref;
727		$productstatic->label = $objp->produit;
728		$productstatic->type = $objp->type;
729		$productstatic->entity = $objp->entity;
730		$productstatic->status_batch = $objp->tobatch;
731
732		$productlot->id = $objp->lotid;
733		$productlot->batch = $objp->batch;
734		$productlot->eatby = $objp->eatby;
735		$productlot->sellby = $objp->sellby;
736
737		$warehousestatic->id = $objp->entrepot_id;
738		$warehousestatic->libelle = $objp->warehouse_ref; // deprecated
739		$warehousestatic->label = $objp->warehouse_ref;
740		$warehousestatic->lieu = $objp->lieu;
741
742		if (!empty($objp->fk_origin)) {
743			$origin = $objectlist->get_origin($objp->fk_origin, $objp->origintype);
744		} else {
745			$origin = '';
746		}
747
748		print '<tr class="oddeven">';
749		// Id movement
750		if (!empty($arrayfields['m.rowid']['checked'])) {
751			// This is primary not movement id
752			print '<td>'.$objp->mid.'</td>';
753		}
754		if (!empty($arrayfields['m.datem']['checked'])) {
755			// Date
756			print '<td>'.dol_print_date($db->jdate($objp->datem), 'dayhour').'</td>';
757		}
758		if (!empty($arrayfields['p.ref']['checked'])) {
759			// Product ref
760			print '<td class="nowraponall">';
761			print $productstatic->getNomUrl(1, 'stock', 16);
762			print "</td>\n";
763		}
764		if (!empty($arrayfields['p.label']['checked'])) {
765			// Product label
766			print '<td>';
767			/*
768			 * $productstatic->id=$objp->rowid;
769			 * $productstatic->ref=$objp->produit;
770			 * $productstatic->type=$objp->type;
771			 * print $productstatic->getNomUrl(1,'',16);
772			 */
773			print $productstatic->label;
774			print "</td>\n";
775		}
776		if (!empty($arrayfields['m.batch']['checked'])) {
777			print '<td class="center nowraponall">';
778			if ($productlot->id > 0)
779				print $productlot->getNomUrl(1);
780			else print $productlot->batch; // the id may not be defined if movement was entered when lot was not saved or if lot was removed after movement.
781			print '</td>';
782		}
783		if (!empty($arrayfields['pl.eatby']['checked'])) {
784			print '<td class="center">'.dol_print_date($objp->eatby, 'day').'</td>';
785		}
786		if (!empty($arrayfields['pl.sellby']['checked'])) {
787			print '<td class="center">'.dol_print_date($objp->sellby, 'day').'</td>';
788		}
789		// Warehouse
790		if (!empty($arrayfields['e.ref']['checked'])) {
791			print '<td>';
792			print $warehousestatic->getNomUrl(1);
793			print "</td>\n";
794		}
795		// Author
796		if (!empty($arrayfields['m.fk_user_author']['checked'])) {
797			print '<td class="tdoverflowmax100">';
798			print $userstatic->getNomUrl(-1);
799			print "</td>\n";
800		}
801		if (!empty($arrayfields['m.inventorycode']['checked'])) {
802			// Inventory code
803			print '<td>';
804			//print '<a href="' . DOL_URL_ROOT . '/product/stock/movement_card.php' . '?id=' . $objp->entrepot_id . '&amp;search_inventorycode=' . $objp->inventorycode . '&amp;search_type_mouvement=' . $objp->type_mouvement . '">';
805			print $objp->inventorycode;
806			//print '</a>';
807			print '</td>';
808		}
809		if (!empty($arrayfields['m.label']['checked'])) {
810			// Label of movement
811			print '<td class="tdoverflowmax100aaa">'.$objp->label.'</td>';
812		}
813		if (!empty($arrayfields['m.type_mouvement']['checked'])) {
814			// Type of movement
815			switch ($objp->type_mouvement) {
816				case "0":
817					print '<td class="center">'.$langs->trans('StockIncreaseAfterCorrectTransfer').'</td>';
818					break;
819				case "1":
820					print '<td class="center">'.$langs->trans('StockDecreaseAfterCorrectTransfer').'</td>';
821					break;
822				case "2":
823					print '<td class="center">'.$langs->trans('StockDecrease').'</td>';
824					break;
825				case "3":
826					print '<td class="center">'.$langs->trans('StockIncrease').'</td>';
827					break;
828			}
829		}
830		if (!empty($arrayfields['origin']['checked'])) {
831			// Origin of movement
832			print '<td class="nowraponall">'.$origin.'</td>';
833		}
834		if (!empty($arrayfields['m.value']['checked'])) {
835			// Qty
836			print '<td class="right">';
837			if ($objp->qt > 0)
838				print '+';
839			print $objp->qty;
840			print '</td>';
841		}
842		if (!empty($arrayfields['m.price']['checked'])) {
843			// Price
844			print '<td class="right">';
845			if ($objp->price != 0)
846				print price($objp->price);
847			print '</td>';
848		}
849		// Action column
850		print '<td class="nowrap center">';
851		if ($massactionbutton || $massaction) // If we are in select mode (massactionbutton defined) or if we have already selected and sent an action ($massaction) defined
852		{
853			$selected = 0;
854			if (in_array($obj->rowid, $arrayofselected))
855				$selected = 1;
856			print '<input id="cb'.$obj->rowid.'" class="flat checkforselect" type="checkbox" name="toselect[]" value="'.$obj->rowid.'"'.($selected ? ' checked="checked"' : '').'>';
857		}
858		print '</td>';
859		if (!$i)
860			$totalarray['nbfield']++;
861
862		print "</tr>\n";
863		$i++;
864	}
865	$db->free($resql);
866
867	print "</table>";
868	print '</div>';
869	print "</form>";
870}
871
872// End of page
873llxFooter();
874$db->close();
875