productbatch.class.php 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543
  1. <?php
  2. /* Copyright (C) 2007-2012 Laurent Destailleur <eldy@users.sourceforge.net>
  3. * Copyright (C) 2013-2014 Cedric GROSS <c.gross@kreiz-it.fr>
  4. *
  5. * This program is free software; you can redistribute it and/or modify
  6. * it under the terms of the GNU General Public License as published by
  7. * the Free Software Foundation; either version 3 of the License, or
  8. * (at your option) any later version.
  9. *
  10. * This program is distributed in the hope that it will be useful,
  11. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  12. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  13. * GNU General Public License for more details.
  14. *
  15. * You should have received a copy of the GNU General Public License
  16. * along with this program. If not, see <http://www.gnu.org/licenses/>.
  17. */
  18. /**
  19. * \file product/class/productbatch.class.php
  20. * \ingroup productbatch
  21. * \brief Manage record and specific data for batch number management
  22. */
  23. require_once DOL_DOCUMENT_ROOT."/core/class/commonobject.class.php";
  24. /**
  25. * Manage record for batch number management
  26. */
  27. class Productbatch extends CommonObject
  28. {
  29. /**
  30. * @var string ID to identify managed object
  31. */
  32. public $element='productbatch';
  33. private static $_table_element='product_batch'; //!< Name of table without prefix where object is stored
  34. var $tms='';
  35. var $fk_product_stock;
  36. var $sellby='';
  37. var $eatby='';
  38. var $batch='';
  39. var $qty;
  40. public $warehouseid;
  41. public $fk_product;
  42. /**
  43. * Constructor
  44. *
  45. * @param DoliDb $db Database handler
  46. */
  47. function __construct($db)
  48. {
  49. $this->db = $db;
  50. }
  51. /**
  52. * Create object into database
  53. *
  54. * @param User $user User that creates
  55. * @param int $notrigger 0=launch triggers after, 1=disable triggers
  56. * @return int <0 if KO, Id of created object if OK
  57. */
  58. function create($user, $notrigger=0)
  59. {
  60. global $conf, $langs;
  61. $error=0;
  62. // Clean parameters
  63. $this->cleanParam();
  64. // Check parameters
  65. // Put here code to add control on parameters values
  66. // Insert request
  67. $sql = "INSERT INTO ".MAIN_DB_PREFIX."product_batch (";
  68. $sql.= "fk_product_stock,";
  69. $sql.= "sellby,";
  70. $sql.= "eatby,";
  71. $sql.= "batch,";
  72. $sql.= "qty,";
  73. $sql.= "import_key";
  74. $sql.= ") VALUES (";
  75. $sql.= " ".(! isset($this->fk_product_stock)?'NULL':$this->fk_product_stock).",";
  76. $sql.= " ".(! isset($this->sellby) || dol_strlen($this->sellby)==0?'NULL':"'".$this->db->idate($this->sellby)."'").",";
  77. $sql.= " ".(! isset($this->eatby) || dol_strlen($this->eatby)==0?'NULL':"'".$this->db->idate($this->eatby)."'").",";
  78. $sql.= " ".(! isset($this->batch)?'NULL':"'".$this->db->escape($this->batch)."'").",";
  79. $sql.= " ".(! isset($this->qty)?'NULL':$this->qty).",";
  80. $sql.= " ".(! isset($this->import_key)?'NULL':"'".$this->db->escape($this->import_key)."'")."";
  81. $sql.= ")";
  82. $this->db->begin();
  83. dol_syslog(get_class($this)."::create", LOG_DEBUG);
  84. $resql=$this->db->query($sql);
  85. if (! $resql) { $error++; $this->errors[]="Error ".$this->db->lasterror(); }
  86. if (! $error)
  87. {
  88. $this->id = $this->db->last_insert_id(MAIN_DB_PREFIX.self::$_table_element);
  89. if (! $notrigger)
  90. {
  91. // Uncomment this and change MYOBJECT to your own tag if you
  92. // want this action calls a trigger.
  93. //// Call triggers
  94. //include_once DOL_DOCUMENT_ROOT . '/core/class/interfaces.class.php';
  95. //$interface=new Interfaces($this->db);
  96. //$result=$interface->run_triggers('MYOBJECT_CREATE',$this,$user,$langs,$conf);
  97. //if ($result < 0) { $error++; $this->errors=$interface->errors; }
  98. //// End call triggers
  99. }
  100. }
  101. // Commit or rollback
  102. if ($error)
  103. {
  104. $this->db->rollback();
  105. return -1*$error;
  106. }
  107. else
  108. {
  109. $this->db->commit();
  110. return $this->id;
  111. }
  112. }
  113. /**
  114. * Load object in memory from the database
  115. *
  116. * @param int $id Id object
  117. * @return int <0 if KO, >0 if OK
  118. */
  119. function fetch($id)
  120. {
  121. global $langs;
  122. $sql = "SELECT";
  123. $sql.= " t.rowid,";
  124. $sql.= " t.tms,";
  125. $sql.= " t.fk_product_stock,";
  126. $sql.= " t.sellby as oldsellby,";
  127. $sql.= " t.eatby as oldeatby,";
  128. $sql.= " t.batch,";
  129. $sql.= " t.qty,";
  130. $sql.= " t.import_key,";
  131. $sql.= " w.fk_entrepot,";
  132. $sql.= " w.fk_product,";
  133. $sql.= " pl.eatby,";
  134. $sql.= " pl.sellby";
  135. $sql.= " FROM ".MAIN_DB_PREFIX."product_batch as t INNER JOIN ".MAIN_DB_PREFIX."product_stock w on t.fk_product_stock = w.rowid";
  136. $sql.= " LEFT JOIN ".MAIN_DB_PREFIX."product_lot as pl on pl.fk_product = w.fk_product and pl.batch = t.batch";
  137. $sql.= " WHERE t.rowid = ".$id;
  138. dol_syslog(get_class($this)."::fetch", LOG_DEBUG);
  139. $resql = $this->db->query($sql);
  140. if ($resql)
  141. {
  142. if ($this->db->num_rows($resql))
  143. {
  144. $obj = $this->db->fetch_object($resql);
  145. $this->id = $obj->rowid;
  146. $this->tms = $this->db->jdate($obj->tms);
  147. $this->fk_product_stock = $obj->fk_product_stock;
  148. $this->sellby = $this->db->jdate($obj->sellby?$obj->sellby:$obj->oldsellby);
  149. $this->eatby = $this->db->jdate($obj->eatby?$obj->eatby:$obj->oldeatby);
  150. $this->batch = $obj->batch;
  151. $this->qty = $obj->qty;
  152. $this->import_key = $obj->import_key;
  153. $this->warehouseid= $obj->fk_entrepot;
  154. $this->fk_product= $obj->fk_product;
  155. }
  156. $this->db->free($resql);
  157. return 1;
  158. }
  159. else
  160. {
  161. $this->error = "Error ".$this->db->lasterror();
  162. return -1;
  163. }
  164. }
  165. /**
  166. * Update object into database
  167. *
  168. * @param User $user User that modifies
  169. * @param int $notrigger 0=launch triggers after, 1=disable triggers
  170. * @return int <0 if KO, >0 if OK
  171. */
  172. function update($user=null, $notrigger=0)
  173. {
  174. global $conf, $langs;
  175. $error=0;
  176. // Clean parameters
  177. $this->cleanParam();
  178. // TODO Check qty is ok for stock move. Negative may not be allowed.
  179. if ($this->qty < 0)
  180. {
  181. }
  182. // Update request
  183. $sql = "UPDATE ".MAIN_DB_PREFIX.self::$_table_element." SET";
  184. $sql.= " fk_product_stock=".(isset($this->fk_product_stock)?$this->fk_product_stock:"null").",";
  185. $sql.= " sellby=".(dol_strlen($this->sellby)!=0 ? "'".$this->db->idate($this->sellby)."'" : 'null').",";
  186. $sql.= " eatby=".(dol_strlen($this->eatby)!=0 ? "'".$this->db->idate($this->eatby)."'" : 'null').",";
  187. $sql.= " batch=".(isset($this->batch)?"'".$this->db->escape($this->batch)."'":"null").",";
  188. $sql.= " qty=".(isset($this->qty)?$this->qty:"null").",";
  189. $sql.= " import_key=".(isset($this->import_key)?"'".$this->db->escape($this->import_key)."'":"null")."";
  190. $sql.= " WHERE rowid=".$this->id." AND tms='".$this->db->idate($this->tms)."'";
  191. $this->db->begin();
  192. dol_syslog(get_class($this)."::update", LOG_DEBUG);
  193. $resql = $this->db->query($sql);
  194. if (! $resql) { $error++; $this->errors[]="Error ".$this->db->lasterror(); }
  195. if (! $error)
  196. {
  197. if (! $notrigger)
  198. {
  199. // Uncomment this and change MYOBJECT to your own tag if you
  200. // want this action calls a trigger.
  201. //// Call triggers
  202. //include_once DOL_DOCUMENT_ROOT . '/core/class/interfaces.class.php';
  203. //$interface=new Interfaces($this->db);
  204. //$result=$interface->run_triggers('MYOBJECT_MODIFY',$this,$user,$langs,$conf);
  205. //if ($result < 0) { $error++; $this->errors=$interface->errors; }
  206. //// End call triggers
  207. }
  208. }
  209. // Commit or rollback
  210. if ($error)
  211. {
  212. foreach($this->errors as $errmsg)
  213. {
  214. dol_syslog(get_class($this)."::update ".$errmsg, LOG_ERR);
  215. $this->error.=($this->error?', '.$errmsg:$errmsg);
  216. }
  217. $this->db->rollback();
  218. return -1*$error;
  219. }
  220. else
  221. {
  222. $this->db->commit();
  223. return 1;
  224. }
  225. }
  226. /**
  227. * Delete object in database
  228. *
  229. * @param User $user User that deletes
  230. * @param int $notrigger 0=launch triggers after, 1=disable triggers
  231. * @return int <0 if KO, >0 if OK
  232. */
  233. function delete($user, $notrigger=0)
  234. {
  235. global $conf, $langs;
  236. $error=0;
  237. $this->db->begin();
  238. if (! $error)
  239. {
  240. if (! $notrigger)
  241. {
  242. // Uncomment this and change MYOBJECT to your own tag if you
  243. // want this action calls a trigger.
  244. //// Call triggers
  245. //include_once DOL_DOCUMENT_ROOT . '/core/class/interfaces.class.php';
  246. //$interface=new Interfaces($this->db);
  247. //$result=$interface->run_triggers('MYOBJECT_DELETE',$this,$user,$langs,$conf);
  248. //if ($result < 0) { $error++; $this->errors=$interface->errors; }
  249. //// End call triggers
  250. }
  251. }
  252. if (! $error)
  253. {
  254. $sql = "DELETE FROM ".MAIN_DB_PREFIX.self::$_table_element."";
  255. $sql.= " WHERE rowid=".$this->id;
  256. dol_syslog(get_class($this)."::delete", LOG_DEBUG);
  257. $resql = $this->db->query($sql);
  258. if (! $resql) { $error++; $this->errors[]="Error ".$this->db->lasterror(); }
  259. }
  260. // Commit or rollback
  261. if ($error)
  262. {
  263. foreach($this->errors as $errmsg)
  264. {
  265. dol_syslog(get_class($this)."::delete ".$errmsg, LOG_ERR);
  266. $this->error.=($this->error?', '.$errmsg:$errmsg);
  267. }
  268. $this->db->rollback();
  269. return -1*$error;
  270. }
  271. else
  272. {
  273. $this->db->commit();
  274. return 1;
  275. }
  276. }
  277. /**
  278. * Load an object from its id and create a new one in database
  279. *
  280. * @param int $fromid Id of object to clone
  281. * @return int New id of clone
  282. */
  283. function createFromClone($fromid)
  284. {
  285. global $user,$langs;
  286. $error=0;
  287. $object=new Productbatch($this->db);
  288. $object->context['createfromclone']='createfromclone';
  289. $this->db->begin();
  290. // Load source object
  291. $object->fetch($fromid);
  292. $object->id=0;
  293. $object->statut=0;
  294. // Clear fields
  295. // ...
  296. // Create clone
  297. $result=$object->create($user);
  298. // Other options
  299. if ($result < 0)
  300. {
  301. $this->error=$object->error;
  302. $error++;
  303. }
  304. if (! $error)
  305. {
  306. }
  307. unset($object->context['createfromclone']);
  308. // End
  309. if (! $error)
  310. {
  311. $this->db->commit();
  312. return $object->id;
  313. }
  314. else
  315. {
  316. $this->db->rollback();
  317. return -1;
  318. }
  319. }
  320. /**
  321. * Initialise object with example values
  322. * Id must be 0 if object instance is a specimen
  323. *
  324. * @return void
  325. */
  326. function initAsSpecimen()
  327. {
  328. $this->id=0;
  329. $this->tms='';
  330. $this->fk_product_stock='';
  331. $this->sellby='';
  332. $this->eatby='';
  333. $this->batch='';
  334. $this->import_key='';
  335. }
  336. /**
  337. * Clean fields (triming)
  338. *
  339. * @return void
  340. */
  341. private function cleanParam()
  342. {
  343. if (isset($this->fk_product_stock)) $this->fk_product_stock=(int) trim($this->fk_product_stock);
  344. if (isset($this->batch)) $this->batch=trim($this->batch);
  345. if (isset($this->qty)) $this->qty=(float) trim($this->qty);
  346. if (isset($this->import_key)) $this->import_key=trim($this->import_key);
  347. }
  348. /**
  349. * Find first detail record that match eather eat-by or sell-by or batch within given warehouse
  350. *
  351. * @param int $fk_product_stock id product_stock for objet
  352. * @param date $eatby eat-by date for object - deprecated: a search must be done on batch number
  353. * @param date $sellby sell-by date for object - deprecated: a search must be done on batch number
  354. * @param string $batch_number batch number for object
  355. * @return int <0 if KO, >0 if OK
  356. */
  357. function find($fk_product_stock=0, $eatby='',$sellby='',$batch_number='')
  358. {
  359. global $langs;
  360. $where = array();
  361. $sql = "SELECT";
  362. $sql.= " t.rowid,";
  363. $sql.= " t.tms,";
  364. $sql.= " t.fk_product_stock,";
  365. $sql.= " t.sellby,"; // deprecated
  366. $sql.= " t.eatby,"; // deprecated
  367. $sql.= " t.batch,";
  368. $sql.= " t.qty,";
  369. $sql.= " t.import_key";
  370. $sql.= " FROM ".MAIN_DB_PREFIX.self::$_table_element." as t";
  371. $sql.= " WHERE fk_product_stock=".$fk_product_stock;
  372. if (! empty($eatby)) array_push($where," eatby = '".$this->db->idate($eatby)."'"); // deprecated
  373. if (! empty($sellby)) array_push($where," sellby = '".$this->db->idate($sellby)."'"); // deprecated
  374. if (! empty($batch_number)) $sql.= " AND batch = '".$this->db->escape($batch_number)."'";
  375. if (! empty($where)) $sql.= " AND (".implode(" OR ",$where).")";
  376. dol_syslog(get_class($this)."::fetch", LOG_DEBUG);
  377. $resql=$this->db->query($sql);
  378. if ($resql)
  379. {
  380. if ($this->db->num_rows($resql))
  381. {
  382. $obj = $this->db->fetch_object($resql);
  383. $this->id = $obj->rowid;
  384. $this->tms = $this->db->jdate($obj->tms);
  385. $this->fk_product_stock = $obj->fk_product_stock;
  386. $this->sellby = $this->db->jdate($obj->sellby);
  387. $this->eatby = $this->db->jdate($obj->eatby);
  388. $this->batch = $obj->batch;
  389. $this->qty = $obj->qty;
  390. $this->import_key = $obj->import_key;
  391. }
  392. $this->db->free($resql);
  393. return 1;
  394. }
  395. else
  396. {
  397. $this->error="Error ".$this->db->lasterror();
  398. return -1;
  399. }
  400. }
  401. /**
  402. * Return all batch detail records for a given product and warehouse
  403. *
  404. * @param DoliDB $db database object
  405. * @param int $fk_product_stock id product_stock for objet
  406. * @param int $with_qty 1 = doesn't return line with 0 quantity
  407. * @param int $fk_product If set to a product id, get eatby and sellby from table llx_product_lot
  408. * @return array <0 if KO, array of batch
  409. */
  410. public static function findAll($db, $fk_product_stock, $with_qty=0, $fk_product=0)
  411. {
  412. global $langs;
  413. $ret = array();
  414. $sql = "SELECT";
  415. $sql.= " t.rowid,";
  416. $sql.= " t.tms,";
  417. $sql.= " t.fk_product_stock,";
  418. $sql.= " t.sellby as oldsellby,"; // deprecated but may not be migrated into new table
  419. $sql.= " t.eatby as oldeatby,"; // deprecated but may not be migrated into new table
  420. $sql.= " t.batch,";
  421. $sql.= " t.qty,";
  422. $sql.= " t.import_key";
  423. if ($fk_product > 0)
  424. {
  425. $sql.= ", pl.rowid as lotid, pl.eatby as eatby, pl.sellby as sellby";
  426. // TODO May add extrafields to ?
  427. }
  428. $sql.= " FROM ".MAIN_DB_PREFIX."product_batch as t";
  429. if ($fk_product > 0)
  430. {
  431. $sql.= " LEFT JOIN ".MAIN_DB_PREFIX."product_lot as pl ON pl.fk_product = ".$fk_product." AND pl.batch = t.batch";
  432. // TODO May add extrafields to ?
  433. }
  434. $sql.= " WHERE fk_product_stock=".$fk_product_stock;
  435. if ($with_qty) $sql.= " AND t.qty <> 0";
  436. dol_syslog("productbatch::findAll", LOG_DEBUG);
  437. $resql=$db->query($sql);
  438. if ($resql)
  439. {
  440. $num = $db->num_rows($resql);
  441. $i=0;
  442. while ($i < $num)
  443. {
  444. $obj = $db->fetch_object($resql);
  445. $tmp = new Productbatch($db);
  446. $tmp->id = $obj->rowid;
  447. $tmp->lotid = $obj->lotid;
  448. $tmp->tms = $db->jdate($obj->tms);
  449. $tmp->fk_product_stock = $obj->fk_product_stock;
  450. $tmp->sellby = $db->jdate($obj->sellby ? $obj->sellby : $obj->oldsellby);
  451. $tmp->eatby = $db->jdate($obj->eatby ? $obj->eatby : $obj->oldeatby);
  452. $tmp->batch = $obj->batch;
  453. $tmp->qty = $obj->qty;
  454. $tmp->import_key = $obj->import_key;
  455. $ret[$tmp->batch] = $tmp; // $ret is for a $fk_product_stock and unique key is on $fk_product_stock+batch
  456. $i++;
  457. }
  458. $db->free($resql);
  459. return $ret;
  460. }
  461. else
  462. {
  463. $error="Error ".$db->lasterror();
  464. return -1;
  465. }
  466. }
  467. }