1<?php
2/* Copyright (C) 2007-2019  Laurent Destailleur <eldy@users.sourceforge.net>
3 * Copyright (C) 2014-2016  Juanjo Menent       <jmenent@2byte.es>
4 * Copyright (C) 2015       Florian Henry       <florian.henry@open-concept.pro>
5 * Copyright (C) 2015       Raphaël Doursenaud  <rdoursenaud@gpcsolutions.fr>
6 *
7 * This program is free software; you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License as published by
9 * the Free Software Foundation; either version 3 of the License, or
10 * (at your option) any later version.
11 *
12 * This program is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15 * GNU General Public License for more details.
16 *
17 * You should have received a copy of the GNU General Public License
18 * along with this program. If not, see <https://www.gnu.org/licenses/>.
19 */
20
21/**
22 * \file        product/inventory/class/inventory.class.php
23 * \ingroup     inventory
24 * \brief       This file is a CRUD class file for Inventory (Create/Read/Update/Delete)
25 */
26
27// Put here all includes required by your class file
28require_once DOL_DOCUMENT_ROOT.'/core/class/commonobject.class.php';
29//require_once DOL_DOCUMENT_ROOT . '/societe/class/societe.class.php';
30//require_once DOL_DOCUMENT_ROOT . '/product/class/product.class.php';
31
32/**
33 * Class for Inventory
34 */
35class Inventory extends CommonObject
36{
37	/**
38	 * @var string ID to identify managed object
39	 */
40	public $element = 'inventory';
41
42	/**
43	 * @var string Name of table without prefix where object is stored
44	 */
45	public $table_element = 'inventory';
46
47	/**
48	 * @var array  Does inventory support multicompany module ? 0=No test on entity, 1=Test with field entity, 2=Test with link by societe
49	 */
50	public $ismultientitymanaged = 1;
51
52	/**
53	 * @var int  Does object support extrafields ? 0=No, 1=Yes
54	 */
55	public $isextrafieldmanaged = 1;
56
57	/**
58	 * @var string String with name of icon for inventory
59	 */
60	public $picto = 'stock';
61
62	const STATUS_DRAFT = 0;
63	const STATUS_VALIDATED = 1;
64	const STATUS_RECORDED = 2;
65	const STATUS_CANCELED = 9;
66
67	/**
68	 *  'type' if the field format ('integer', 'integer:ObjectClass:PathToClass[:AddCreateButtonOrNot[:Filter]]', 'varchar(x)', 'double(24,8)', 'real', 'price', 'text', 'html', 'date', 'datetime', 'timestamp', 'duration', 'mail', 'phone', 'url', 'password')
69	 *         Note: Filter can be a string like "(t.ref:like:'SO-%') or (t.date_creation:<:'20160101') or (t.nature:is:NULL)"
70	 *  'label' the translation key.
71	 *  'enabled' is a condition when the field must be managed.
72	 *  'visible' says if field is visible in list (Examples: 0=Not visible, 1=Visible on list and create/update/view forms, 2=Visible on list only, 3=Visible on create/update/view form only (not list), 4=Visible on list and update/view form only (not create). 5=Visible on list and view only (not create/not update). Using a negative value means field is not shown by default on list but can be selected for viewing)
73	 *  'noteditable' says if field is not editable (1 or 0)
74	 *  'notnull' is set to 1 if not null in database. Set to -1 if we must set data to null if empty ('' or 0).
75	 *  'default' is a default value for creation (can still be replaced by the global setup of default values)
76	 *  'index' if we want an index in database.
77	 *  'foreignkey'=>'tablename.field' if the field is a foreign key (it is recommanded to name the field fk_...).
78	 *  'position' is the sort order of field.
79	 *  'searchall' is 1 if we want to search in this field when making a search from the quick search button.
80	 *  'isameasure' must be set to 1 if you want to have a total on list for this field. Field type must be summable like integer or double(24,8).
81	 *  'css' is the CSS style to use on field. For example: 'maxwidth200'
82	 *  'help' is a string visible as a tooltip on field
83	 *  'comment' is not used. You can store here any text of your choice. It is not used by application.
84	 *  'showoncombobox' if value of the field must be visible into the label of the combobox that list record
85	 *  'arraykeyval' to set list of value if type is a list of predefined values. For example: array("0"=>"Draft","1"=>"Active","-1"=>"Cancel")
86	 */
87
88	// BEGIN MODULEBUILDER PROPERTIES
89	/**
90	 * @var array  Array with all fields and their property
91	 */
92	public $fields = array(
93		'rowid' => array('type'=>'integer', 'label'=>'TechnicalID', 'visible'=>-1, 'enabled'=>1, 'position'=>1, 'notnull'=>1, 'index'=>1, 'comment'=>'Id',),
94		'ref' => array('type'=>'varchar(64)', 'label'=>'Ref', 'visible'=>1, 'enabled'=>1, 'position'=>10, 'notnull'=>1, 'index'=>1, 'searchall'=>1, 'comment'=>'Reference of object', 'css'=>'maxwidth200'),
95		'entity'         => array('type'=>'integer', 'label'=>'Entity', 'visible'=>0, 'enabled'=>1, 'position'=>20, 'notnull'=>1, 'index'=>1,),
96		'title'          => array('type'=>'varchar(255)', 'label'=>'Label', 'visible'=>1, 'enabled'=>1, 'position'=>25, 'css'=>'minwidth300'),
97		'fk_warehouse'   => array('type'=>'integer:Entrepot:product/stock/class/entrepot.class.php', 'label'=>'Warehouse', 'visible'=>1, 'enabled'=>1, 'position'=>30, 'index'=>1, 'help'=>'InventoryForASpecificWarehouse'),
98		'fk_product'     => array('type'=>'integer:Product:product/class/product.class.php', 'label'=>'Product', 'visible'=>1, 'enabled'=>1, 'position'=>32, 'index'=>1, 'help'=>'InventoryForASpecificProduct'),
99		'date_inventory' => array('type'=>'date', 'label'=>'DateValue', 'visible'=>1, 'enabled'=>1, 'position'=>35),
100
101		'date_creation' => array('type'=>'datetime', 'label'=>'DateCreation', 'enabled'=>1, 'visible'=>-2, 'notnull'=>1, 'position'=>500),
102		'tms'           => array('type'=>'timestamp', 'label'=>'DateModification', 'enabled'=>1, 'visible'=>-2, 'notnull'=>1, 'position'=>501),
103		'date_validation' => array('type'=>'datetime', 'label'=>'DateValidation', 'visible'=>-2, 'enabled'=>1, 'position'=>502),
104		'fk_user_creat' => array('type'=>'integer:User:user/class/user.class.php', 'label'=>'UserAuthor', 'enabled'=>1, 'visible'=>-2, 'notnull'=>1, 'position'=>510, 'foreignkey'=>'user.rowid'),
105		'fk_user_modif' => array('type'=>'integer:User:user/class/user.class.php', 'label'=>'UserModif', 'enabled'=>1, 'visible'=>-2, 'notnull'=>-1, 'position'=>511),
106		'fk_user_valid' => array('type'=>'integer:User:user/class/user.class.php', 'label'=>'UserValidation', 'visible'=>-2, 'enabled'=>1, 'position'=>512),
107		'import_key'    => array('type'=>'varchar(14)', 'label'=>'ImportId', 'enabled'=>1, 'visible'=>-2, 'notnull'=>-1, 'index'=>0, 'position'=>1000),
108
109		'status' => array('type'=>'integer', 'label'=>'Status', 'visible'=>4, 'enabled'=>1, 'position'=>1000, 'notnull'=>1, 'default'=>0, 'index'=>1, 'arrayofkeyval'=>array(0=>'Draft', 1=>'Validated', 2=>'Recorded', 9=>'Canceled'))
110	);
111
112	/**
113	 * @var int ID
114	 */
115	public $rowid;
116
117	/**
118	 * @var string Ref
119	 */
120	public $ref;
121
122	/**
123	 * @var int Entity
124	 */
125	public $entity;
126
127	/**
128	 * @var int ID
129	 */
130	public $fk_warehouse;
131
132	/**
133	 * @var int ID
134	 */
135	public $fk_product;
136
137	public $date_inventory;
138	public $title;
139
140	/**
141	 * @var int Status
142	 */
143	public $status;
144
145	/**
146	 * @var integer|string date_creation
147	 */
148	public $date_creation;
149
150	/**
151	 * @var integer|string date_validation
152	 */
153	public $date_validation;
154
155
156	public $tms;
157
158	/**
159	 * @var int ID
160	 */
161	public $fk_user_creat;
162
163	/**
164	 * @var int ID
165	 */
166	public $fk_user_modif;
167
168	/**
169	 * @var int ID
170	 */
171	public $fk_user_valid;
172
173	/**
174	 * @var string import key
175	 */
176	public $import_key;
177	// END MODULEBUILDER PROPERTIES
178
179
180
181	// If this object has a subtable with lines
182
183	/**
184	 * @var string    Name of subtable line
185	 */
186	public $table_element_line = 'inventorydet';
187
188	/**
189	 * @var string    Field with ID of parent key if this field has a parent
190	 */
191	public $fk_element = 'fk_inventory';
192
193	/**
194	 * @var string    Name of subtable class that manage subtable lines
195	 */
196	public $class_element_line = 'Inventoryline';
197
198	/**
199	 * @var array	List of child tables. To test if we can delete object.
200	 */
201	protected $childtables = array();
202	/**
203	 * @var array	List of child tables. To know object to delete on cascade.
204	 */
205	protected $childtablesoncascade = array('inventorydet');
206
207	/**
208	 * @var InventoryLine[]     Array of subtable lines
209	 */
210	public $lines = array();
211
212
213
214	/**
215	 * Constructor
216	 *
217	 * @param DoliDb $db Database handler
218	 */
219	public function __construct(DoliDB $db)
220	{
221		global $conf;
222
223		$this->db = $db;
224
225		if (empty($conf->global->MAIN_SHOW_TECHNICAL_ID)) $this->fields['rowid']['visible'] = 0;
226		if (empty($conf->multicompany->enabled)) $this->fields['entity']['enabled'] = 0;
227	}
228
229
230	/**
231	 * Create object into database
232	 *
233	 * @param  User $user      User that creates
234	 * @param  bool $notrigger false=launch triggers after, true=disable triggers
235	 * @return int             <0 if KO, Id of created object if OK
236	 */
237	public function create(User $user, $notrigger = false)
238	{
239		$result = $this->createCommon($user, $notrigger);
240
241		return $result;
242	}
243
244	/**
245	 * Validate inventory (start it)
246	 *
247	 * @param  User $user      User that creates
248	 * @param  bool $notrigger false=launch triggers after, true=disable triggers
249	 * @return int             <0 if KO, Id of created object if OK
250	 */
251	public function validate(User $user, $notrigger = false)
252	{
253		$this->db->begin();
254
255		$result = 0;
256
257		if ($this->status == self::STATUS_DRAFT) {
258			// Delete inventory
259			$sql = 'DELETE FROM '.MAIN_DB_PREFIX.'inventorydet WHERE fk_inventory = '.$this->id;
260			$resql = $this->db->query($sql);
261			if (!$resql) {
262				$this->error = $this->db->lasterror();
263				$this->db->rollback();
264				return -1;
265			}
266
267			// Scan existing stock to prefill the inventory
268			$sql = 'SELECT ps.rowid, ps.fk_entrepot as fk_warehouse, ps.fk_product, ps.reel,';
269			$sql .= ' pb.batch, pb.qty';
270			$sql .= ' FROM '.MAIN_DB_PREFIX.'product_stock as ps';
271			$sql .= ' LEFT JOIN '.MAIN_DB_PREFIX.'product_batch as pb ON pb.fk_product_stock = ps.rowid,';
272			$sql .= ' '.MAIN_DB_PREFIX.'product as p, '.MAIN_DB_PREFIX.'entrepot as e';
273			$sql .= ' WHERE p.entity IN ('.getEntity('product').')';
274			$sql .= ' AND ps.fk_product = p.rowid AND ps.fk_entrepot = e.rowid';
275			if (empty($conf->global->STOCK_SUPPORTS_SERVICES)) $sql .= " AND p.fk_product_type = 0";
276			if ($this->fk_product > 0) $sql .= ' AND ps.fk_product = '.$this->fk_product;
277			if ($this->fk_warehouse > 0) $sql .= ' AND ps.fk_entrepot = '.$this->fk_warehouse;
278
279			$inventoryline = new InventoryLine($this->db);
280
281			$resql = $this->db->query($sql);
282			if ($resql)
283			{
284				$num = $this->db->num_rows($resql);
285
286				$i = 0;
287				while ($i < $num)
288				{
289					$obj = $this->db->fetch_object($resql);
290
291					$inventoryline->fk_inventory = $this->id;
292					$inventoryline->fk_warehouse = $obj->fk_warehouse;
293					$inventoryline->fk_product = $obj->fk_product;
294					$inventoryline->batch = $obj->batch;
295					$inventoryline->qty_stock = ($obj->batch ? $obj->qty : $obj->reel); // If there is batch detail, we take qty for batch, else global qty
296					$inventoryline->datec = dol_now();
297
298					$resultline = $inventoryline->create($user);
299					if ($resultline <= 0) {
300						$this->error = $inventoryline->error;
301						$this->errors = $inventoryline->errors;
302						$result = -1;
303						break;
304					}
305
306					$i++;
307				}
308			} else {
309				$result = -1;
310				$this->error = $this->db->lasterror();
311			}
312		}
313
314		if ($result >= 0) {
315			$result = $this->setStatut($this::STATUS_VALIDATED, null, '', 'INVENTORY_VALIDATED');
316		}
317
318		if ($result > 0) {
319			$this->db->commit();
320		} else {
321			$this->db->rollback();
322		}
323	}
324
325	/**
326	 * Go back to draft
327	 *
328	 * @param  User $user      User that creates
329	 * @param  bool $notrigger false=launch triggers after, true=disable triggers
330	 * @return int             <0 if KO, Id of created object if OK
331	 */
332	public function setDraft(User $user, $notrigger = false)
333	{
334		$this->db->begin();
335
336		// Delete inventory
337		$sql = 'DELETE FROM '.MAIN_DB_PREFIX.'inventorydet WHERE fk_inventory = '.$this->id;
338		$resql = $this->db->query($sql);
339		if (!$resql) {
340			$this->error = $this->db->lasterror();
341			$this->db->rollback();
342			return -1;
343		}
344
345		$result = $this->setStatut($this::STATUS_DRAFT, null, '', 'INVENTORY_DRAFT');
346
347		if ($result > 0) {
348			$this->db->commit();
349		} else {
350			$this->db->rollback();
351		}
352	}
353
354	/**
355	 * Clone and object into another one
356	 *
357	 * @param  	User 	$user      	User that creates
358	 * @param  	int 	$fromid     Id of object to clone
359	 * @return 	mixed 				New object created, <0 if KO
360	 */
361	public function createFromClone(User $user, $fromid)
362	{
363		global $hookmanager, $langs;
364		$error = 0;
365
366		dol_syslog(__METHOD__, LOG_DEBUG);
367
368		$object = new self($this->db);
369
370		$this->db->begin();
371
372		// Load source object
373		$object->fetchCommon($fromid);
374		// Reset some properties
375		unset($object->id);
376		unset($object->fk_user_creat);
377		unset($object->import_key);
378
379		// Clear fields
380		$object->ref = "copy_of_".$object->ref;
381		$object->title = $langs->trans("CopyOf")." ".$object->title;
382		// ...
383
384		// Create clone
385		$object->context['createfromclone'] = 'createfromclone';
386		$result = $object->createCommon($user);
387		if ($result < 0) {
388			$error++;
389			$this->error = $object->error;
390			$this->errors = $object->errors;
391		}
392
393		unset($object->context['createfromclone']);
394
395		// End
396		if (!$error) {
397			$this->db->commit();
398			return $object;
399		} else {
400			$this->db->rollback();
401			return -1;
402		}
403	}
404
405	/**
406	 * Load object in memory from the database
407	 *
408	 * @param int    $id   Id object
409	 * @param string $ref  Ref
410	 * @return int         <0 if KO, 0 if not found, >0 if OK
411	 */
412	public function fetch($id, $ref = null)
413	{
414		$result = $this->fetchCommon($id, $ref);
415		//if ($result > 0 && ! empty($this->table_element_line)) $this->fetchLines();
416		return $result;
417	}
418
419	/**
420	 * Load object lines in memory from the database
421	 *
422	 * @return int         <0 if KO, 0 if not found, >0 if OK
423	 */
424	/*public function fetchLines()
425	 {
426	 $this->lines=array();
427
428	 // Load lines with object MyObjectLine
429
430	 return count($this->lines)?1:0;
431	 }*/
432
433	/**
434	 * Update object into database
435	 *
436	 * @param  User $user      User that modifies
437	 * @param  bool $notrigger false=launch triggers after, true=disable triggers
438	 * @return int             <0 if KO, >0 if OK
439	 */
440	public function update(User $user, $notrigger = false)
441	{
442		return $this->updateCommon($user, $notrigger);
443	}
444
445	/**
446	 * Delete object in database
447	 *
448	 * @param User $user       User that deletes
449	 * @param bool $notrigger  false=launch triggers after, true=disable triggers
450	 * @return int             <0 if KO, >0 if OK
451	 */
452	public function delete(User $user, $notrigger = false)
453	{
454		return $this->deleteCommon($user, $notrigger);
455	}
456
457	/**
458	 *  Delete a line of object in database
459	 *
460	 *	@param  User	$user       User that delete
461	 *  @param	int		$idline		Id of line to delete
462	 *  @param 	bool 	$notrigger  false=launch triggers after, true=disable triggers
463	 *  @return int         		>0 if OK, <0 if KO
464	 */
465	public function deleteLine(User $user, $idline, $notrigger = false)
466	{
467		if ($this->status < 0)
468		{
469			$this->error = 'ErrorDeleteLineNotAllowedByObjectStatus';
470			return -2;
471		}
472
473		return $this->deleteLineCommon($user, $idline, $notrigger);
474	}
475
476	/**
477	 *  Return a link to the object card (with optionaly the picto)
478	 *
479	 *	@param	int		$withpicto					Include picto in link (0=No picto, 1=Include picto into link, 2=Only picto)
480	 *	@param	string	$option						On what the link point to
481	 *  @param	int  	$notooltip					1=Disable tooltip
482	 *  @param  string  $morecss            		Add more css on link
483	 *  @param  int     $save_lastsearch_value    	-1=Auto, 0=No save of lastsearch_values when clicking, 1=Save lastsearch_values whenclicking
484	 *	@return	string								String with URL
485	 */
486	public function getNomUrl($withpicto = 0, $option = '', $notooltip = 0, $morecss = '', $save_lastsearch_value = -1)
487	{
488		global $db, $conf, $langs;
489		global $dolibarr_main_authentication, $dolibarr_main_demo;
490		global $menumanager;
491
492		if (!empty($conf->dol_no_mouse_hover)) $notooltip = 1; // Force disable tooltips
493
494		$result = '';
495		$companylink = '';
496
497		$label = '<u>'.$langs->trans("Inventory").'</u>';
498		$label .= '<br>';
499		$label .= '<b>'.$langs->trans('Ref').':</b> '.$this->ref;
500
501		$url = dol_buildpath('/product/inventory/card.php', 1).'?id='.$this->id;
502
503		$linkclose = '';
504		if (empty($notooltip))
505		{
506			if (!empty($conf->global->MAIN_OPTIMIZEFORTEXTBROWSER))
507			{
508				$label = $langs->trans("ShowInventory");
509				$linkclose .= ' alt="'.dol_escape_htmltag($label, 1).'"';
510			}
511			$linkclose .= ' title="'.dol_escape_htmltag($label, 1).'"';
512			$linkclose .= ' class="classfortooltip'.($morecss ? ' '.$morecss : '').'"';
513		} else $linkclose = ($morecss ? ' class="'.$morecss.'"' : '');
514
515		$linkstart = '<a href="'.$url.'"';
516		$linkstart .= $linkclose.'>';
517		$linkend = '</a>';
518
519		$result .= $linkstart;
520		if ($withpicto) $result .= img_object(($notooltip ? '' : $label), ($this->picto ? $this->picto : 'generic'), ($notooltip ? (($withpicto != 2) ? 'class="paddingright"' : '') : 'class="'.(($withpicto != 2) ? 'paddingright ' : '').'classfortooltip"'), 0, 0, $notooltip ? 0 : 1);
521		if ($withpicto != 2) $result .= $this->ref;
522		$result .= $linkend;
523		//if ($withpicto != 2) $result.=(($addlabel && $this->label) ? $sep . dol_trunc($this->label, ($addlabel > 1 ? $addlabel : 0)) : '');
524
525		return $result;
526	}
527
528	/**
529	 *  Retourne le libelle du status d'un user (actif, inactif)
530	 *
531	 *  @param	int		$mode          0=libelle long, 1=libelle court, 2=Picto + Libelle court, 3=Picto, 4=Picto + Libelle long, 5=Libelle court + Picto
532	 *  @return	string 			       Label of status
533	 */
534	public function getLibStatut($mode = 0)
535	{
536		return $this->LibStatut($this->status, $mode);
537	}
538
539	// phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
540	/**
541	 *  Return the status
542	 *
543	 *  @param	int		$status        	Id status
544	 *  @param  int		$mode          	0=long label, 1=short label, 2=Picto + short label, 3=Picto, 4=Picto + long label, 5=Short label + Picto, 5=Long label + Picto, 6=Long label + Picto
545	 *  @return string 			       	Label of status
546	 */
547	public static function LibStatut($status, $mode = 0)
548	{
549		// phpcs:enable
550		global $langs;
551
552		$labelStatus = array();
553		$labelStatus[self::STATUS_DRAFT] = $langs->trans('Draft');
554		$labelStatus[self::STATUS_VALIDATED] = $langs->trans('Validated').' ('.$langs->trans('Started').')';
555		$labelStatus[self::STATUS_CANCELED] = $langs->trans('Canceled');
556		$labelStatusShort[self::STATUS_DRAFT] = $langs->trans('Draft');
557		$labelStatusShort[self::STATUS_VALIDATED] = $langs->trans('Started');
558		$labelStatusShort[self::STATUS_CANCELED] = $langs->trans('Canceled');
559
560		return dolGetStatus($labelStatus[$status], $labelStatusShort[$status], '', 'status'.$status, $mode);
561	}
562
563	/**
564	 *	Charge les informations d'ordre info dans l'objet commande
565	 *
566	 *	@param  int		$id       Id of order
567	 *	@return	void
568	 */
569	public function info($id)
570	{
571		$sql = 'SELECT rowid, date_creation as datec, tms as datem,';
572		$sql .= ' fk_user_creat, fk_user_modif';
573		$sql .= ' FROM '.MAIN_DB_PREFIX.$this->table_element.' as t';
574		$sql .= ' WHERE t.rowid = '.$id;
575		$result = $this->db->query($sql);
576		if ($result)
577		{
578			if ($this->db->num_rows($result))
579			{
580				$obj = $this->db->fetch_object($result);
581				$this->id = $obj->rowid;
582				if ($obj->fk_user_author)
583				{
584					$cuser = new User($this->db);
585					$cuser->fetch($obj->fk_user_author);
586					$this->user_creation = $cuser;
587				}
588
589				if ($obj->fk_user_valid)
590				{
591					$vuser = new User($this->db);
592					$vuser->fetch($obj->fk_user_valid);
593					$this->user_validation = $vuser;
594				}
595
596				if ($obj->fk_user_cloture)
597				{
598					$cluser = new User($this->db);
599					$cluser->fetch($obj->fk_user_cloture);
600					$this->user_cloture = $cluser;
601				}
602
603				$this->date_creation     = $this->db->jdate($obj->datec);
604				$this->date_modification = $this->db->jdate($obj->datem);
605				$this->date_validation   = $this->db->jdate($obj->datev);
606			}
607
608			$this->db->free($result);
609		} else {
610			dol_print_error($this->db);
611		}
612	}
613
614	/**
615	 * Initialise object with example values
616	 * Id must be 0 if object instance is a specimen
617	 *
618	 * @return void
619	 */
620	public function initAsSpecimen()
621	{
622		$this->initAsSpecimenCommon();
623	}
624}
625
626/**
627 * Class InventoryLine
628 */
629class InventoryLine extends CommonObjectLine
630{
631	/**
632	 * @var string ID to identify managed object
633	 */
634	public $element = 'inventoryline';
635
636	/**
637	 * @var string Name of table without prefix where object is stored
638	 */
639	public $table_element = 'inventorydet';
640
641	/**
642	 * @var array  Does inventory support multicompany module ? 0=No test on entity, 1=Test with field entity, 2=Test with link by societe
643	 */
644	public $ismultientitymanaged = 0;
645
646	/**
647	 * @var int  Does object support extrafields ? 0=No, 1=Yes
648	 */
649	public $isextrafieldmanaged = 0;
650
651	/**
652	 * @var string String with name of icon for inventory
653	 */
654	public $picto = 'stock';
655
656	/**
657	 *  'type' if the field format.
658	 *  'label' the translation key.
659	 *  'enabled' is a condition when the field must be managed.
660	 *  'visible' says if field is visible in list (Examples: 0=Not visible, 1=Visible on list and create/update/view forms, 2=Visible on list only. Using a negative value means field is not shown by default on list but can be selected for viewing)
661	 *  'notnull' is set to 1 if not null in database. Set to -1 if we must set data to null if empty ('' or 0).
662	 *  'index' if we want an index in database.
663	 *  'foreignkey'=>'tablename.field' if the field is a foreign key (it is recommanded to name the field fk_...).
664	 *  'position' is the sort order of field.
665	 *  'searchall' is 1 if we want to search in this field when making a search from the quick search button.
666	 *  'isameasure' must be set to 1 if you want to have a total on list for this field. Field type must be summable like integer or double(24,8).
667	 *  'help' is a string visible as a tooltip on field
668	 *  'comment' is not used. You can store here any text of your choice. It is not used by application.
669	 *  'default' is a default value for creation (can still be replaced by the global setup of default values)
670	 *  'showoncombobox' if field must be shown into the label of combobox
671	 */
672
673	// BEGIN MODULEBUILDER PROPERTIES
674	/**
675	 * @var array  Array with all fields and their property
676	 */
677	public $fields = array(
678		'rowid' => array('type'=>'integer', 'label'=>'TechnicalID', 'visible'=>-1, 'enabled'=>1, 'position'=>1, 'notnull'=>1, 'index'=>1, 'comment'=>'Id',),
679		'fk_inventory'  => array('type'=>'integer:Inventory:product/inventory/class/inventory.class.php', 'label'=>'Inventory', 'visible'=>1, 'enabled'=>1, 'position'=>30, 'index'=>1, 'help'=>'LinkToInventory'),
680		'fk_warehouse'  => array('type'=>'integer:Entrepot:product/stock/class/entrepot.class.php', 'label'=>'Warehouse', 'visible'=>1, 'enabled'=>1, 'position'=>30, 'index'=>1, 'help'=>'LinkToThirparty'),
681		'fk_product'    => array('type'=>'integer:Product:product/class/product.class.php', 'label'=>'Product', 'visible'=>1, 'enabled'=>1, 'position'=>32, 'index'=>1, 'help'=>'LinkToProduct'),
682		'batch'         => array('type'=>'string', 'label'=>'Batch', 'visible'=>1, 'enabled'=>1, 'position'=>32, 'index'=>1, 'help'=>'LinkToProduct'),
683		'datec'         => array('type'=>'datetime', 'label'=>'DateCreation', 'enabled'=>1, 'visible'=>-2, 'notnull'=>1, 'position'=>500),
684		'tms'           => array('type'=>'timestamp', 'label'=>'DateModification', 'enabled'=>1, 'visible'=>-2, 'notnull'=>1, 'position'=>501),
685		'qty_stock'     => array('type'=>'double', 'label'=>'QtyFound', 'visible'=>1, 'enabled'=>1, 'position'=>32, 'index'=>1, 'help'=>'Qty we found/want (to define during draft edition)'),
686		'qty_view'      => array('type'=>'double', 'label'=>'QtyBefore', 'visible'=>1, 'enabled'=>1, 'position'=>33, 'index'=>1, 'help'=>'Qty before (filled once movements are validated)'),
687		'qty_regulated' => array('type'=>'double', 'label'=>'QtyDelta', 'visible'=>1, 'enabled'=>1, 'position'=>34, 'index'=>1, 'help'=>'Qty aadded or removed (filled once movements are validated)'),
688	);
689
690	/**
691	 * @var int ID
692	 */
693	public $rowid;
694
695	public $fk_inventory;
696	public $fk_warehouse;
697	public $fk_product;
698	public $batch;
699	public $datec;
700	public $tms;
701	public $qty_stock;
702	public $qty_view;
703	public $qty_regulated;
704
705
706	/**
707	 * Create object in database
708	 *
709	 * @param User $user       User that creates
710	 * @param bool $notrigger  false=launch triggers after, true=disable triggers
711	 * @return int             <0 if KO, >0 if OK
712	 */
713	public function create(User $user, $notrigger = false)
714	{
715		return $this->createCommon($user, $notrigger);
716	}
717
718	/**
719	 * Load object in memory from the database
720	 *
721	 * @param int    $id   Id object
722	 * @param string $ref  Ref
723	 * @return int         <0 if KO, 0 if not found, >0 if OK
724	 */
725	public function fetch($id, $ref = null)
726	{
727		$result = $this->fetchCommon($id, $ref);
728		//if ($result > 0 && ! empty($this->table_element_line)) $this->fetchLines();
729		return $result;
730	}
731
732	/**
733	 * Update object into database
734	 *
735	 * @param  User $user      User that modifies
736	 * @param  bool $notrigger false=launch triggers after, true=disable triggers
737	 * @return int             <0 if KO, >0 if OK
738	 */
739	public function update(User $user, $notrigger = false)
740	{
741		return $this->updateCommon($user, $notrigger);
742	}
743
744	/**
745	 * Delete object in database
746	 *
747	 * @param User $user       User that deletes
748	 * @param bool $notrigger  false=launch triggers after, true=disable triggers
749	 * @return int             <0 if KO, >0 if OK
750	 */
751	public function delete(User $user, $notrigger = false)
752	{
753		return $this->deleteCommon($user, $notrigger);
754		//return $this->deleteCommon($user, $notrigger, 1);
755	}
756}
757