stocktransferline.class.php 35 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042
  1. <?php
  2. /* Copyright (C) 2017 Laurent Destailleur <eldy@users.sourceforge.net>
  3. * Copyright (C) 2021 Gauthier VERDOL <gauthier.verdol@atm-consulting.fr>
  4. * Copyright (C) ---Put here your own copyright and developer email---
  5. *
  6. * This program is free software; you can redistribute it and/or modify
  7. * it under the terms of the GNU General Public License as published by
  8. * the Free Software Foundation; either version 3 of the License, or
  9. * (at your option) any later version.
  10. *
  11. * This program is distributed in the hope that it will be useful,
  12. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  13. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  14. * GNU General Public License for more details.
  15. *
  16. * You should have received a copy of the GNU General Public License
  17. * along with this program. If not, see <https://www.gnu.org/licenses/>.
  18. */
  19. /**
  20. * \file class/stocktransferline.class.php
  21. * \ingroup stocktransfer
  22. * \brief This file is a CRUD class file for StockTransferLine (Create/Read/Update/Delete)
  23. */
  24. // Put here all includes required by your class file
  25. require_once DOL_DOCUMENT_ROOT.'/core/class/commonobjectline.class.php';
  26. //require_once DOL_DOCUMENT_ROOT . '/societe/class/societe.class.php';
  27. //require_once DOL_DOCUMENT_ROOT . '/product/class/product.class.php';
  28. /**
  29. * Class for StockTransferLine
  30. */
  31. class StockTransferLine extends CommonObjectLine
  32. {
  33. /**
  34. * @var string ID to identify managed object.
  35. */
  36. public $element = 'stocktransferline';
  37. /**
  38. * @var string Name of table without prefix where object is stored. This is also the key used for extrafields management.
  39. */
  40. public $table_element = 'stocktransfer_stocktransferline';
  41. /**
  42. * @var int Does this object support multicompany module ?
  43. * 0=No test on entity, 1=Test with field entity, 'field@table'=Test with link by field@table
  44. */
  45. public $ismultientitymanaged = 0;
  46. /**
  47. * @var int Does object support extrafields ? 0=No, 1=Yes
  48. */
  49. public $isextrafieldmanaged = 1;
  50. /**
  51. * @var string String with name of icon for stocktransferline. Must be the part after the 'object_' into object_stocktransferline.png
  52. */
  53. public $picto = 'stocktransferline@stocktransfer';
  54. const STATUS_DRAFT = 0;
  55. const STATUS_VALIDATED = 1;
  56. const STATUS_CANCELED = 9;
  57. /**
  58. * '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')
  59. * Note: Filter can be a string like "(t.ref:like:'SO-%') or (t.date_creation:<:'20160101') or (t.nature:is:NULL)"
  60. * 'label' the translation key.
  61. * 'enabled' is a condition when the field must be managed (Example: 1 or '$conf->global->MY_SETUP_PARAM)
  62. * 'position' is the sort order of field.
  63. * 'notnull' is set to 1 if not null in database. Set to -1 if we must set data to null if empty ('' or 0).
  64. * '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)
  65. * 'noteditable' says if field is not editable (1 or 0)
  66. * 'default' is a default value for creation (can still be overwrote by the Setup of Default Values if field is editable in creation form). Note: If default is set to '(PROV)' and field is 'ref', the default value will be set to '(PROVid)' where id is rowid when a new record is created.
  67. * 'index' if we want an index in database.
  68. * 'foreignkey'=>'tablename.field' if the field is a foreign key (it is recommanded to name the field fk_...).
  69. * 'searchall' is 1 if we want to search in this field when making a search from the quick search button.
  70. * '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).
  71. * 'css' is the CSS style to use on field. For example: 'maxwidth200'
  72. * 'help' is a string visible as a tooltip on field
  73. * 'showoncombobox' if value of the field must be visible into the label of the combobox that list record
  74. * 'disabled' is 1 if we want to have the field locked by a 'disabled' attribute. In most cases, this is never set into the definition of $fields into class, but is set dynamically by some part of code.
  75. * 'arraykeyval' to set list of value if type is a list of predefined values. For example: array("0"=>"Draft","1"=>"Active","-1"=>"Cancel")
  76. * 'autofocusoncreate' to have field having the focus on a create form. Only 1 field should have this property set to 1.
  77. * 'comment' is not used. You can store here any text of your choice. It is not used by application.
  78. *
  79. * Note: To have value dynamic, you can set value to 0 in definition and edit the value on the fly into the constructor.
  80. */
  81. // BEGIN MODULEBUILDER PROPERTIES
  82. /**
  83. * @var array Array with all fields and their property. Do not use it as a static var. It may be modified by constructor.
  84. */
  85. public $fields=array(
  86. 'rowid' => array('type'=>'integer', 'label'=>'TechnicalID', 'enabled'=>'1', 'position'=>1, 'notnull'=>1, 'visible'=>0, 'noteditable'=>'1', 'index'=>1, 'comment'=>"Id"),
  87. 'amount' => array('type'=>'price', 'label'=>'Amount', 'enabled'=>'1', 'position'=>40, 'notnull'=>0, 'visible'=>1, 'default'=>'null', 'isameasure'=>'1', 'help'=>"Help text for amount",),
  88. 'qty' => array('type'=>'real', 'label'=>'Qty', 'enabled'=>'1', 'position'=>45, 'notnull'=>0, 'visible'=>1, 'default'=>'0', 'isameasure'=>'1', 'css'=>'maxwidth75imp', 'help'=>"Help text for quantity",),
  89. 'fk_warehouse_destination' => array('type'=>'integer:Entrepot:product/stock/class/entrepot.class.php', 'label'=>'Entrepôt de destination', 'enabled'=>'1', 'position'=>50, 'notnull'=>1, 'visible'=>1,),
  90. 'fk_warehouse_source' => array('type'=>'integer:Entrepot:product/stock/class/entrepot.class.php', 'label'=>'Entrepôt source', 'enabled'=>'1', 'position'=>50, 'notnull'=>1, 'visible'=>1,),
  91. 'fk_stocktransfer' => array('type'=>'integer:StockTransfer:stocktransfer/stock/class/stocktransfer.class.php', 'label'=>'StockTransfer', 'enabled'=>'1', 'position'=>50, 'notnull'=>1, 'visible'=>0,),
  92. 'fk_product' => array('type'=>'integer:Product:product/class/product.class.php', 'label'=>'Product', 'enabled'=>'1', 'position'=>50, 'notnull'=>1, 'visible'=>1,),
  93. 'batch' => array('type'=>'varchar(128)', 'label'=>'Batch', 'enabled'=>'1', 'position'=>1000, 'notnull'=>-1, 'visible'=>1,),
  94. 'pmp' => array('type'=>'double'/*, 'help'=>'THMEstimatedHelp'*/, 'label'=>'PMP', 'enabled'=>'1', 'position'=>50, 'notnull'=>0, 'visible'=>1,),
  95. 'rang' => array('type'=>'integer', 'label'=>'Qty', 'enabled'=>'1', 'position'=>45, 'notnull'=>0, 'visible'=>0, 'default'=>'0', 'isameasure'=>'1', 'css'=>'maxwidth75imp', 'help'=>"Help text for quantity",),
  96. );
  97. public $rowid;
  98. public $amount;
  99. public $qty;
  100. public $fk_warehouse_destination;
  101. public $fk_warehouse_source;
  102. public $fk_stocktransfer;
  103. public $fk_product;
  104. public $batch;
  105. /**
  106. * @var double pmp
  107. */
  108. public $pmp;
  109. // END MODULEBUILDER PROPERTIES
  110. /**
  111. * Constructor
  112. *
  113. * @param DoliDb $db Database handler
  114. */
  115. public function __construct(DoliDB $db)
  116. {
  117. global $conf, $langs;
  118. $this->db = $db;
  119. if (!getDolGlobalString('MAIN_SHOW_TECHNICAL_ID') && isset($this->fields['rowid'])) {
  120. $this->fields['rowid']['visible'] = 0;
  121. }
  122. if (!isModEnabled('multicompany') && isset($this->fields['entity'])) {
  123. $this->fields['entity']['enabled'] = 0;
  124. }
  125. // Example to show how to set values of fields definition dynamically
  126. /*if ($user->rights->stocktransfer->stocktransferline->read) {
  127. $this->fields['myfield']['visible'] = 1;
  128. $this->fields['myfield']['noteditable'] = 0;
  129. }*/
  130. // Unset fields that are disabled
  131. foreach ($this->fields as $key => $val) {
  132. if (isset($val['enabled']) && empty($val['enabled'])) {
  133. unset($this->fields[$key]);
  134. }
  135. }
  136. // Translate some data of arrayofkeyval
  137. if (is_object($langs)) {
  138. foreach ($this->fields as $key => $val) {
  139. if (isset($val['arrayofkeyval']) && is_array($val['arrayofkeyval'])) {
  140. foreach ($val['arrayofkeyval'] as $key2 => $val2) {
  141. $this->fields[$key]['arrayofkeyval'][$key2] = $langs->trans($val2);
  142. }
  143. }
  144. }
  145. }
  146. }
  147. /**
  148. * Create object into database
  149. *
  150. * @param User $user User that creates
  151. * @param bool $notrigger false=launch triggers after, true=disable triggers
  152. * @return int Return integer <0 if KO, Id of created object if OK
  153. */
  154. public function create(User $user, $notrigger = false)
  155. {
  156. return $this->createCommon($user, $notrigger);
  157. }
  158. /**
  159. * Clone an object into another one
  160. *
  161. * @param User $user User that creates
  162. * @param int $fromid Id of object to clone
  163. * @return mixed New object created, <0 if KO
  164. */
  165. public function createFromClone(User $user, $fromid)
  166. {
  167. global $langs, $extrafields;
  168. $error = 0;
  169. dol_syslog(__METHOD__, LOG_DEBUG);
  170. $object = new self($this->db);
  171. $this->db->begin();
  172. // Load source object
  173. $result = $object->fetchCommon($fromid);
  174. if ($result > 0 && !empty($object->table_element_line)) {
  175. $object->fetchLines();
  176. }
  177. // get lines so they will be clone
  178. //foreach($this->lines as $line)
  179. // $line->fetch_optionals();
  180. // Reset some properties
  181. unset($object->id);
  182. unset($object->import_key);
  183. // Clear fields
  184. $object->ref = empty($this->fields['ref']['default']) ? "copy_of_".$object->ref : $this->fields['ref']['default'];
  185. $object->label = empty($this->fields['label']['default']) ? $langs->trans("CopyOf")." ".$object->label : $this->fields['label']['default'];
  186. $object->status = self::STATUS_DRAFT;
  187. // ...
  188. // Clear extrafields that are unique
  189. if (is_array($object->array_options) && count($object->array_options) > 0) {
  190. $extrafields->fetch_name_optionals_label($this->table_element);
  191. foreach ($object->array_options as $key => $option) {
  192. $shortkey = preg_replace('/options_/', '', $key);
  193. if (!empty($extrafields->attributes[$this->table_element]['unique'][$shortkey])) {
  194. //var_dump($key); var_dump($clonedObj->array_options[$key]); exit;
  195. unset($object->array_options[$key]);
  196. }
  197. }
  198. }
  199. // Create clone
  200. $object->context['createfromclone'] = 'createfromclone';
  201. $result = $object->createCommon($user);
  202. if ($result < 0) {
  203. $error++;
  204. $this->error = $object->error;
  205. $this->errors = $object->errors;
  206. }
  207. if (!$error) {
  208. // copy internal contacts
  209. if ($this->copy_linked_contact($object, 'internal') < 0) {
  210. $error++;
  211. }
  212. }
  213. if (!$error) {
  214. // copy external contacts if same company
  215. if (property_exists($this, 'socid') && $this->socid == $object->socid) {
  216. if ($this->copy_linked_contact($object, 'external') < 0) {
  217. $error++;
  218. }
  219. }
  220. }
  221. unset($object->context['createfromclone']);
  222. // End
  223. if (!$error) {
  224. $this->db->commit();
  225. return $object;
  226. } else {
  227. $this->db->rollback();
  228. return -1;
  229. }
  230. }
  231. /**
  232. * Load object in memory from the database
  233. *
  234. * @param int $id Id object
  235. * @param string $ref Ref
  236. * @return int Return integer <0 if KO, 0 if not found, >0 if OK
  237. */
  238. public function fetch($id, $ref = null)
  239. {
  240. $result = $this->fetchCommon($id, $ref);
  241. if ($result > 0 && !empty($this->table_element_line)) {
  242. $this->fetchLines();
  243. }
  244. return $result;
  245. }
  246. /**
  247. * Load object lines in memory from the database
  248. *
  249. * @return int Return integer <0 if KO, 0 if not found, >0 if OK
  250. */
  251. public function fetchLines()
  252. {
  253. $this->lines = array();
  254. $result = $this->fetchLinesCommon();
  255. return $result;
  256. }
  257. /**
  258. * Load list of objects in memory from the database.
  259. *
  260. * @param string $sortorder Sort Order
  261. * @param string $sortfield Sort field
  262. * @param int $limit limit
  263. * @param int $offset Offset
  264. * @param array $filter Filter array. Example array('field'=>'valueforlike', 'customurl'=>...)
  265. * @param string $filtermode Filter mode (AND or OR)
  266. * @return array|int int <0 if KO, array of pages if OK
  267. */
  268. public function fetchAll($sortorder = '', $sortfield = '', $limit = 0, $offset = 0, array $filter = array(), $filtermode = 'AND')
  269. {
  270. dol_syslog(__METHOD__, LOG_DEBUG);
  271. $records = array();
  272. $sql = 'SELECT ';
  273. $sql .= $this->getFieldList();
  274. $sql .= ' FROM '.MAIN_DB_PREFIX.$this->table_element.' as t';
  275. if (isset($this->ismultientitymanaged) && $this->ismultientitymanaged == 1) {
  276. $sql .= ' WHERE t.entity IN ('.getEntity($this->element).')';
  277. } else {
  278. $sql .= ' WHERE 1 = 1';
  279. }
  280. // Manage filter
  281. $sqlwhere = array();
  282. if (count($filter) > 0) {
  283. foreach ($filter as $key => $value) {
  284. if ($key == 't.rowid') {
  285. $sqlwhere[] = $key.'='.$value;
  286. } elseif (strpos($key, 'date') !== false) {
  287. $sqlwhere[] = $key.' = \''.$this->db->idate($value).'\'';
  288. } elseif ($key == 'customsql') {
  289. $sqlwhere[] = $value;
  290. } else {
  291. $sqlwhere[] = $key.' LIKE \'%'.$this->db->escape($value).'%\'';
  292. }
  293. }
  294. }
  295. if (count($sqlwhere) > 0) {
  296. $sql .= " AND (".implode(" ".$filtermode." ", $sqlwhere).")";
  297. }
  298. if (!empty($sortfield)) {
  299. $sql .= $this->db->order($sortfield, $sortorder);
  300. }
  301. if (!empty($limit)) {
  302. $sql .= ' '.$this->db->plimit($limit, $offset);
  303. }
  304. $resql = $this->db->query($sql);
  305. if ($resql) {
  306. $num = $this->db->num_rows($resql);
  307. $i = 0;
  308. while ($i < ($limit ? min($limit, $num) : $num)) {
  309. $obj = $this->db->fetch_object($resql);
  310. $record = new self($this->db);
  311. $record->setVarsFromFetchObj($obj);
  312. $records[$record->id] = $record;
  313. $i++;
  314. }
  315. $this->db->free($resql);
  316. return $records;
  317. } else {
  318. $this->errors[] = 'Error '.$this->db->lasterror();
  319. dol_syslog(__METHOD__.' '.join(',', $this->errors), LOG_ERR);
  320. return -1;
  321. }
  322. }
  323. /**
  324. * Update object into database
  325. *
  326. * @param User $user User that modifies
  327. * @param bool $notrigger false=launch triggers after, true=disable triggers
  328. * @return int Return integer <0 if KO, >0 if OK
  329. */
  330. public function update(User $user, $notrigger = false)
  331. {
  332. return $this->updateCommon($user, $notrigger);
  333. }
  334. /**
  335. * Delete object in database
  336. *
  337. * @param User $user User that deletes
  338. * @param bool $notrigger false=launch triggers after, true=disable triggers
  339. * @return int Return integer <0 if KO, >0 if OK
  340. */
  341. public function delete(User $user, $notrigger = false)
  342. {
  343. return $this->deleteCommon($user, $notrigger);
  344. //return $this->deleteCommon($user, $notrigger, 1);
  345. }
  346. /**
  347. * Delete a line of object in database
  348. *
  349. * @param User $user User that delete
  350. * @param int $idline Id of line to delete
  351. * @param bool $notrigger false=launch triggers after, true=disable triggers
  352. * @return int >0 if OK, <0 if KO
  353. */
  354. public function deleteLine(User $user, $idline, $notrigger = false)
  355. {
  356. if ($this->status < 0) {
  357. $this->error = 'ErrorDeleteLineNotAllowedByObjectStatus';
  358. return -2;
  359. }
  360. return $this->deleteLineCommon($user, $idline, $notrigger);
  361. }
  362. /**
  363. * Makes all stock movements (add quantity, remove quantity or cancel all actions)
  364. *
  365. * @param string $label label of stock movement
  366. * @param string $code_inv label of stock movement
  367. * @param int $fk_entrepot Warehouse concerned by stock movement
  368. * @param int $direction add or remove qty
  369. * @return int 1 if ok, <= 0 if ko
  370. */
  371. public function doStockMovement($label, $code_inv, $fk_entrepot, $direction = 1)
  372. {
  373. global $conf, $user, $langs;
  374. require_once DOL_DOCUMENT_ROOT . '/product/class/product.class.php';
  375. include_once DOL_DOCUMENT_ROOT . '/product/stock/class/mouvementstock.class.php';
  376. include_once DOL_DOCUMENT_ROOT . '/product/stock/stocktransfer/class/stocktransfer.class.php';
  377. $p = new Product($this->db);
  378. $p->fetch($this->fk_product);
  379. $op[0] = "+".trim($this->qty);
  380. $op[1] = "-".trim($this->qty);
  381. $movementstock = new MouvementStock($this->db);
  382. $st = new StockTransfer($this->db);
  383. $movementstock->origin_type = $st->origin_type;
  384. $movementstock->origin_id = $this->fk_stocktransfer;
  385. if (empty($this->batch)) { // no batch for line
  386. /*$result = $p->correct_stock(
  387. $user,
  388. $fk_entrepot,
  389. $this->qty,
  390. $direction, // 1=décrémentation
  391. $label,
  392. empty($direction) ? $this->pmp : 0,
  393. GETPOST('inventorycode', 'alphanohtml'),
  394. 'stocktransfer',
  395. $this->fk_stocktransfer
  396. );*/
  397. $result = $movementstock->_create(
  398. $user,
  399. $p->id,
  400. $fk_entrepot,
  401. $op[$direction],
  402. $direction,
  403. empty($direction) ? $this->pmp : 0,
  404. $label,
  405. $code_inv
  406. );
  407. if ($result < 0) {
  408. setEventMessages($p->error, $p->errors, 'errors');
  409. return 0;
  410. }
  411. } else {
  412. if ($p->hasbatch()) {
  413. $arraybatchinfo = $p->loadBatchInfo($this->batch);
  414. if (count($arraybatchinfo) > 0) {
  415. $firstrecord = array_shift($arraybatchinfo);
  416. $dlc = $firstrecord['eatby'];
  417. $dluo = $firstrecord['sellby'];
  418. //var_dump($batch); var_dump($arraybatchinfo); var_dump($firstrecord); var_dump($dlc); var_dump($dluo); exit;
  419. } else {
  420. $dlc = '';
  421. $dluo = '';
  422. }
  423. /*$result = $p->correct_stock_batch(
  424. $user,
  425. $fk_entrepot,
  426. $this->qty,
  427. $direction,
  428. $label,
  429. empty($direction) ? $this->pmp : 0,
  430. $dlc,
  431. $dluo,
  432. $this->batch,
  433. GETPOST("codemove")
  434. );*/
  435. $result = $movementstock->_create(
  436. $user,
  437. $p->id,
  438. $fk_entrepot,
  439. $op[$direction],
  440. $direction,
  441. empty($direction) ? $this->pmp : 0,
  442. $label,
  443. $code_inv,
  444. '',
  445. $dlc,
  446. $dluo,
  447. $this->batch
  448. );
  449. if ($result < 0) {
  450. setEventMessages($p->error, $p->errors, 'errors');
  451. return 0;
  452. }
  453. } else {
  454. setEventMessages($langs->trans('StockTransferNoBatchForProduct', $p->getNomUrl()), '', 'errors');
  455. return -1;
  456. }
  457. }
  458. return 1;
  459. }
  460. /**
  461. * Validate object
  462. *
  463. * @param User $user User making status change
  464. * @param int $notrigger 1=Does not execute triggers, 0= execute triggers
  465. * @return int Return integer <=0 if OK, 0=Nothing done, >0 if KO
  466. */
  467. public function validate($user, $notrigger = 0)
  468. {
  469. global $conf, $langs;
  470. require_once DOL_DOCUMENT_ROOT.'/core/lib/files.lib.php';
  471. $error = 0;
  472. // Protection
  473. if ($this->status == self::STATUS_VALIDATED) {
  474. dol_syslog(get_class($this)."::validate action abandonned: already validated", LOG_WARNING);
  475. return 0;
  476. }
  477. /*if (! ((empty($conf->global->MAIN_USE_ADVANCED_PERMS) && !empty($user->rights->stocktransfer->stocktransferline->write))
  478. || (!empty($conf->global->MAIN_USE_ADVANCED_PERMS) && !empty($user->rights->stocktransfer->stocktransferline->stocktransferline_advance->validate))))
  479. {
  480. $this->error='NotEnoughPermissions';
  481. dol_syslog(get_class($this)."::valid ".$this->error, LOG_ERR);
  482. return -1;
  483. }*/
  484. $now = dol_now();
  485. $this->db->begin();
  486. // Define new ref
  487. if (!$error && (preg_match('/^[\(]?PROV/i', $this->ref) || empty($this->ref))) { // empty should not happened, but when it occurs, the test save life
  488. $num = $this->getNextNumRef();
  489. } else {
  490. $num = $this->ref;
  491. }
  492. $this->newref = $num;
  493. if (!empty($num)) {
  494. // Validate
  495. $sql = "UPDATE ".MAIN_DB_PREFIX.$this->table_element;
  496. $sql .= " SET ref = '".$this->db->escape($num)."',";
  497. $sql .= " status = ".self::STATUS_VALIDATED;
  498. if (!empty($this->fields['date_validation'])) {
  499. $sql .= ", date_validation = '".$this->db->idate($now)."',";
  500. }
  501. if (!empty($this->fields['fk_user_valid'])) {
  502. $sql .= ", fk_user_valid = ".((int) $user->id);
  503. }
  504. $sql .= " WHERE rowid = ".((int) $this->id);
  505. dol_syslog(get_class($this)."::validate()", LOG_DEBUG);
  506. $resql = $this->db->query($sql);
  507. if (!$resql) {
  508. dol_print_error($this->db);
  509. $this->error = $this->db->lasterror();
  510. $error++;
  511. }
  512. if (!$error && !$notrigger) {
  513. // Call trigger
  514. $result = $this->call_trigger('STOCKTRANSFERLINE_VALIDATE', $user);
  515. if ($result < 0) {
  516. $error++;
  517. }
  518. // End call triggers
  519. }
  520. }
  521. if (!$error) {
  522. $this->oldref = $this->ref;
  523. // Rename directory if dir was a temporary ref
  524. if (preg_match('/^[\(]?PROV/i', $this->ref)) {
  525. // Now we rename also files into index
  526. $sql = 'UPDATE '.MAIN_DB_PREFIX."ecm_files set filename = CONCAT('".$this->db->escape($this->newref)."', SUBSTR(filename, ".(strlen($this->ref) + 1).")), filepath = 'stocktransferline/".$this->db->escape($this->newref)."'";
  527. $sql .= " WHERE filename LIKE '".$this->db->escape($this->ref)."%' AND filepath = 'stocktransferline/".$this->db->escape($this->ref)."' and entity = ".((int) $conf->entity);
  528. $resql = $this->db->query($sql);
  529. if (!$resql) {
  530. $error++;
  531. $this->error = $this->db->lasterror();
  532. }
  533. $sql = 'UPDATE '.MAIN_DB_PREFIX."ecm_files set filepath = 'stocktransferline/".$this->db->escape($this->newref)."'";
  534. $sql .= " WHERE filepath = 'stocktransferline/".$this->db->escape($this->ref)."' and entity = ".$conf->entity;
  535. $resql = $this->db->query($sql);
  536. if (!$resql) {
  537. $error++;
  538. $this->error = $this->db->lasterror();
  539. }
  540. // We rename directory ($this->ref = old ref, $num = new ref) in order not to lose the attachments
  541. $oldref = dol_sanitizeFileName($this->ref);
  542. $newref = dol_sanitizeFileName($num);
  543. $dirsource = $conf->stocktransfer->dir_output.'/stocktransferline/'.$oldref;
  544. $dirdest = $conf->stocktransfer->dir_output.'/stocktransferline/'.$newref;
  545. if (!$error && file_exists($dirsource)) {
  546. dol_syslog(get_class($this)."::validate() rename dir ".$dirsource." into ".$dirdest);
  547. if (@rename($dirsource, $dirdest)) {
  548. dol_syslog("Rename ok");
  549. // Rename docs starting with $oldref with $newref
  550. $listoffiles = dol_dir_list($conf->stocktransfer->dir_output.'/stocktransferline/'.$newref, 'files', 1, '^'.preg_quote($oldref, '/'));
  551. foreach ($listoffiles as $fileentry) {
  552. $dirsource = $fileentry['name'];
  553. $dirdest = preg_replace('/^'.preg_quote($oldref, '/').'/', $newref, $dirsource);
  554. $dirsource = $fileentry['path'].'/'.$dirsource;
  555. $dirdest = $fileentry['path'].'/'.$dirdest;
  556. @rename($dirsource, $dirdest);
  557. }
  558. }
  559. }
  560. }
  561. }
  562. // Set new ref and current status
  563. if (!$error) {
  564. $this->ref = $num;
  565. $this->status = self::STATUS_VALIDATED;
  566. }
  567. if (!$error) {
  568. $this->db->commit();
  569. return 1;
  570. } else {
  571. $this->db->rollback();
  572. return -1;
  573. }
  574. }
  575. /**
  576. * Set draft status
  577. *
  578. * @param User $user Object user that modify
  579. * @param int $notrigger 1=Does not execute triggers, 0=Execute triggers
  580. * @return int Return integer <0 if KO, >0 if OK
  581. */
  582. public function setDraft($user, $notrigger = 0)
  583. {
  584. // Protection
  585. if ($this->status <= self::STATUS_DRAFT) {
  586. return 0;
  587. }
  588. /*if (! ((empty($conf->global->MAIN_USE_ADVANCED_PERMS) && !empty($user->rights->stocktransfer->write))
  589. || (!empty($conf->global->MAIN_USE_ADVANCED_PERMS) && !empty($user->rights->stocktransfer->stocktransfer_advance->validate))))
  590. {
  591. $this->error='Permission denied';
  592. return -1;
  593. }*/
  594. return $this->setStatusCommon($user, self::STATUS_DRAFT, $notrigger, 'STOCKTRANSFERLINE_UNVALIDATE');
  595. }
  596. /**
  597. * Set cancel status
  598. *
  599. * @param User $user Object user that modify
  600. * @param int $notrigger 1=Does not execute triggers, 0=Execute triggers
  601. * @return int Return integer <0 if KO, 0=Nothing done, >0 if OK
  602. */
  603. public function cancel($user, $notrigger = 0)
  604. {
  605. // Protection
  606. if ($this->status != self::STATUS_VALIDATED) {
  607. return 0;
  608. }
  609. /*if (! ((empty($conf->global->MAIN_USE_ADVANCED_PERMS) && !empty($user->rights->stocktransfer->write))
  610. || (!empty($conf->global->MAIN_USE_ADVANCED_PERMS) && !empty($user->rights->stocktransfer->stocktransfer_advance->validate))))
  611. {
  612. $this->error='Permission denied';
  613. return -1;
  614. }*/
  615. return $this->setStatusCommon($user, self::STATUS_CANCELED, $notrigger, 'STOCKTRANSFERLINE_CLOSE');
  616. }
  617. /**
  618. * Set back to validated status
  619. *
  620. * @param User $user Object user that modify
  621. * @param int $notrigger 1=Does not execute triggers, 0=Execute triggers
  622. * @return int Return integer <0 if KO, 0=Nothing done, >0 if OK
  623. */
  624. public function reopen($user, $notrigger = 0)
  625. {
  626. // Protection
  627. if ($this->status != self::STATUS_CANCELED) {
  628. return 0;
  629. }
  630. /*if (! ((empty($conf->global->MAIN_USE_ADVANCED_PERMS) && !empty($user->rights->stocktransfer->write))
  631. || (!empty($conf->global->MAIN_USE_ADVANCED_PERMS) && !empty($user->rights->stocktransfer->stocktransfer_advance->validate))))
  632. {
  633. $this->error='Permission denied';
  634. return -1;
  635. }*/
  636. return $this->setStatusCommon($user, self::STATUS_VALIDATED, $notrigger, 'STOCKTRANSFERLINE_REOPEN');
  637. }
  638. /**
  639. * Return a link to the object card (with optionaly the picto)
  640. *
  641. * @param int $withpicto Include picto in link (0=No picto, 1=Include picto into link, 2=Only picto)
  642. * @param string $option On what the link point to ('nolink', ...)
  643. * @param int $notooltip 1=Disable tooltip
  644. * @param string $morecss Add more css on link
  645. * @param int $save_lastsearch_value -1=Auto, 0=No save of lastsearch_values when clicking, 1=Save lastsearch_values whenclicking
  646. * @return string String with URL
  647. */
  648. public function getNomUrl($withpicto = 0, $option = '', $notooltip = 0, $morecss = '', $save_lastsearch_value = -1)
  649. {
  650. global $conf, $langs, $hookmanager;
  651. if (!empty($conf->dol_no_mouse_hover)) {
  652. $notooltip = 1;
  653. } // Force disable tooltips
  654. $result = '';
  655. $label = '<u>'.$langs->trans("StockTransferLine").'</u>';
  656. $label .= '<br>';
  657. $label .= '<b>'.$langs->trans('Ref').':</b> '.$this->ref;
  658. if (isset($this->status)) {
  659. $label .= '<br><b>'.$langs->trans("Status").":</b> ".$this->getLibStatut(5);
  660. }
  661. $url = dol_buildpath('/stocktransfer/stocktransferline_card.php', 1).'?id='.$this->id;
  662. if ($option != 'nolink') {
  663. // Add param to save lastsearch_values or not
  664. $add_save_lastsearch_values = ($save_lastsearch_value == 1 ? 1 : 0);
  665. if ($save_lastsearch_value == -1 && isset($_SERVER["PHP_SELF"]) && preg_match('/list\.php/', $_SERVER["PHP_SELF"])) {
  666. $add_save_lastsearch_values = 1;
  667. }
  668. if ($add_save_lastsearch_values) {
  669. $url .= '&save_lastsearch_values=1';
  670. }
  671. }
  672. $linkclose = '';
  673. if (empty($notooltip)) {
  674. if (getDolGlobalString('MAIN_OPTIMIZEFORTEXTBROWSER')) {
  675. $label = $langs->trans("ShowStockTransferLine");
  676. $linkclose .= ' alt="'.dol_escape_htmltag($label, 1).'"';
  677. }
  678. $linkclose .= ' title="'.dol_escape_htmltag($label, 1).'"';
  679. $linkclose .= ' class="classfortooltip'.($morecss ? ' '.$morecss : '').'"';
  680. } else {
  681. $linkclose = ($morecss ? ' class="'.$morecss.'"' : '');
  682. }
  683. $linkstart = '<a href="'.$url.'"';
  684. $linkstart .= $linkclose.'>';
  685. $linkend = '</a>';
  686. $result .= $linkstart;
  687. if (empty($this->showphoto_on_popup)) {
  688. if ($withpicto) {
  689. $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);
  690. }
  691. } else {
  692. if ($withpicto) {
  693. require_once DOL_DOCUMENT_ROOT.'/core/lib/files.lib.php';
  694. list($class, $module) = explode('@', $this->picto);
  695. $upload_dir = $conf->$module->multidir_output[$conf->entity]."/$class/".dol_sanitizeFileName($this->ref);
  696. $filearray = dol_dir_list($upload_dir, "files");
  697. $filename = $filearray[0]['name'];
  698. if (!empty($filename)) {
  699. $pospoint = strpos($filearray[0]['name'], '.');
  700. $pathtophoto = $class.'/'.$this->ref.'/thumbs/'.substr($filename, 0, $pospoint).'_mini'.substr($filename, $pospoint);
  701. if (!getDolGlobalString(strtoupper($module.'_'.$class).'_FORMATLISTPHOTOSASUSERS')) {
  702. $result .= '<div class="floatleft inline-block valignmiddle divphotoref"><div class="photoref"><img class="photo'.$module.'" alt="No photo" border="0" src="'.DOL_URL_ROOT.'/viewimage.php?modulepart='.$module.'&entity='.$conf->entity.'&file='.urlencode($pathtophoto).'"></div></div>';
  703. } else {
  704. $result .= '<div class="floatleft inline-block valignmiddle divphotoref"><img class="photouserphoto userphoto" alt="No photo" border="0" src="'.DOL_URL_ROOT.'/viewimage.php?modulepart='.$module.'&entity='.$conf->entity.'&file='.urlencode($pathtophoto).'"></div>';
  705. }
  706. $result .= '</div>';
  707. } else {
  708. $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);
  709. }
  710. }
  711. }
  712. if ($withpicto != 2) {
  713. $result .= $this->ref;
  714. }
  715. $result .= $linkend;
  716. //if ($withpicto != 2) $result.=(($addlabel && $this->label) ? $sep . dol_trunc($this->label, ($addlabel > 1 ? $addlabel : 0)) : '');
  717. global $action, $hookmanager;
  718. $hookmanager->initHooks(array('stocktransferlinedao'));
  719. $parameters = array('id'=>$this->id, 'getnomurl'=>$result);
  720. $reshook = $hookmanager->executeHooks('getNomUrl', $parameters, $this, $action); // Note that $action and $object may have been modified by some hooks
  721. if ($reshook > 0) {
  722. $result = $hookmanager->resPrint;
  723. } else {
  724. $result .= $hookmanager->resPrint;
  725. }
  726. return $result;
  727. }
  728. /**
  729. * Return label of the status
  730. *
  731. * @param int $mode 0=long label, 1=short label, 2=Picto + short label, 3=Picto, 4=Picto + long label, 5=Short label + Picto, 6=Long label + Picto
  732. * @return string Label of status
  733. */
  734. public function getLibStatut($mode = 0)
  735. {
  736. return $this->LibStatut($this->status, $mode);
  737. }
  738. // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
  739. /**
  740. * Return the status
  741. *
  742. * @param int $status Id status
  743. * @param int $mode 0=long label, 1=short label, 2=Picto + short label, 3=Picto, 4=Picto + long label, 5=Short label + Picto, 6=Long label + Picto
  744. * @return string Label of status
  745. */
  746. public function LibStatut($status, $mode = 0)
  747. {
  748. // phpcs:enable
  749. if (empty($this->labelStatus) || empty($this->labelStatusShort)) {
  750. global $langs;
  751. //$langs->load("stocktransfer@stocktransfer");
  752. $this->labelStatus[self::STATUS_DRAFT] = $langs->trans('Draft');
  753. $this->labelStatus[self::STATUS_VALIDATED] = $langs->trans('Enabled');
  754. $this->labelStatus[self::STATUS_CANCELED] = $langs->trans('Disabled');
  755. $this->labelStatusShort[self::STATUS_DRAFT] = $langs->trans('Draft');
  756. $this->labelStatusShort[self::STATUS_VALIDATED] = $langs->trans('Enabled');
  757. $this->labelStatusShort[self::STATUS_CANCELED] = $langs->trans('Disabled');
  758. }
  759. $statusType = 'status'.$status;
  760. //if ($status == self::STATUS_VALIDATED) $statusType = 'status1';
  761. if ($status == self::STATUS_CANCELED) {
  762. $statusType = 'status6';
  763. }
  764. return dolGetStatus($this->labelStatus[$status], $this->labelStatusShort[$status], '', $statusType, $mode);
  765. }
  766. /**
  767. * Load the info information in the object
  768. *
  769. * @param int $id Id of object
  770. * @return void
  771. */
  772. public function info($id)
  773. {
  774. $sql = 'SELECT rowid, date_creation as datec, tms as datem,';
  775. $sql .= ' fk_user_creat, fk_user_modif';
  776. $sql .= ' FROM '.MAIN_DB_PREFIX.$this->table_element.' as t';
  777. $sql .= ' WHERE t.rowid = '.((int) $id);
  778. $result = $this->db->query($sql);
  779. if ($result) {
  780. if ($this->db->num_rows($result)) {
  781. $obj = $this->db->fetch_object($result);
  782. $this->id = $obj->rowid;
  783. $this->user_creation_id = $obj->fk_user_creat;
  784. $this->user_modification_id = $obj->fk_user_modif;
  785. $this->date_creation = $this->db->jdate($obj->datec);
  786. $this->date_modification = empty($obj->datem) ? '' : $this->db->jdate($obj->datem);
  787. }
  788. $this->db->free($result);
  789. } else {
  790. dol_print_error($this->db);
  791. }
  792. }
  793. /**
  794. * Initialise object with example values
  795. * Id must be 0 if object instance is a specimen
  796. *
  797. * @return void
  798. */
  799. public function initAsSpecimen()
  800. {
  801. $this->initAsSpecimenCommon();
  802. }
  803. /**
  804. * Returns the reference to the following non used object depending on the active numbering module.
  805. *
  806. * @return string Object free reference
  807. */
  808. public function getNextNumRef()
  809. {
  810. global $langs, $conf;
  811. $langs->load("stocks");
  812. if (!getDolGlobalString('STOCKTRANSFER_STOCKTRANSFERLINE_ADDON')) {
  813. $conf->global->STOCKTRANSFER_STOCKTRANSFERLINE_ADDON = 'mod_stocktransferline_standard';
  814. }
  815. if (getDolGlobalString('STOCKTRANSFER_STOCKTRANSFERLINE_ADDON')) {
  816. $mybool = false;
  817. $file = getDolGlobalString('STOCKTRANSFER_STOCKTRANSFERLINE_ADDON') . ".php";
  818. $classname = $conf->global->STOCKTRANSFER_STOCKTRANSFERLINE_ADDON;
  819. // Include file with class
  820. $dirmodels = array_merge(array('/'), (array) $conf->modules_parts['models']);
  821. foreach ($dirmodels as $reldir) {
  822. $dir = dol_buildpath($reldir."core/modules/stocktransfer/");
  823. // Load file with numbering class (if found)
  824. $mybool |= @include_once $dir.$file;
  825. }
  826. if ($mybool === false) {
  827. dol_print_error('', "Failed to include file ".$file);
  828. return '';
  829. }
  830. if (class_exists($classname)) {
  831. $obj = new $classname();
  832. $numref = $obj->getNextValue($this);
  833. if ($numref != '' && $numref != '-1') {
  834. return $numref;
  835. } else {
  836. $this->error = $obj->error;
  837. //dol_print_error($this->db,get_class($this)."::getNextNumRef ".$obj->error);
  838. return "";
  839. }
  840. } else {
  841. print $langs->trans("Error")." ".$langs->trans("ClassNotFound").' '.$classname;
  842. return "";
  843. }
  844. } else {
  845. print $langs->trans("ErrorNumberingModuleNotSetup", $this->element);
  846. return "";
  847. }
  848. }
  849. /**
  850. * Create a document onto disk according to template module.
  851. *
  852. * @param string $modele Force template to use ('' to not force)
  853. * @param Translate $outputlangs objet lang a utiliser pour traduction
  854. * @param int $hidedetails Hide details of lines
  855. * @param int $hidedesc Hide description
  856. * @param int $hideref Hide ref
  857. * @param null|array $moreparams Array to provide more information
  858. * @return int 0 if KO, 1 if OK
  859. */
  860. public function generateDocument($modele, $outputlangs, $hidedetails = 0, $hidedesc = 0, $hideref = 0, $moreparams = null)
  861. {
  862. global $conf, $langs;
  863. $result = 0;
  864. $includedocgeneration = 0;
  865. $langs->load("stocks");
  866. if (!dol_strlen($modele)) {
  867. $modele = 'standard_stocktransferline';
  868. if (!empty($this->model_pdf)) {
  869. $modele = $this->model_pdf;
  870. } elseif (getDolGlobalString('STOCKTRANSFERLINE_ADDON_PDF')) {
  871. $modele = $conf->global->STOCKTRANSFERLINE_ADDON_PDF;
  872. }
  873. }
  874. $modelpath = "core/modules/stocktransfer/doc/";
  875. if ($includedocgeneration) {
  876. $result = $this->commonGenerateDocument($modelpath, $modele, $outputlangs, $hidedetails, $hidedesc, $hideref, $moreparams);
  877. }
  878. return $result;
  879. }
  880. /**
  881. * Action executed by scheduler
  882. * CAN BE A CRON TASK. In such a case, parameters come from the schedule job setup field 'Parameters'
  883. * Use public function doScheduledJob($param1, $param2, ...) to get parameters
  884. *
  885. * @return int 0 if OK, <>0 if KO (this function is used also by cron so only 0 is OK)
  886. */
  887. public function doScheduledJob()
  888. {
  889. //$conf->global->SYSLOG_FILE = 'DOL_DATA_ROOT/dolibarr_mydedicatedlofile.log';
  890. $error = 0;
  891. $this->output = '';
  892. $this->error = '';
  893. dol_syslog(__METHOD__, LOG_DEBUG);
  894. $now = dol_now();
  895. $this->db->begin();
  896. // ...
  897. $this->db->commit();
  898. return $error;
  899. }
  900. }