PageRenderTime 71ms CodeModel.GetById 19ms RepoModel.GetById 0ms app.codeStats 1ms

/htdocs/core/class/commonobject.class.php

https://bitbucket.org/speedealing/speedealing
PHP | 2750 lines | 1800 code | 287 blank | 663 comment | 580 complexity | 600cdc8b234b25507da49b2a6db75b39 MD5 | raw file
Possible License(s): LGPL-3.0, LGPL-2.1, GPL-3.0, MIT
  1. <?php
  2. /* Copyright (C) 2006-2012 Laurent Destailleur <eldy@users.sourceforge.net>
  3. * Copyright (C) 2005-2013 Regis Houssin <regis.houssin@capnetworks.com>
  4. * Copyright (C) 2010-2011 Juanjo Menent <jmenent@2byte.es>
  5. * Copyright (C) 2012 Christophe Battarel <christophe.battarel@altairis.fr>
  6. * Copyright (C) 2011-2012 Philippe Grand <philippe.grand@atoo-net.com>
  7. * Copyright (C) 2012 Marcos García <marcosgdf@gmail.com>
  8. *
  9. * This program is free software; you can redistribute it and/or modify
  10. * it under the terms of the GNU General Public License as published by
  11. * the Free Software Foundation; either version 3 of the License, or
  12. * (at your option) any later version.
  13. *
  14. * This program is distributed in the hope that it will be useful,
  15. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  16. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  17. * GNU General Public License for more details.
  18. *
  19. * You should have received a copy of the GNU General Public License
  20. * along with this program. If not, see <http://www.gnu.org/licenses/>.
  21. */
  22. /**
  23. * \file htdocs/core/class/commonobject.class.php
  24. * \ingroup core
  25. * \brief File of parent class of all other business classes (invoices, contracts, proposals, orders, ...)
  26. */
  27. /**
  28. * Parent class of all other business classes (invoices, contracts, proposals, orders, ...)
  29. */
  30. abstract class CommonObject {
  31. protected $db;
  32. public $error;
  33. public $errors;
  34. public $canvas; // Contains canvas name if it is
  35. /**
  36. * class constructor
  37. *
  38. * @param couchClient $db Database handler
  39. */
  40. function __construct($db = '') {
  41. global $conf;
  42. $this->db = $db;
  43. return 1;
  44. }
  45. /**
  46. * Return full name (civility+' '+name+' '+lastname)
  47. *
  48. * @param Translate $langs Language object for translation of civility
  49. * @param int $option 0=No option, 1=Add civility
  50. * @param int $nameorder -1=Auto, 0=Lastname+Firstname, 1=Firstname+Lastname
  51. * @param int $maxlen Maximum length
  52. * @return string String with full name
  53. */
  54. function getFullName($langs, $option = 0, $nameorder = -1, $maxlen = 0) {
  55. global $conf;
  56. $lastname = $this->Lastname;
  57. $firstname = $this->Firstname;
  58. if (empty($lastname))
  59. $lastname = ($this->name ? $this->name : $this->nom);
  60. if (empty($firstname))
  61. $firstname = $this->prenom;
  62. $ret = '';
  63. if ($option && $this->civilite_id) {
  64. if ($langs->transnoentitiesnoconv("Civility" . $this->civilite_id) != "Civility" . $this->civilite_id)
  65. $ret.=$langs->transnoentitiesnoconv("Civility" . $this->civilite_id) . ' ';
  66. else
  67. $ret.=$this->civilite_id . ' ';
  68. }
  69. // If order not defined, we use the setup
  70. if ($nameorder < 0)
  71. $nameorder = (empty($conf->global->MAIN_FIRSTNAME_NAME_POSITION));
  72. if ($nameorder) {
  73. $ret.=$firstname;
  74. if ($firstname && $lastname)
  75. $ret.=' ';
  76. $ret.=$lastname;
  77. }
  78. else {
  79. $ret.=$lastname;
  80. if ($firstname && $lastname)
  81. $ret.=' ';
  82. $ret.=$firstname;
  83. }
  84. return dol_trunc($ret, $maxlen);
  85. }
  86. /**
  87. * Check if ref is used.
  88. *
  89. * @return int <0 if KO, 0 if not found, >0 if found
  90. */
  91. function verifyNumRef() {
  92. global $conf;
  93. $sql = "SELECT rowid";
  94. $sql.= " FROM " . MAIN_DB_PREFIX . $this->table_element;
  95. $sql.= " WHERE ref = '" . $this->ref . "'";
  96. $sql.= " AND entity = " . $conf->entity;
  97. dol_syslog(get_class($this) . "::verifyNumRef sql=" . $sql, LOG_DEBUG);
  98. $resql = $this->db->query($sql);
  99. if ($resql) {
  100. $num = $this->db->num_rows($resql);
  101. return $num;
  102. } else {
  103. $this->error = $this->db->lasterror();
  104. dol_syslog(get_class($this) . "::verifyNumRef " . $this->error, LOG_ERR);
  105. return -1;
  106. }
  107. }
  108. /**
  109. * Add a link between element $this->element and a contact
  110. *
  111. * @param int $fk_socpeople Id of contact to link
  112. * @param int $type_contact Type of contact (code or id)
  113. * @param int $source external=Contact extern (llx_socpeople), internal=Contact intern (llx_user)
  114. * @param int $notrigger Disable all triggers
  115. * @return int <0 if KO, >0 if OK
  116. */
  117. function add_contact($fk_socpeople, $type_contact, $source = 'external', $notrigger = 0) {
  118. global $user, $conf, $langs;
  119. $error = 0;
  120. dol_syslog(get_class($this) . "::add_contact $fk_socpeople, $type_contact, $source");
  121. // Check parameters
  122. if ($fk_socpeople <= 0) {
  123. $this->error = $langs->trans("ErrorWrongValueForParameter", "1");
  124. dol_syslog(get_class($this) . "::add_contact " . $this->error, LOG_ERR);
  125. return -1;
  126. }
  127. if (!$type_contact) {
  128. $this->error = $langs->trans("ErrorWrongValueForParameter", "2");
  129. dol_syslog(get_class($this) . "::add_contact " . $this->error, LOG_ERR);
  130. return -2;
  131. }
  132. $id_type_contact = 0;
  133. if (is_numeric($type_contact)) {
  134. $id_type_contact = $type_contact;
  135. } else {
  136. // On recherche id type_contact
  137. $sql = "SELECT tc.rowid";
  138. $sql.= " FROM " . MAIN_DB_PREFIX . "c_type_contact as tc";
  139. $sql.= " WHERE element='" . $this->element . "'";
  140. $sql.= " AND source='" . $source . "'";
  141. $sql.= " AND code='" . $type_contact . "' AND active=1";
  142. $resql = $this->db->query($sql);
  143. if ($resql) {
  144. $obj = $this->db->fetch_object($resql);
  145. $id_type_contact = $obj->rowid;
  146. }
  147. }
  148. $datecreate = dol_now();
  149. // Insertion dans la base
  150. $sql = "INSERT INTO " . MAIN_DB_PREFIX . "element_contact";
  151. $sql.= " (element_id, fk_socpeople, datecreate, statut, fk_c_type_contact) ";
  152. $sql.= " VALUES (" . $this->id . ", " . $fk_socpeople . " , ";
  153. $sql.= $this->db->idate($datecreate);
  154. $sql.= ", 4, '" . $id_type_contact . "' ";
  155. $sql.= ")";
  156. dol_syslog(get_class($this) . "::add_contact sql=" . $sql);
  157. $resql = $this->db->query($sql);
  158. if ($resql) {
  159. if (!$notrigger) {
  160. // Call triggers
  161. include_once DOL_DOCUMENT_ROOT . '/core/class/interfaces.class.php';
  162. $interface = new Interfaces($this->db);
  163. $result = $interface->run_triggers(strtoupper($this->element) . '_ADD_CONTACT', $this, $user, $langs, $conf);
  164. if ($result < 0) {
  165. $error++;
  166. $this->errors = $interface->errors;
  167. }
  168. // End call triggers
  169. }
  170. return 1;
  171. } else {
  172. if ($this->db->errno() == 'DB_ERROR_RECORD_ALREADY_EXISTS') {
  173. $this->error = $this->db->errno();
  174. return -2;
  175. } else {
  176. $this->error = $this->db->error();
  177. dol_syslog($this->error, LOG_ERR);
  178. return -1;
  179. }
  180. }
  181. }
  182. /**
  183. * Update a link to contact line
  184. *
  185. * @param int $rowid Id of line contact-element
  186. * @param int $statut New status of link
  187. * @param int $type_contact_id Id of contact type (not modified if 0)
  188. * @return int <0 if KO, >= 0 if OK
  189. */
  190. function update_contact($rowid, $statut, $type_contact_id = 0) {
  191. // Insertion dans la base
  192. $sql = "UPDATE " . MAIN_DB_PREFIX . "element_contact set";
  193. $sql.= " statut = " . $statut;
  194. if ($type_contact_id)
  195. $sql.= ", fk_c_type_contact = '" . $type_contact_id . "'";
  196. $sql.= " where rowid = " . $rowid;
  197. $resql = $this->db->query($sql);
  198. if ($resql) {
  199. return 0;
  200. } else {
  201. $this->error = $this->db->lasterror();
  202. return -1;
  203. }
  204. }
  205. /**
  206. * Delete a link to contact line
  207. *
  208. * @param int $rowid Id of contact link line to delete
  209. * @param int $notrigger Disable all triggers
  210. * @return int >0 if OK, <0 if KO
  211. */
  212. function delete_contact($rowid, $notrigger = 0) {
  213. global $user, $langs, $conf;
  214. $error = 0;
  215. $sql = "DELETE FROM " . MAIN_DB_PREFIX . "element_contact";
  216. $sql.= " WHERE rowid =" . $rowid;
  217. dol_syslog(get_class($this) . "::delete_contact sql=" . $sql);
  218. if ($this->db->query($sql)) {
  219. if (!$notrigger) {
  220. // Call triggers
  221. include_once DOL_DOCUMENT_ROOT . '/core/class/interfaces.class.php';
  222. $interface = new Interfaces($this->db);
  223. $result = $interface->run_triggers(strtoupper($this->element) . '_DELETE_CONTACT', $this, $user, $langs, $conf);
  224. if ($result < 0) {
  225. $error++;
  226. $this->errors = $interface->errors;
  227. }
  228. // End call triggers
  229. }
  230. return 1;
  231. } else {
  232. $this->error = $this->db->lasterror();
  233. dol_syslog(get_class($this) . "::delete_contact error=" . $this->error, LOG_ERR);
  234. return -1;
  235. }
  236. }
  237. /**
  238. * Delete all links between an object $this and all its contacts
  239. *
  240. * @return int >0 if OK, <0 if KO
  241. */
  242. function delete_linked_contact() {
  243. $temp = array();
  244. $typeContact = $this->liste_type_contact('');
  245. foreach ($typeContact as $key => $value) {
  246. array_push($temp, $key);
  247. }
  248. $listId = implode(",", $temp);
  249. $sql = "DELETE FROM " . MAIN_DB_PREFIX . "element_contact";
  250. $sql.= " WHERE element_id =" . $this->id;
  251. $sql.= " AND fk_c_type_contact IN (" . $listId . ")";
  252. dol_syslog(get_class($this) . "::delete_linked_contact sql=" . $sql, LOG_DEBUG);
  253. if ($this->db->query($sql)) {
  254. return 1;
  255. } else {
  256. $this->error = $this->db->lasterror();
  257. dol_syslog(get_class($this) . "::delete_linked_contact error=" . $this->error, LOG_ERR);
  258. return -1;
  259. }
  260. }
  261. /**
  262. * Get array of all contacts for an object
  263. *
  264. * @param int $statut Status of lines to get (-1=all)
  265. * @param string $source Source of contact: external or thirdparty (llx_socpeople) or internal (llx_user)
  266. * @param int $list 0:Return array contains all properties, 1:Return array contains just id
  267. * @return array Array of contacts
  268. */
  269. function liste_contact($statut = -1, $source = 'external', $list = 0) {
  270. global $langs;
  271. $tab = array();
  272. $sql = "SELECT ec.rowid, ec.statut, ec.fk_socpeople as id"; // This field contains id of llx_socpeople or id of llx_user
  273. if ($source == 'internal')
  274. $sql.=", '-1' as socid";
  275. if ($source == 'external' || $source == 'thirdparty')
  276. $sql.=", t.fk_soc as socid";
  277. $sql.= ", t.civilite as civility, t.name as lastname, t.firstname, t.email";
  278. $sql.= ", tc.source, tc.element, tc.code, tc.libelle";
  279. $sql.= " FROM " . MAIN_DB_PREFIX . "c_type_contact tc";
  280. $sql.= ", " . MAIN_DB_PREFIX . "element_contact ec";
  281. if ($source == 'internal')
  282. $sql.=" LEFT JOIN " . MAIN_DB_PREFIX . "user t on ec.fk_socpeople = t.rowid";
  283. if ($source == 'external' || $source == 'thirdparty')
  284. $sql.=" LEFT JOIN " . MAIN_DB_PREFIX . "socpeople t on ec.fk_socpeople = t.rowid";
  285. $sql.= " WHERE ec.element_id =" . $this->id;
  286. $sql.= " AND ec.fk_c_type_contact=tc.rowid";
  287. $sql.= " AND tc.element='" . $this->element . "'";
  288. if ($source == 'internal')
  289. $sql.= " AND tc.source = 'internal'";
  290. if ($source == 'external' || $source == 'thirdparty')
  291. $sql.= " AND tc.source = 'external'";
  292. $sql.= " AND tc.active=1";
  293. if ($statut >= 0)
  294. $sql.= " AND ec.statut = '" . $statut . "'";
  295. $sql.=" ORDER BY t.name ASC";
  296. dol_syslog(get_class($this) . "::liste_contact sql=" . $sql);
  297. $resql = $this->db->query($sql);
  298. if ($resql) {
  299. $num = $this->db->num_rows($resql);
  300. $i = 0;
  301. while ($i < $num) {
  302. $obj = $this->db->fetch_object($resql);
  303. if (!$list) {
  304. $transkey = "TypeContact_" . $obj->element . "_" . $obj->source . "_" . $obj->code;
  305. $libelle_type = ($langs->trans($transkey) != $transkey ? $langs->trans($transkey) : $obj->libelle);
  306. $tab[$i] = array('source' => $obj->source, 'socid' => $obj->socid, 'id' => $obj->id,
  307. 'nom' => $obj->lastname, // For backward compatibility
  308. 'civility' => $obj->civility, 'lastname' => $obj->lastname, 'firstname' => $obj->firstname, 'email' => $obj->email,
  309. 'rowid' => $obj->rowid, 'code' => $obj->code, 'libelle' => $libelle_type, 'status' => $obj->statut);
  310. } else {
  311. $tab[$i] = $obj->id;
  312. }
  313. $i++;
  314. }
  315. return $tab;
  316. } else {
  317. $this->error = $this->db->error();
  318. dol_print_error($this->db);
  319. return -1;
  320. }
  321. }
  322. /**
  323. * Update status of a contact linked to object
  324. *
  325. * @param int $rowid Id of link between object and contact
  326. * @return int <0 if KO, >=0 if OK
  327. */
  328. function swapContactStatus($rowid) {
  329. $sql = "SELECT ec.datecreate, ec.statut, ec.fk_socpeople, ec.fk_c_type_contact,";
  330. $sql.= " tc.code, tc.libelle";
  331. //$sql.= ", s.fk_soc";
  332. $sql.= " FROM (" . MAIN_DB_PREFIX . "element_contact as ec, " . MAIN_DB_PREFIX . "c_type_contact as tc)";
  333. //$sql.= " LEFT JOIN ".MAIN_DB_PREFIX."socpeople as s ON ec.fk_socpeople=s.rowid"; // Si contact de type external, alors il est lie a une societe
  334. $sql.= " WHERE ec.rowid =" . $rowid;
  335. $sql.= " AND ec.fk_c_type_contact=tc.rowid";
  336. $sql.= " AND tc.element = '" . $this->element . "'";
  337. dol_syslog(get_class($this) . "::swapContactStatus sql=" . $sql);
  338. $resql = $this->db->query($sql);
  339. if ($resql) {
  340. $obj = $this->db->fetch_object($resql);
  341. $newstatut = ($obj->statut == 4) ? 5 : 4;
  342. $result = $this->update_contact($rowid, $newstatut);
  343. $this->db->free($resql);
  344. return $result;
  345. } else {
  346. $this->error = $this->db->error();
  347. dol_print_error($this->db);
  348. return -1;
  349. }
  350. }
  351. /**
  352. * Return array with list of possible values for type of contacts
  353. *
  354. * @param string $source 'internal', 'external' or 'all'
  355. * @param string $order Sort order by : 'code' or 'rowid'
  356. * @param string $option 0=Return array id->label, 1=Return array code->label
  357. * @return array Array list of type of contacts (id->label if option=0, code->label if option=1)
  358. */
  359. function liste_type_contact($source = 'internal', $order = 'code', $option = 0) {
  360. global $langs;
  361. $tab = array();
  362. $sql = "SELECT DISTINCT tc.rowid, tc.code, tc.libelle";
  363. $sql.= " FROM " . MAIN_DB_PREFIX . "c_type_contact as tc";
  364. $sql.= " WHERE tc.element='" . $this->element . "'";
  365. if (!empty($source))
  366. $sql.= " AND tc.source='" . $source . "'";
  367. $sql.= " ORDER by tc." . $order;
  368. //print "sql=".$sql;
  369. $resql = $this->db->query($sql);
  370. if ($resql) {
  371. $num = $this->db->num_rows($resql);
  372. $i = 0;
  373. while ($i < $num) {
  374. $obj = $this->db->fetch_object($resql);
  375. $transkey = "TypeContact_" . $this->element . "_" . $source . "_" . $obj->code;
  376. $libelle_type = ($langs->trans($transkey) != $transkey ? $langs->trans($transkey) : $obj->libelle);
  377. if (empty($option))
  378. $tab[$obj->rowid] = $libelle_type;
  379. else
  380. $tab[$obj->code] = $libelle_type;
  381. $i++;
  382. }
  383. return $tab;
  384. }
  385. else {
  386. $this->error = $this->db->lasterror();
  387. //dol_print_error($this->db);
  388. return null;
  389. }
  390. }
  391. /**
  392. * Return id of contacts for a source and a contact code.
  393. * Example: contact client de facturation ('external', 'BILLING')
  394. * Example: contact client de livraison ('external', 'SHIPPING')
  395. * Example: contact interne suivi paiement ('internal', 'SALESREPFOLL')
  396. *
  397. * @param string $source 'external' or 'internal'
  398. * @param string $code 'BILLING', 'SHIPPING', 'SALESREPFOLL', ...
  399. * @param int $status limited to a certain status
  400. * @return array List of id for such contacts
  401. */
  402. function getIdContact($source, $code, $status = 0) {
  403. global $conf;
  404. $result = array();
  405. $i = 0;
  406. $sql = "SELECT ec.fk_socpeople";
  407. $sql.= " FROM " . MAIN_DB_PREFIX . "element_contact as ec,";
  408. if ($source == 'internal')
  409. $sql.= " " . MAIN_DB_PREFIX . "user as c,";
  410. if ($source == 'external')
  411. $sql.= " " . MAIN_DB_PREFIX . "socpeople as c,";
  412. $sql.= " " . MAIN_DB_PREFIX . "c_type_contact as tc";
  413. $sql.= " WHERE ec.element_id = " . $this->id;
  414. $sql.= " AND ec.fk_socpeople = c.rowid";
  415. if ($source == 'internal')
  416. $sql.= " AND c.entity IN (0," . $conf->entity . ")";
  417. if ($source == 'external')
  418. $sql.= " AND c.entity IN (" . getEntity('societe', 1) . ")";
  419. $sql.= " AND ec.fk_c_type_contact = tc.rowid";
  420. $sql.= " AND tc.element = '" . $this->element . "'";
  421. $sql.= " AND tc.source = '" . $source . "'";
  422. $sql.= " AND tc.code = '" . $code . "'";
  423. $sql.= " AND tc.active = 1";
  424. if ($status)
  425. $sql.= " AND ec.statut = " . $status;
  426. dol_syslog(get_class($this) . "::getIdContact sql=" . $sql);
  427. $resql = $this->db->query($sql);
  428. if ($resql) {
  429. while ($obj = $this->db->fetch_object($resql)) {
  430. $result[$i] = $obj->fk_socpeople;
  431. $i++;
  432. }
  433. } else {
  434. $this->error = $this->db->error();
  435. dol_syslog(get_class($this) . "::getIdContact " . $this->error, LOG_ERR);
  436. return null;
  437. }
  438. return $result;
  439. }
  440. /**
  441. * Charge le contact d'id $id dans this->contact
  442. *
  443. * @param int $contactid Id du contact
  444. * @return int <0 if KO, >0 if OK
  445. */
  446. function fetch_contact($contactid) {
  447. require_once DOL_DOCUMENT_ROOT . '/contact/class/contact.class.php';
  448. $contact = new Contact($this->db);
  449. $result = $contact->fetch($contactid);
  450. $this->contact = $contact;
  451. return $result;
  452. }
  453. /**
  454. * Load the third party of object from id $this->socid into this->thirdpary
  455. *
  456. * @return int <0 if KO, >0 if OK
  457. */
  458. function fetch_thirdparty() {
  459. global $conf;
  460. if (empty($this->socid))
  461. return 0;
  462. $thirdparty = new Societe($this->db);
  463. $result = $thirdparty->fetch($this->socid);
  464. //$this->client = $thirdparty; // deprecated
  465. $this->thirdparty = $thirdparty;
  466. // Use first price level if level not defined for third party
  467. if (!empty($conf->global->PRODUIT_MULTIPRICES) && empty($this->thirdparty->price_level)) {
  468. $this->client->price_level = 1; // deprecated
  469. $this->thirdparty->price_level = 1;
  470. }
  471. return $result;
  472. }
  473. /**
  474. * Load data for barcode
  475. *
  476. * @return int <0 if KO, >=0 if OK
  477. */
  478. function fetch_barcode() {
  479. global $conf;
  480. dol_syslog(get_class($this) . '::fetch_barcode this->element=' . $this->element . ' this->barcode_type=' . $this->barcode_type);
  481. $idtype = $this->barcode_type;
  482. if (!$idtype) {
  483. if ($this->element == 'product')
  484. $idtype = $conf->global->PRODUIT_DEFAULT_BARCODE_TYPE;
  485. else if ($this->element == 'societe')
  486. $idtype = $conf->global->GENBARCODE_BARCODETYPE_THIRDPARTY;
  487. else
  488. dol_print_error('', 'Call fetch_barcode with barcode_type not defined and cant be guessed');
  489. }
  490. if ($idtype > 0) {
  491. if (empty($this->barcode_type) || empty($this->barcode_type_code) || empty($this->barcode_type_label) || empty($this->barcode_type_coder)) { // If data not already loaded
  492. $sql = "SELECT rowid, code, libelle as label, coder";
  493. $sql.= " FROM " . MAIN_DB_PREFIX . "c_barcode_type";
  494. $sql.= " WHERE rowid = " . $idtype;
  495. dol_syslog(get_class($this) . '::fetch_barcode sql=' . $sql);
  496. $resql = $this->db->query($sql);
  497. if ($resql) {
  498. $obj = $this->db->fetch_object($resql);
  499. $this->barcode_type = $obj->rowid;
  500. $this->barcode_type_code = $obj->code;
  501. $this->barcode_type_label = $obj->label;
  502. $this->barcode_type_coder = $obj->coder;
  503. return 1;
  504. } else {
  505. dol_print_error($this->db);
  506. return -1;
  507. }
  508. }
  509. }
  510. else
  511. return 0;
  512. }
  513. /**
  514. * Charge le projet d'id $this->fk_project dans this->projet
  515. *
  516. * @return int <0 if KO, >=0 if OK
  517. */
  518. function fetch_projet() {
  519. if (empty($this->fk_project))
  520. return 0;
  521. $project = new Project($this->db);
  522. $result = $project->fetch($this->fk_project);
  523. $this->projet = $project;
  524. return $result;
  525. }
  526. /**
  527. * Charge le user d'id userid dans this->user
  528. *
  529. * @param int $userid Id du contact
  530. * @return int <0 if KO, >0 if OK
  531. */
  532. function fetch_user($userid) {
  533. $user = new User($this->db);
  534. $result = $user->fetch($userid);
  535. $this->user = $user;
  536. return $result;
  537. }
  538. /**
  539. * Read linked origin object
  540. *
  541. * @return void
  542. */
  543. function fetch_origin() {
  544. // TODO uniformise code
  545. if ($this->origin == 'shipping')
  546. $this->origin = 'expedition';
  547. if ($this->origin == 'delivery')
  548. $this->origin = 'livraison';
  549. $object = $this->origin;
  550. $classname = ucfirst($object);
  551. $this->$object = new $classname($this->db);
  552. $this->$object->fetch($this->origin_id);
  553. }
  554. /**
  555. * Load object from specific field
  556. *
  557. * @param string $table Table element or element line
  558. * @param string $field Field selected
  559. * @param string $key Import key
  560. * @return int <0 if KO, >0 if OK
  561. */
  562. function fetchObjectFrom($table, $field, $key) {
  563. global $conf;
  564. $result = false;
  565. $sql = "SELECT rowid FROM " . MAIN_DB_PREFIX . $table;
  566. $sql.= " WHERE " . $field . " = '" . $key . "'";
  567. $sql.= " AND entity = " . $conf->entity;
  568. dol_syslog(get_class($this) . '::fetchObjectFrom sql=' . $sql);
  569. $resql = $this->db->query($sql);
  570. if ($resql) {
  571. $row = $this->db->fetch_row($resql);
  572. $result = $this->fetch($row[0]);
  573. }
  574. return $result;
  575. }
  576. /**
  577. * Load value from specific field
  578. *
  579. * @param string $table Table of element or element line
  580. * @param int $id Element id
  581. * @param string $field Field selected
  582. * @return int <0 if KO, >0 if OK
  583. */
  584. function getValueFrom($table, $id, $field) {
  585. $result = false;
  586. $sql = "SELECT " . $field . " FROM " . MAIN_DB_PREFIX . $table;
  587. $sql.= " WHERE rowid = " . $id;
  588. dol_syslog(get_class($this) . '::getValueFrom sql=' . $sql);
  589. $resql = $this->db->query($sql);
  590. if ($resql) {
  591. $row = $this->db->fetch_row($resql);
  592. $result = $row[0];
  593. }
  594. return $result;
  595. }
  596. /**
  597. * Update a specific field from an object
  598. *
  599. * @param string $field Field to update
  600. * @param mixte $value New value
  601. * @param string $table To force other table element or element line
  602. * @param int $id To force other object id
  603. * @param string $format Data format ('text' by default, 'date')
  604. * @return int <0 if KO, >0 if OK
  605. */
  606. function setValueFrom($field, $value, $table = '', $id = '', $format = 'text') {
  607. global $conf;
  608. if (empty($table))
  609. $table = $this->table_element;
  610. if (empty($id))
  611. $id = $this->id;
  612. $this->db->begin();
  613. $sql = "UPDATE " . MAIN_DB_PREFIX . $table . " SET ";
  614. if ($format == 'text')
  615. $sql.= $field . " = '" . $this->db->escape($value) . "'";
  616. else if ($format == 'date')
  617. $sql.= $field . " = '" . $this->db->idate($value) . "'";
  618. $sql.= " WHERE rowid = " . $id;
  619. dol_syslog(get_class($this) . "::setValueFrom sql=" . $sql, LOG_DEBUG);
  620. $resql = $this->db->query($sql);
  621. if ($resql) {
  622. $this->db->commit();
  623. return 1;
  624. } else {
  625. $this->error = $this->db->lasterror();
  626. $this->db->rollback();
  627. return -1;
  628. }
  629. }
  630. /**
  631. * Load properties id_previous and id_next
  632. *
  633. * @param string $filter Optional filter
  634. * @param int $fieldid Name of field to use for the select MAX and MIN
  635. * @return int <0 if KO, >0 if OK
  636. */
  637. function load_previous_next_ref($filter, $fieldid) {
  638. global $conf, $user;
  639. if (!$this->table_element) {
  640. dol_print_error('', get_class($this) . "::load_previous_next_ref was called on objet with property table_element not defined", LOG_ERR);
  641. return -1;
  642. }
  643. // this->ismultientitymanaged contains
  644. // 0=No test on entity, 1=Test with field entity, 2=Test with link by societe
  645. $alias = 's';
  646. if ($this->element == 'societe')
  647. $alias = 'te';
  648. $sql = "SELECT MAX(te." . $fieldid . ")";
  649. $sql.= " FROM " . MAIN_DB_PREFIX . $this->table_element . " as te";
  650. if (isset($this->ismultientitymanaged) && $this->ismultientitymanaged == 2 || ($this->element != 'societe' && empty($this->isnolinkedbythird) && empty($user->rights->societe->client->voir)))
  651. $sql.= ", " . MAIN_DB_PREFIX . "societe as s"; // If we need to link to societe to limit select to entity
  652. if (empty($this->isnolinkedbythird) && !$user->rights->societe->client->voir)
  653. $sql.= " LEFT JOIN " . MAIN_DB_PREFIX . "societe_commerciaux as sc ON " . $alias . ".rowid = sc.fk_soc";
  654. $sql.= " WHERE te." . $fieldid . " < '" . $this->db->escape($this->ref) . "'";
  655. if (empty($this->isnolinkedbythird) && !$user->rights->societe->client->voir)
  656. $sql.= " AND sc.fk_user = " . $user->id;
  657. if (!empty($filter))
  658. $sql.=" AND " . $filter;
  659. if (isset($this->ismultientitymanaged) && $this->ismultientitymanaged == 2 || ($this->element != 'societe' && empty($this->isnolinkedbythird) && !$user->rights->societe->client->voir))
  660. $sql.= ' AND te.fk_soc = s.rowid'; // If we need to link to societe to limit select to entity
  661. if (isset($this->ismultientitymanaged) && $this->ismultientitymanaged == 1)
  662. $sql.= ' AND te.entity IN (' . getEntity($this->element, 1) . ')';
  663. //print $sql."<br>";
  664. $result = $this->db->query($sql);
  665. if (!$result) {
  666. $this->error = $this->db->error();
  667. return -1;
  668. }
  669. $row = $this->db->fetch_row($result);
  670. $this->ref_previous = $row[0];
  671. $sql = "SELECT MIN(te." . $fieldid . ")";
  672. $sql.= " FROM " . MAIN_DB_PREFIX . $this->table_element . " as te";
  673. if (isset($this->ismultientitymanaged) && $this->ismultientitymanaged == 2 || ($this->element != 'societe' && empty($this->isnolinkedbythird) && !$user->rights->societe->client->voir))
  674. $sql.= ", " . MAIN_DB_PREFIX . "societe as s"; // If we need to link to societe to limit select to entity
  675. if (empty($this->isnolinkedbythird) && !$user->rights->societe->client->voir)
  676. $sql.= " LEFT JOIN " . MAIN_DB_PREFIX . "societe_commerciaux as sc ON " . $alias . ".rowid = sc.fk_soc";
  677. $sql.= " WHERE te." . $fieldid . " > '" . $this->db->escape($this->ref) . "'";
  678. if (empty($this->isnolinkedbythird) && !$user->rights->societe->client->voir)
  679. $sql.= " AND sc.fk_user = " . $user->id;
  680. if (!empty($filter))
  681. $sql.=" AND " . $filter;
  682. if (isset($this->ismultientitymanaged) && $this->ismultientitymanaged == 2 || ($this->element != 'societe' && empty($this->isnolinkedbythird) && !$user->rights->societe->client->voir))
  683. $sql.= ' AND te.fk_soc = s.rowid'; // If we need to link to societe to limit select to entity
  684. if (isset($this->ismultientitymanaged) && $this->ismultientitymanaged == 1)
  685. $sql.= ' AND te.entity IN (' . getEntity($this->element, 1) . ')';
  686. // Rem: Bug in some mysql version: SELECT MIN(rowid) FROM llx_socpeople WHERE rowid > 1 when one row in database with rowid=1, returns 1 instead of null
  687. //print $sql."<br>";
  688. $result = $this->db->query($sql);
  689. if (!$result) {
  690. $this->error = $this->db->error();
  691. return -2;
  692. }
  693. $row = $this->db->fetch_row($result);
  694. $this->ref_next = $row[0];
  695. return 1;
  696. }
  697. /**
  698. * Return list of id of contacts of project
  699. *
  700. * @param string $source Source of contact: external (llx_socpeople) or internal (llx_user) or thirdparty (llx_societe)
  701. * @return array Array of id of contacts (if source=external or internal)
  702. * Array of id of third parties with at least one contact on project (if source=thirdparty)
  703. */
  704. function getListContactId($source = 'external') {
  705. $contactAlreadySelected = array();
  706. $tab = $this->liste_contact(-1, $source);
  707. $num = count($tab);
  708. $i = 0;
  709. while ($i < $num) {
  710. if ($source == 'thirdparty')
  711. $contactAlreadySelected[$i] = $tab[$i]['socid'];
  712. else
  713. $contactAlreadySelected[$i] = $tab[$i]['id'];
  714. $i++;
  715. }
  716. return $contactAlreadySelected;
  717. }
  718. /**
  719. * Link element with a project
  720. *
  721. * @param int $projectid Project id to link element to
  722. * @return int <0 if KO, >0 if OK
  723. */
  724. function setProject($projectid) {
  725. if (!$this->table_element) {
  726. dol_syslog(get_class($this) . "::setProject was called on objet with property table_element not defined", LOG_ERR);
  727. return -1;
  728. }
  729. $sql = 'UPDATE ' . MAIN_DB_PREFIX . $this->table_element;
  730. if ($projectid)
  731. $sql.= ' SET fk_projet = ' . $projectid;
  732. else
  733. $sql.= ' SET fk_projet = NULL';
  734. $sql.= ' WHERE rowid = ' . $this->id;
  735. dol_syslog(get_class($this) . "::setProject sql=" . $sql);
  736. if ($this->db->query($sql)) {
  737. $this->fk_project = $projectid;
  738. return 1;
  739. } else {
  740. dol_print_error($this->db);
  741. return -1;
  742. }
  743. }
  744. /**
  745. * Change the payments methods
  746. *
  747. * @param int $id Id of new payment method
  748. * @return int >0 if OK, <0 if KO
  749. */
  750. function setPaymentMethods($id) {
  751. dol_syslog(get_class($this) . '::setPaymentMethods(' . $id . ')');
  752. if ($this->statut >= 0 || $this->element == 'societe') {
  753. // TODO uniformize field name
  754. $fieldname = 'fk_mode_reglement';
  755. if ($this->element == 'societe')
  756. $fieldname = 'mode_reglement';
  757. $sql = 'UPDATE ' . MAIN_DB_PREFIX . $this->table_element;
  758. $sql .= ' SET ' . $fieldname . ' = ' . $id;
  759. $sql .= ' WHERE rowid=' . $this->id;
  760. if ($this->db->query($sql)) {
  761. $this->mode_reglement_id = $id;
  762. $this->mode_reglement = $id; // for compatibility
  763. return 1;
  764. } else {
  765. dol_syslog(get_class($this) . '::setPaymentMethods Erreur ' . $sql . ' - ' . $this->db->error());
  766. $this->error = $this->db->error();
  767. return -1;
  768. }
  769. } else {
  770. dol_syslog(get_class($this) . '::setPaymentMethods, status of the object is incompatible');
  771. $this->error = 'Status of the object is incompatible ' . $this->statut;
  772. return -2;
  773. }
  774. }
  775. /**
  776. * Change the payments terms
  777. *
  778. * @param int $id Id of new payment terms
  779. * @return int >0 if OK, <0 if KO
  780. */
  781. function setPaymentTerms($id) {
  782. dol_syslog(get_class($this) . '::setPaymentTerms(' . $id . ')');
  783. if ($this->statut >= 0 || $this->element == 'societe') {
  784. // TODO uniformize field name
  785. $fieldname = 'fk_cond_reglement';
  786. if ($this->element == 'societe')
  787. $fieldname = 'cond_reglement';
  788. $sql = 'UPDATE ' . MAIN_DB_PREFIX . $this->table_element;
  789. $sql .= ' SET ' . $fieldname . ' = ' . $id;
  790. $sql .= ' WHERE rowid=' . $this->id;
  791. if ($this->db->query($sql)) {
  792. $this->cond_reglement_id = $id;
  793. $this->cond_reglement = $id; // for compatibility
  794. return 1;
  795. } else {
  796. dol_syslog(get_class($this) . '::setPaymentTerms Erreur ' . $sql . ' - ' . $this->db->error());
  797. $this->error = $this->db->error();
  798. return -1;
  799. }
  800. } else {
  801. dol_syslog(get_class($this) . '::setPaymentTerms, status of the object is incompatible');
  802. $this->error = 'Status of the object is incompatible ' . $this->statut;
  803. return -2;
  804. }
  805. }
  806. /**
  807. * Define delivery address
  808. *
  809. * @param int $id Address id
  810. * @return int <0 si ko, >0 si ok
  811. */
  812. function setDeliveryAddress($id) {
  813. $fieldname = 'fk_adresse_livraison';
  814. if ($this->element == 'delivery' || $this->element == 'shipping')
  815. $fieldname = 'fk_address';
  816. $sql = "UPDATE " . MAIN_DB_PREFIX . $this->table_element . " SET " . $fieldname . " = " . $id;
  817. $sql.= " WHERE rowid = " . $this->id . " AND fk_statut = 0";
  818. if ($this->db->query($sql)) {
  819. $this->fk_delivery_address = $id;
  820. return 1;
  821. } else {
  822. $this->error = $this->db->error();
  823. dol_syslog(get_class($this) . '::setDeliveryAddress Erreur ' . $sql . ' - ' . $this->error);
  824. return -1;
  825. }
  826. }
  827. /**
  828. * Set last model used by doc generator
  829. *
  830. * @param User $user User object that make change
  831. * @param string $modelpdf Modele name
  832. * @return int <0 if KO, >0 if OK
  833. */
  834. function setDocModel($user, $modelpdf) {
  835. $newmodelpdf = dol_trunc($modelpdf, 255);
  836. if ($this->modelpdf != $newmodelpdf) {
  837. $this->modelpdf = $newmodelpdf;
  838. $this->record();
  839. }
  840. return 1;
  841. }
  842. /**
  843. * Save a new position (field rang) for details lines.
  844. * You can choose to ser position for lines with already a position or lines wihtout any position defined.
  845. * Call this function only for table that contains a field fk_parent_line.
  846. *
  847. * @param boolean $renum true to renum all already ordered lines, false to renum only not already ordered lines.
  848. * @param string $rowidorder ASC or DESC
  849. * @return void
  850. */
  851. function line_order($renum = false, $rowidorder = 'ASC') {
  852. if (!$this->table_element_line) {
  853. dol_syslog(get_class($this) . "::line_order was called on objet with property table_element_line not defined", LOG_ERR);
  854. return -1;
  855. }
  856. if (!$this->fk_element) {
  857. dol_syslog(get_class($this) . "::line_order was called on objet with property fk_element not defined", LOG_ERR);
  858. return -1;
  859. }
  860. // Count number of lines to reorder (according to choice $renum)
  861. $nl = 0;
  862. $sql = 'SELECT count(rowid) FROM ' . MAIN_DB_PREFIX . $this->table_element_line;
  863. $sql.= ' WHERE ' . $this->fk_element . '=' . $this->id;
  864. if (!$renum)
  865. $sql.= ' AND rang = 0';
  866. if ($renum)
  867. $sql.= ' AND rang <> 0';
  868. dol_syslog(get_class($this) . "::line_order sql=" . $sql, LOG_DEBUG);
  869. $resql = $this->db->query($sql);
  870. if ($resql) {
  871. $row = $this->db->fetch_row($resql);
  872. $nl = $row[0];
  873. }
  874. else
  875. dol_print_error($this->db);
  876. if ($nl > 0) {
  877. // The goal of this part is to reorder all lines, with all children lines sharing the same
  878. // counter that parents.
  879. $rows = array();
  880. // We frist search all lines that are parent lines (for multilevel details lines)
  881. $sql = 'SELECT rowid FROM ' . MAIN_DB_PREFIX . $this->table_element_line;
  882. $sql.= ' WHERE ' . $this->fk_element . ' = ' . $this->id;
  883. $sql.= ' AND fk_parent_line IS NULL';
  884. $sql.= ' ORDER BY rang ASC, rowid ' . $rowidorder;
  885. dol_syslog(get_class($this) . "::line_order search all parent lines sql=" . $sql, LOG_DEBUG);
  886. $resql = $this->db->query($sql);
  887. if ($resql) {
  888. $i = 0;
  889. $num = $this->db->num_rows($resql);
  890. while ($i < $num) {
  891. $row = $this->db->fetch_row($resql);
  892. $rows[] = $row[0]; // Add parent line into array rows
  893. $childrens = $this->getChildrensOfLine($row[0]);
  894. if (!empty($childrens)) {
  895. foreach ($childrens as $child) {
  896. array_push($rows, $child);
  897. }
  898. }
  899. $i++;
  900. }
  901. // Now we set a new number for each lines (parent and children with children included into parent tree)
  902. if (!empty($rows)) {
  903. foreach ($rows as $key => $row) {
  904. $this->updateRangOfLine($row, ($key + 1));
  905. }
  906. }
  907. } else {
  908. dol_print_error($this->db);
  909. }
  910. }
  911. }
  912. /**
  913. * Get childrens of line
  914. *
  915. * @param int $id Id of parent line
  916. * @return array Array with list of child lines id
  917. */
  918. function getChildrensOfLine($id) {
  919. $rows = array();
  920. $sql = 'SELECT rowid FROM ' . MAIN_DB_PREFIX . $this->table_element_line;
  921. $sql.= ' WHERE ' . $this->fk_element . ' = ' . $this->id;
  922. $sql.= ' AND fk_parent_line = ' . $id;
  923. $sql.= ' ORDER BY rang ASC';
  924. dol_syslog(get_class($this) . "::getChildrenOfLines search children lines for line " . $id . " sql=" . $sql, LOG_DEBUG);
  925. $resql = $this->db->query($sql);
  926. if ($resql) {
  927. $i = 0;
  928. $num = $this->db->num_rows($resql);
  929. while ($i < $num) {
  930. $row = $this->db->fetch_row($resql);
  931. $rows[$i] = $row[0];
  932. $i++;
  933. }
  934. }
  935. return $rows;
  936. }
  937. /**
  938. * Update a line to have a lower rank
  939. *
  940. * @param int $rowid Id of line
  941. * @return void
  942. */
  943. function line_up($rowid) {
  944. $this->line_order();
  945. // Get rang of line
  946. $rang = $this->getRangOfLine($rowid);
  947. // Update position of line
  948. $this->updateLineUp($rowid, $rang);
  949. }
  950. /**
  951. * Update a line to have a higher rank
  952. *
  953. * @param int $rowid Id of line
  954. * @return void
  955. */
  956. function line_down($rowid) {
  957. $this->line_order();
  958. // Get rang of line
  959. $rang = $this->getRangOfLine($rowid);
  960. // Get max value for rang
  961. $max = $this->line_max();
  962. // Update position of line
  963. $this->updateLineDown($rowid, $rang, $max);
  964. }
  965. /**
  966. * Update position of line (rang)
  967. *
  968. * @param int $rowid Id of line
  969. * @param int $rang Position
  970. * @return void
  971. */
  972. function updateRangOfLine($rowid, $rang) {
  973. $sql = 'UPDATE ' . MAIN_DB_PREFIX . $this->table_element_line . ' SET rang = ' . $rang;
  974. $sql.= ' WHERE rowid = ' . $rowid;
  975. dol_syslog(get_class($this) . "::updateRangOfLine sql=" . $sql, LOG_DEBUG);
  976. if (!$this->db->query($sql)) {
  977. dol_print_error($this->db);
  978. }
  979. }
  980. /**
  981. * Update position of line with ajax (rang)
  982. *
  983. * @param array $rows Array of rows
  984. * @return void
  985. */
  986. function line_ajaxorder($rows) {
  987. $num = count($rows);
  988. for ($i = 0; $i < $num; $i++) {
  989. $this->updateRangOfLine($rows[$i], ($i + 1));
  990. }
  991. }
  992. /**
  993. * Update position of line up (rang)
  994. *
  995. * @param int $rowid Id of line
  996. * @param int $rang Position
  997. * @return void
  998. */
  999. function updateLineUp($rowid, $rang) {
  1000. if ($rang > 1) {
  1001. $sql = 'UPDATE ' . MAIN_DB_PREFIX . $this->table_element_line . ' SET rang = ' . $rang;
  1002. $sql.= ' WHERE ' . $this->fk_element . ' = ' . $this->id;
  1003. $sql.= ' AND rang = ' . ($rang - 1);
  1004. if ($this->db->query($sql)) {
  1005. $sql = 'UPDATE ' . MAIN_DB_PREFIX . $this->table_element_line . ' SET rang = ' . ($rang - 1);
  1006. $sql.= ' WHERE rowid = ' . $rowid;
  1007. if (!$this->db->query($sql)) {
  1008. dol_print_error($this->db);
  1009. }
  1010. } else {
  1011. dol_print_error($this->db);
  1012. }
  1013. }
  1014. }
  1015. /**
  1016. * Update position of line down (rang)
  1017. *
  1018. * @param int $rowid Id of line
  1019. * @param int $rang Position
  1020. * @param int $max Max
  1021. * @return void
  1022. */
  1023. function updateLineDown($rowid, $rang, $max) {
  1024. if ($rang < $max) {
  1025. $sql = 'UPDATE ' . MAIN_DB_PREFIX . $this->table_element_line . ' SET rang = ' . $rang;
  1026. $sql.= ' WHERE ' . $this->fk_element . ' = ' . $this->id;
  1027. $sql.= ' AND rang = ' . ($rang + 1);
  1028. if ($this->db->query($sql)) {
  1029. $sql = 'UPDATE ' . MAIN_DB_PREFIX . $this->table_element_line . ' SET rang = ' . ($rang + 1);
  1030. $sql.= ' WHERE rowid = ' . $rowid;
  1031. if (!$this->db->query($sql)) {
  1032. dol_print_error($this->db);
  1033. }
  1034. } else {
  1035. dol_print_error($this->db);
  1036. }
  1037. }
  1038. }
  1039. /**
  1040. * Get position of line (rang)
  1041. *
  1042. * @param int $rowid Id of line
  1043. * @return int Value of rang in table of lines
  1044. */
  1045. function getRangOfLine($rowid) {
  1046. $sql = 'SELECT rang FROM ' . MAIN_DB_PREFIX . $this->table_element_line;
  1047. $sql.= ' WHERE rowid =' . $rowid;
  1048. dol_syslog(get_class($this) . "::getRangOfLine sql=" . $sql, LOG_DEBUG);
  1049. $resql = $this->db->query($sql);
  1050. if ($resql) {
  1051. $row = $this->db->fetch_row($resql);
  1052. return $row[0];
  1053. }
  1054. }
  1055. /**
  1056. * Get rowid of the line relative to its position
  1057. *
  1058. * @param int $rang Rang value
  1059. * @return int Rowid of the line
  1060. */
  1061. function getIdOfLine($rang) {
  1062. $sql = 'SELECT rowid FROM ' . MAIN_DB_PREFIX . $this->table_element_line;
  1063. $sql.= ' WHERE ' . $this->fk_element . ' = ' . $this->id;
  1064. $sql.= ' AND rang = ' . $rang;
  1065. $resql = $this->db->query($sql);
  1066. if ($resql) {
  1067. $row = $this->db->fetch_row($resql);
  1068. return $row[0];
  1069. }
  1070. }
  1071. /**
  1072. * Get max value used for position of line (rang)
  1073. *
  1074. * @param int $fk_parent_line Parent line id
  1075. * @return int Max value of rang in table of lines
  1076. */
  1077. function line_max($fk_parent_line = 0) {
  1078. // Search the last rang with fk_parent_line
  1079. if ($fk_parent_line) {
  1080. $sql = 'SELECT max(rang) FROM ' . MAIN_DB_PREFIX . $this->table_element_line;
  1081. $sql.= ' WHERE ' . $this->fk_element . ' = ' . $this->id;
  1082. $sql.= ' AND fk_parent_line = ' . $fk_parent_line;
  1083. dol_syslog(get_class($this) . "::line_max sql=" . $sql, LOG_DEBUG);
  1084. $resql = $this->db->query($sql);
  1085. if ($resql) {
  1086. $row = $this->db->fetch_row($resql);
  1087. if (!empty($row[0])) {
  1088. return $row[0];
  1089. } else {
  1090. return $this->getRangOfLine($fk_parent_line);
  1091. }
  1092. }
  1093. }
  1094. // If not, search the last rang of element
  1095. else {
  1096. $sql = 'SELECT max(rang) FROM ' . MAIN_DB_PREFIX . $this->table_element_line;
  1097. $sql.= ' WHERE ' . $this->fk_element . ' = ' . $this->id;
  1098. dol_syslog(get_class($this) . "::line_max sql=" . $sql, LOG_DEBUG);
  1099. $resql = $this->db->query($sql);
  1100. if ($resql) {
  1101. $row = $this->db->fetch_row($resql);
  1102. return $row[0];
  1103. }
  1104. }
  1105. }
  1106. /**
  1107. * Update external ref of element
  1108. *
  1109. * @param string $ref_ext Update field ref_ext
  1110. * @return int <0 if KO, >0 if OK
  1111. */
  1112. function update_ref_ext($ref_ext) {
  1113. if (!$this->table_element) {
  1114. dol_syslog(get_class($this) . "::update_ref_ext was called on objet with property table_element not defined", LOG_ERR);
  1115. return -1;
  1116. }
  1117. $sql = 'UPDATE ' . MAIN_DB_PREFIX . $this->table_element;
  1118. $sql.= " SET ref_ext = '" . $this->db->escape($ref_ext) . "'";
  1119. $sql.= " WHERE " . (isset($this->table_rowid) ? $this->table_rowid : 'rowid') . " = " . $this->id;
  1120. dol_syslog(get_class($this) . "::update_ref_ext sql=" . $sql, LOG_DEBUG);
  1121. if ($this->db->query($sql)) {
  1122. $this->ref_ext = $ref_ext;
  1123. return 1;
  1124. } else {
  1125. $this->error = $this->db->error();
  1126. dol_syslog(get_class($this) . "::update_ref_ext error=" . $this->error, LOG_ERR);
  1127. return -1;
  1128. }
  1129. }
  1130. /**
  1131. * Update private note of element
  1132. *
  1133. * @param string $note New value for note
  1134. * @return int <0 if KO, >0 if OK
  1135. */
  1136. function update_note($note) {
  1137. if (!$this->table_element) {
  1138. dol_syslog(get_class($this) . "::update_note was called on objet with property table_element not defined", LOG_ERR);
  1139. return -1;
  1140. }
  1141. $sql = 'UPDATE ' . MAIN_DB_PREFIX . $this->table_element;
  1142. // TODO uniformize fields to note_private
  1143. if ($this->table_element == 'fichinter' || $this->table_element == 'projet' || $this->table_element == 'projet_task') {
  1144. $sql.= " SET note_private = '" . $this->db->escape($note) . "'";
  1145. } else {
  1146. $sql.= " SET note = '" . $this->db->escape($note) . "'";
  1147. }
  1148. $sql.= " WHERE rowid =" . $this->id;
  1149. dol_syslog(get_class($this) . "::update_note sql=" . $sql, LOG_DEBUG);
  1150. if ($this->db->query($sql)) {
  1151. $this->note = $note; // deprecated
  1152. $this->note_private = $note;
  1153. return 1;
  1154. } else {
  1155. $this->error = $this->db->error();
  1156. dol_syslog(get_class($this) . "::update_note error=" . $this->error, LOG_ERR);
  1157. return -1;
  1158. }
  1159. }
  1160. /**
  1161. * Update public note of element
  1162. *
  1163. * @param string $note_public New value for note
  1164. * @return int <0 if KO, >0 if OK
  1165. */
  1166. function update_note_public($note_public) {
  1167. if (!$this->table_element) {
  1168. dol_syslog(get_class($this) . "::update_note_public was called on objet with property table_element not defined", LOG_ERR);
  1169. return -1;
  1170. }
  1171. $sql = 'UPDATE ' . MAIN_DB_PREFIX . $this->table_element;
  1172. $sql.= " SET note_public = '" . $this->db->escape($note_public) . "'";
  1173. $sql.= " WHERE rowid =" . $this->id;
  1174. dol_syslog(get_class($this) . "::update_note_public sql=" . $sql);
  1175. if ($this->db->query($sql)) {
  1176. $this->note_public = $note_public;
  1177. return 1;
  1178. } else {
  1179. $this->error = $this->db->error();
  1180. return -1;
  1181. }
  1182. }
  1183. /**
  1184. * Update total_ht, total_ttc and total_vat for an object (sum of lines)
  1185. *
  1186. * @param int $exclspec Exclude special product (product_type=9)
  1187. * @param int $roundingadjust -1=Use default method (MAIN_ROUNDOFTOTAL_NOT_TOTALOFROUND or 0), 0=Use total of rounding, 1=Use rounding of total
  1188. * @param int $nodatabaseupdate 1=Do not update database. Update only properties of object.
  1189. * @return int <0 if KO, >0 if OK
  1190. */
  1191. function update_price($exclspec = 0, $roundingadjust = -1, $nodatabaseupdate = 0) {
  1192. include_once DOL_DOCUMENT_ROOT . '/core/lib/price.lib.php';
  1193. if ($roundingadjust < 0 && isset($conf->global->MAIN_ROUNDOFTOTAL_NOT_TOTALOFROUND))
  1194. $roundingadjust = $conf->global->MAIN_ROUNDOFTOTAL_NOT_TOTALOFROUND;
  1195. if ($roundingadjust < 0)
  1196. $roundingadjust = 0;
  1197. $error = 0;
  1198. // Define constants to find lines to sum
  1199. $fieldtva = 'total_tva';
  1200. $fieldlocaltax1 = 'total_localtax1';
  1201. $fieldlocaltax2 = 'total_localtax2';
  1202. if ($this->element == 'facture_fourn' || $this->element == 'invoice_supplier')
  1203. $fieldtva = 'tva';
  1204. $sql = 'SELECT qty, total_ht, ' . $fieldtva . ' as total_tva, ' . $fieldlocaltax1 . ' as total_localtax1, ' . $fieldlocaltax2 . ' as total_localtax2, total_ttc,';
  1205. $sql.= ' tva_tx as vatrate';
  1206. $sql.= ' FROM ' . MAIN_DB_PREFIX . $this->table_element_line;
  1207. $sql.= ' WHERE ' . $this->fk_element . ' = ' . $this->id;
  1208. if ($exclspec) {
  1209. $product_field = 'product_type';
  1210. if ($this->table_element_line == 'contratdet')
  1211. $product_field = ''; // contratdet table has no product_type field
  1212. if ($product_field)
  1213. $sql.= ' AND ' . $product_field . ' <> 9';
  1214. }
  1215. dol_syslog(get_class($this) . "::update_price sql=" . $sql);
  1216. $resql = $this->db->query($sql);
  1217. if ($resql) {
  1218. $this->total_ht = 0;
  1219. $this->total_tva = 0;
  1220. $this->total_localtax1 = 0;
  1221. $this->total_localtax2 = 0;
  1222. $this->total_ttc = 0;
  1223. $vatrates = array();
  1224. $vatrates_alllines = array();
  1225. $num = $this->db->num_rows($resql);
  1226. $i = 0;
  1227. while ($i < $num) {
  1228. $obj = $this->db->fetch_object($resql);
  1229. $this->total_ht += $obj->total_ht;
  1230. $this->total_tva += $obj->total_tva;
  1231. $this->total_localtax1 += $obj->total_localtax1;
  1232. $this->total_localtax2 += $obj->total_localtax2;
  1233. $this->total_ttc += $obj->total_ttc;
  1234. // Define vatrates with totals for each line and for all lines
  1235. // TODO $vatrates and $vatrates_alllines not used ?
  1236. if (!empty($this->vatrate)) {
  1237. $vatrates[$this->vatrate][] = array(
  1238. 'total_ht' => $obj->total_ht,
  1239. 'total_tva' => $obj->total_tva,
  1240. 'total_ttc' => $obj->total_ttc,
  1241. 'total_localtax1' => $obj->total_localtax1,
  1242. 'total_localtax2' => $obj->total_localtax2
  1243. );
  1244. if (!isset($vatrates_alllines[$this->vatrate]['total_ht']))
  1245. $vatrates_alllines[$this->vatrate]['total_ht'] = 0;
  1246. if (!isset($vatrates_alllines[$this->vatrate]['total_tva']))
  1247. $vatrates_alllines[$this->vatrate]['total_tva'] = 0;
  1248. if (!isset($vatrates_alllines[$this->vatrate]['total_localtax1']))
  1249. $vatrates_alllines[$this->vatrate]['total_localtax1'] = 0;
  1250. if (!isset($vatrates_alllines[$this->vatrate]['total_localtax2']))
  1251. $vatrates_alllines[$this->vatrate]['total_localtax2'] = 0;
  1252. if (!isset($vatrates_alllines[$this->vatrate]['total_ttc']))
  1253. $vatrates_alllines[$this->vatrate]['total_ttc'] = 0;
  1254. $vatrates_alllines[$this->vatrate]['total_ht'] +=$obj->total_ht;
  1255. $vatrates_alllines[$this->vatrate]['total_tva'] +=$obj->total_tva;
  1256. $vatrates_alllines[$this->vatrate]['total_localtax1']+=$obj->total_localtax1;
  1257. $vatrates_alllines[$this->vatrate]['total_localtax2']+=$obj->total_localtax2;
  1258. $vatrates_alllines[$this->vatrate]['total_ttc'] +=$obj->total_ttc;
  1259. }
  1260. $i++;
  1261. }
  1262. $this->db->free($resql);
  1263. // TODO
  1264. if ($roundingadjust) {
  1265. // For each vatrate, calculate if two method of calculation differs
  1266. // If it differs
  1267. if (1 == 2) {
  1268. // Adjust a line and update it
  1269. }
  1270. }
  1271. // Now update global field total_ht, total_ttc and tva
  1272. $fieldht = 'total_ht';
  1273. $fieldtva = 'tva';
  1274. $fieldlocaltax1 = 'localtax1';
  1275. $fieldlocaltax2 = 'localtax2';
  1276. $fieldttc = 'total_ttc';
  1277. if ($this->element == 'facture' || $this->element == 'facturerec')
  1278. $fieldht = 'total';
  1279. if ($this->element == 'facture_fourn' || $this->element == 'invoice_supplier')
  1280. $fieldtva = 'total_tva';
  1281. if ($this->element == 'propal')
  1282. $fieldttc = 'total';
  1283. if (empty($nodatabaseupdate)) {
  1284. $sql = 'UPDATE ' . MAIN_DB_PREFIX . $this->table_element . ' SET';
  1285. $sql .= " " . $fieldht . "='" . price2num($this->total_ht) . "',";
  1286. $sql .= " " . $fieldtva . "='" . price2num($this->total_tva) . "',";
  1287. $sql .= " " . $fieldlocaltax1 . "='" . price2num($this->total_localtax1) . "',";
  1288. $sql .= " " . $fieldlocaltax2 . "='" . price2num($this->total_localtax2) . "',";
  1289. $sql .= " " . $fieldttc . "='" . price2num($this->total_ttc) . "'";
  1290. $sql .= ' WHERE rowid = ' . $this->id;
  1291. //print "xx".$sql;
  1292. dol_syslog(get_class($this) . "::update_price sql=" . $sql);
  1293. $resql = $this->db->query($sql);
  1294. if (!$resql) {
  1295. $error++;
  1296. $this->error = $this->db->error();
  1297. dol_syslog(get_class($this) . "::update_price error=" . $this->error, LOG_ERR);
  1298. }
  1299. }
  1300. if (!$error) {
  1301. return 1;
  1302. } else {
  1303. return -1;
  1304. }
  1305. } else {
  1306. dol_print_error($this->db, 'Bad request in update_price');
  1307. return -1;
  1308. }
  1309. }
  1310. /**
  1311. * Add objects linked in llx_element_element.
  1312. *
  1313. * @param string $origin Linked element type
  1314. * @param int $origin_id Linked element id
  1315. * @return int <=0 if KO, >0 if OK
  1316. */
  1317. function add_object_linked($origin = null, $origin_id = null) {
  1318. $origin = (!empty($origin) ? $origin : $this->origin);
  1319. $origin_id = (!empty($origin_id) ? $origin_id : $this->origin_id);
  1320. $this->db->begin();
  1321. $sql = "INSERT INTO " . MAIN_DB_PREFIX . "element_element (";
  1322. $sql.= "fk_source";
  1323. $sql.= ", sourcetype";
  1324. $sql.= ", fk_target";
  1325. $sql.= ", targettype";
  1326. $sql.= ") VALUES (";
  1327. $sql.= $origin_id;
  1328. $sql.= ", '" . $origin . "'";
  1329. $sql.= ", " . $this->id;
  1330. $sql.= ", '" . $this->element . "'";
  1331. $sql.= ")";
  1332. dol_syslog(get_class($this) . "::add_object_linked sql=" . $sql, LOG_DEBUG);
  1333. if ($this->db->query($sql)) {
  1334. $this->db->commit();
  1335. return 1;
  1336. } else {
  1337. $this->error = $this->db->lasterror();
  1338. $this->db->rollback();
  1339. return 0;
  1340. }
  1341. }
  1342. /**
  1343. * Fetch array of objects linked to current object. Links are loaded into this->linked_object array.
  1344. *
  1345. * @param int $sourceid Object source id
  1346. * @param string $sourcetype Object source type
  1347. * @param int $targetid Object target id
  1348. * @param string $targettype Object target type
  1349. * @param string $clause OR, AND clause
  1350. * @return void
  1351. */
  1352. function fetchObjectLinked($sourceid = '', $sourcetype = '', $targetid = '', $targettype = '', $clause = 'OR') {
  1353. global $conf;
  1354. $this->linkedObjectsIds = array();
  1355. $this->linkedObjects = array();
  1356. $justsource = false;
  1357. $justtarget = false;
  1358. $withtargettype = false;
  1359. $withsourcetype = false;
  1360. if (!empty($sourceid) && !empty($sourcetype) && empty($targetid)) {
  1361. $justsource = true;
  1362. if (!empty($targettype))
  1363. $withtargettype = true;
  1364. }
  1365. if (!empty($targetid) && !empty($targettype) && empty($sourceid)) {
  1366. $justtarget = true;
  1367. if (!empty($sourcetype))
  1368. $withsourcetype = true;
  1369. }
  1370. $sourceid = (!empty($sourceid) ? $sourceid : $this->id);
  1371. $targetid = (!empty($targetid) ? $targetid : $this->id);
  1372. $sourcetype = (!empty($sourcetype) ? $sourcetype : $this->element);
  1373. $targettype = (!empty($targettype) ? $targettype : $this->element);
  1374. // Links beetween objects are stored in this table
  1375. $sql = 'SELECT fk_source, sourcetype, fk_target, targettype';
  1376. $sql.= ' FROM ' . MAIN_DB_PREFIX . 'element_element';
  1377. $sql.= " WHERE ";
  1378. if ($justsource || $justtarget) {
  1379. if ($justsource) {
  1380. $sql.= "fk_source = '" . $sourceid . "' AND sourcetype = '" . $sourcetype . "'";
  1381. if ($withtargettype)
  1382. $sql.= " AND targettype = '" . $targettype . "'";
  1383. }
  1384. else if ($justtarget) {
  1385. $sql.= "fk_target = '" . $targetid . "' AND targettype = '" . $targettype . "'";
  1386. if ($withsourcetype)
  1387. $sql.= " AND sourcetype = '" . $sourcetype . "'";
  1388. }
  1389. }
  1390. else {
  1391. $sql.= "(fk_source = '" . $sourceid . "' AND sourcetype = '" . $sourcetype . "')";
  1392. $sql.= " " . $clause . " (fk_target = '" . $targetid . "' AND targettype = '" . $targettype . "')";
  1393. }
  1394. //print $sql;
  1395. dol_syslog(get_class($this) . "::fetchObjectLink sql=" . $sql);
  1396. $resql = $this->db->query($sql);
  1397. if ($resql) {
  1398. $num = $this->db->num_rows($resql);
  1399. $i = 0;
  1400. while ($i < $num) {
  1401. $obj = $this->db->fetch_object($resql);
  1402. if ($obj->fk_source == $sourceid) {
  1403. $this->linkedObjectsIds[$obj->targettype][] = $obj->fk_target;
  1404. }
  1405. if ($obj->fk_target == $targetid) {
  1406. $this->linkedObjectsIds[$obj->sourcetype][] = $obj->fk_source;
  1407. }
  1408. $i++;
  1409. }
  1410. if (!empty($this->linkedObjectsIds)) {
  1411. foreach ($this->linkedObjectsIds as $objecttype => $objectids) {
  1412. // Parse element/subelement (ex: project_task)
  1413. $module = $element = $subelement = $objecttype;
  1414. if ($objecttype != 'order_supplier' && $objecttype != 'invoice_supplier' && preg_match('/^([^_]+)_([^_]+)/i', $objecttype, $regs)) {
  1415. $module = $element = $regs[1];
  1416. $subelement = $regs[2];
  1417. }
  1418. $classpath = $element . '/class';
  1419. // To work with non standard path
  1420. if ($objecttype == 'facture') {
  1421. $classpath = 'compta/facture/class';
  1422. } else if ($objecttype == 'propal') {
  1423. $classpath = 'comm/propal/class';
  1424. } else if ($objecttype == 'shipping') {
  1425. $classpath = 'expedition/class';
  1426. $subelement = 'expedition';
  1427. $module = 'expedition_bon';
  1428. } else if ($objecttype == 'delivery') {
  1429. $classpath = 'livraison/class';
  1430. $subelement = 'livraison';
  1431. $module = 'livraison_bon';
  1432. } else if ($objecttype == 'invoice_supplier' || $objecttype == 'order_supplier') {
  1433. $classpath = 'fourn/class';
  1434. $module = 'fournisseur';
  1435. } else if ($objecttype == 'order_supplier') {
  1436. $classpath = 'fourn/class';
  1437. } else if ($objecttype == 'fichinter') {
  1438. $classpath = 'fichinter/class';
  1439. $subelement = 'fichinter';
  1440. $module = 'ficheinter';
  1441. }
  1442. // TODO ajout temporaire - MAXIME MANGIN
  1443. else if ($objecttype == 'contratabonnement') {
  1444. $classpath = 'contrat/class';
  1445. $subelement = 'contrat';
  1446. $module = 'contratabonnement';
  1447. }
  1448. $classfile = strtolower($subelement);
  1449. $classname = ucfirst($subelement);
  1450. if ($objecttype == 'invoice_supplier') {
  1451. $classfile = 'fournisseur.facture';
  1452. $classname = 'FactureFournisseur';
  1453. } else if ($objecttype == 'order_supplier') {
  1454. $classfile = 'fournisseur.commande';
  1455. $classname = 'CommandeFournisseur';
  1456. }
  1457. if ($conf->$module->enabled && $element != $this->element) {
  1458. dol_include_once('/' . $classpath . '/' . $classfile . '.class.php');
  1459. $num = count($objectids);
  1460. for ($i = 0; $i < $num; $i++) {
  1461. $object = new $classname($this->db);
  1462. $ret = $object->fetch($objectids[$i]);
  1463. if ($ret >= 0) {
  1464. $this->linkedObjects[$objecttype][$i] = $object;
  1465. }
  1466. }
  1467. }
  1468. }
  1469. }
  1470. } else {
  1471. dol_print_error($this->db);
  1472. }
  1473. }
  1474. /**
  1475. * Update object linked of a current object
  1476. *
  1477. * @param int $sourceid Object source id
  1478. * @param string $sourcetype Object source type
  1479. * @param int $targetid Object target id
  1480. * @param string $targettype Object target type
  1481. * @return int >0 if OK, <0 if KO
  1482. */
  1483. function updateObjectLinked($sourceid = '', $sourcetype = '', $targetid = '', $targettype = '') {
  1484. $updatesource = false;
  1485. $updatetarget = false;
  1486. if (!empty($sourceid) && !empty($sourcetype) && empty($targetid) && empty($targettype))
  1487. $updatesource = true;
  1488. else if (empty($sourceid) && empty($sourcetype) && !empty($targetid) && !empty($targettype))
  1489. $updatetarget = true;
  1490. $sql = "UPDATE " . MAIN_DB_PREFIX . "element_element SET ";
  1491. if ($updatesource) {
  1492. $sql.= "fk_source = " . $sourceid;
  1493. $sql.= ", sourcetype = '" . $sourcetype . "'";
  1494. $sql.= " WHERE fk_target = " . $this->id;
  1495. $sql.= " AND targettype = '" . $this->element . "'";
  1496. } else if ($updatetarget) {
  1497. $sql.= "fk_target = " . $targetid;
  1498. $sql.= ", targettype = '" . $targettype . "'";
  1499. $sql.= " WHERE fk_source = " . $this->id;
  1500. $sql.= " AND sourcetype = '" . $this->element . "'";
  1501. }
  1502. dol_syslog(get_class($this) . "::updateObjectLinked sql=" . $sql, LOG_DEBUG);
  1503. if ($this->db->query($sql)) {
  1504. return 1;
  1505. } else {
  1506. $this->error = $this->db->lasterror();
  1507. dol_syslog(get_class($this) . "::updateObjectLinked error=" . $this->error, LOG_ERR);
  1508. return -1;
  1509. }
  1510. }
  1511. /**
  1512. * Delete all links between an object $this
  1513. *
  1514. * @param int $sourceid Object source id
  1515. * @param string $sourcetype Object source type
  1516. * @param int $targetid Object target id
  1517. * @param string $targettype Object target type
  1518. * @return int >0 if OK, <0 if KO
  1519. */
  1520. function deleteObjectLinked($sourceid = '', $sourcetype = '', $targetid = '', $targettype = '') {
  1521. $deletesource = false;
  1522. $deletetarget = false;
  1523. if (!empty($sourceid) && !empty($sourcetype) && empty($targetid) && empty($targettype))
  1524. $deletesource = true;
  1525. else if (empty($sourceid) && empty($sourcetype) && !empty($targetid) && !empty($targettype))
  1526. $deletetarget = true;
  1527. $sourceid = (!empty($sourceid) ? $sourceid : $this->id);
  1528. $sourcetype = (!empty($sourcetype) ? $sourcetype : $this->element);
  1529. $targetid = (!empty($targetid) ? $targetid : $this->id);
  1530. $targettype = (!empty($targettype) ? $targettype : $this->element);
  1531. $sql = "DELETE FROM " . MAIN_DB_PREFIX . "element_element";
  1532. $sql.= " WHERE";
  1533. if ($deletesource) {
  1534. $sql.= " fk_source = " . $sourceid . " AND sourcetype = '" . $sourcetype . "'";
  1535. $sql.= " AND fk_target = " . $this->id . " AND targettype = '" . $this->element . "'";
  1536. } else if ($deletetarget) {
  1537. $sql.= " fk_target = " . $targetid . " AND targettype = '" . $targettype . "'";
  1538. $sql.= " AND fk_source = " . $this->id . " AND sourcetype = '" . $this->element . "'";
  1539. } else {
  1540. $sql.= " (fk_source = " . $this->id . " AND sourcetype = '" . $this->element . "')";
  1541. $sql.= " OR";
  1542. $sql.= " (fk_target = " . $this->id . " AND targettype = '" . $this->element . "')";
  1543. }
  1544. dol_syslog(get_class($this) . "::deleteObjectLinked sql=" . $sql, LOG_DEBUG);
  1545. if ($this->db->query($sql)) {
  1546. return 1;
  1547. } else {
  1548. $this->error = $this->db->lasterror();
  1549. dol_syslog(get_class($this) . "::deleteObjectLinked error=" . $this->error, LOG_ERR);
  1550. return -1;
  1551. }
  1552. }
  1553. /**
  1554. * Set status of an object
  1555. *
  1556. * @param int $status Status to set
  1557. * @param int $elementId Id of element to force (use this->id by default)
  1558. * @param string $elementType Type of element to force (use ->this->element by default)
  1559. * @return int <0 if KO, >0 if OK
  1560. */
  1561. function setStatut($status, $elementId = '', $elementType = '') {
  1562. $elementId = (!empty($elementId) ? $elementId : $this->id);
  1563. $elementTable = (!empty($elementType) ? $elementType : $this->table_element);
  1564. $this->db->begin();
  1565. $fieldstatus = "fk_statut";
  1566. if ($elementTable == 'user')
  1567. $fieldstatus = "statut";
  1568. $sql = "UPDATE " . MAIN_DB_PREFIX . $elementTable;
  1569. $sql.= " SET " . $fieldstatus . " = " . $status;
  1570. $sql.= " WHERE rowid=" . $elementId;
  1571. dol_syslog(get_class($this) . "::setStatut sql=" . $sql, LOG_DEBUG);
  1572. if ($this->db->query($sql)) {
  1573. $this->db->commit();
  1574. $this->statut = $status;
  1575. return 1;
  1576. } else {
  1577. $this->error = $this->db->lasterror();
  1578. dol_syslog(get_class($this) . "::setStatut " . $this->error, LOG_ERR);
  1579. $this->db->rollback();
  1580. return -1;
  1581. }
  1582. }
  1583. /**
  1584. * Load type of canvas of an object if it exists
  1585. *
  1586. * @param int $id Record id
  1587. * @param string $ref Record ref
  1588. * @return int <0 if KO, 0 if nothing done, >0 if OK
  1589. */
  1590. function getCanvas($id = 0, $ref = '') {
  1591. return false;
  1592. /*
  1593. global $conf;
  1594. if (empty($id) && empty($ref))
  1595. return 0;
  1596. if (!empty($conf->global->MAIN_DISABLE_CANVAS))
  1597. return 0; // To increase speed. Not enabled by default.
  1598. // Clean parameters
  1599. $ref = trim($ref);
  1600. $sql = "SELECT rowid, canvas";
  1601. $sql.= " FROM " . MAIN_DB_PREFIX . $this->table_element;
  1602. $sql.= " WHERE entity IN (" . getEntity($this->element, 1) . ")";
  1603. if (!empty($id))
  1604. $sql.= " AND rowid = " . $id;
  1605. if (!empty($ref))
  1606. $sql.= " AND ref = '" . $ref . "'";
  1607. $resql = $this->db->query($sql);
  1608. if ($resql) {
  1609. $obj = $this->db->fetch_object($resql);
  1610. if ($obj) {
  1611. $this->id = $obj->rowid;
  1612. $this->canvas = $obj->canvas;
  1613. return 1;
  1614. }
  1615. else
  1616. return 0;
  1617. }
  1618. else {
  1619. dol_print_error($this->db);
  1620. return -1;
  1621. }*/
  1622. }
  1623. /**
  1624. * Get special code of line
  1625. *
  1626. * @param int $lineid Id of line
  1627. * @return int Special code
  1628. */
  1629. function getSpecialCode($lineid) {
  1630. $sql = 'SELECT special_code FROM ' . MAIN_DB_PREFIX . $this->table_element_line;
  1631. $sql.= ' WHERE rowid = ' . $lineid;
  1632. $resql = $this->db->query($sql);
  1633. if ($resql) {
  1634. $row = $this->db->fetch_row($resql);
  1635. return $row[0];
  1636. }
  1637. }
  1638. /**
  1639. * Add/Update all extra fields values for the current object.
  1640. * All data to describe values to insert are stored into $this->array_options=array('keyextrafield'=>'valueextrafieldtoadd')
  1641. *
  1642. * @return void
  1643. */
  1644. function insertExtraFields() {
  1645. global $langs;
  1646. $error = 0;
  1647. if (!empty($this->array_options)) {
  1648. // Check parameters
  1649. $langs->load('admin');
  1650. require_once DOL_DOCUMENT_ROOT . '/core/class/extrafields.class.php';
  1651. $extrafields = new ExtraFields($this->db);
  1652. $optionsArray = $extrafields->fetch_name_optionals_label($this->elementType);
  1653. foreach ($this->array_options as $key => $value) {
  1654. $attributeKey = substr($key, 8); // Remove 'options_' prefix
  1655. $attributeType = $extrafields->attribute_type[$attributeKey];
  1656. $attributeSize = $extrafields->attribute_size[$attributeKey];
  1657. $attributeLabel = $extrafields->attribute_label[$attributeKey];
  1658. switch ($attributeType) {
  1659. case 'int':
  1660. if (!is_numeric($value) && $value != '') {
  1661. $error++;
  1662. $this->errors[] = $langs->trans("ExtraFieldHasWrongValue", $attributeLabel);
  1663. return -1;
  1664. } elseif ($value == '') {
  1665. $this->array_options[$key] = null;
  1666. }
  1667. break;
  1668. }
  1669. }
  1670. $this->db->begin();
  1671. $sql_del = "DELETE FROM " . MAIN_DB_PREFIX . $this->table_element . "_extrafields WHERE fk_object = " . $this->id;
  1672. dol_syslog(get_class($this) . "::insertExtraFields delete sql=" . $sql_del);
  1673. $this->db->query($sql_del);
  1674. $sql = "INSERT INTO " . MAIN_DB_PREFIX . $this->table_element . "_extrafields (fk_object";
  1675. foreach ($this->array_options as $key => $value) {
  1676. // Add field of attribut
  1677. $sql.="," . substr($key, 8); // Remove 'options_' prefix
  1678. }
  1679. $sql .= ") VALUES (" . $this->id;
  1680. foreach ($this->array_options as $key => $value) {
  1681. // Add field o fattribut
  1682. if ($this->array_options[$key] != '') {
  1683. $sql.=",'" . $this->array_options[$key] . "'";
  1684. } else {
  1685. $sql.=",null";
  1686. }
  1687. }
  1688. $sql.=")";
  1689. dol_syslog(get_class($this) . "::insertExtraFields insert sql=" . $sql);
  1690. $resql = $this->db->query($sql);
  1691. if (!$resql) {
  1692. $this->error = $this->db->lasterror();
  1693. dol_syslog(get_class($this) . "::update " . $this->error, LOG_ERR);
  1694. $this->db->rollback();
  1695. return -1;
  1696. } else {
  1697. $this->db->commit();
  1698. return 1;
  1699. }
  1700. }
  1701. else
  1702. return 0;
  1703. }
  1704. /**
  1705. * Function to check if an object is used by others
  1706. *
  1707. * @param int $id Id of object
  1708. * @return int <0 if KO, 0 if not used, >0 if already used
  1709. */
  1710. function isObjectUsed($id) {
  1711. return false;
  1712. /*
  1713. // Check parameters
  1714. if (!isset($this->childtables) || !is_array($this->childtables) || count($this->childtables) == 0) {
  1715. dol_print_error('Called isObjectUsed on a class with property this->childtables not defined');
  1716. return -1;
  1717. }
  1718. // Test if child exists
  1719. $haschild = 0;
  1720. foreach ($this->childtables as $table) {
  1721. // Check if third party can be deleted
  1722. $nb = 0;
  1723. $sql = "SELECT COUNT(*) as nb from " . MAIN_DB_PREFIX . $table;
  1724. $sql.= " WHERE " . $this->fk_element . " = " . $id;
  1725. $resql = $this->db->query($sql);
  1726. if ($resql) {
  1727. $obj = $this->db->fetch_object($resql);
  1728. $haschild+=$obj->nb;
  1729. //print 'Found into table '.$table;
  1730. if ($haschild)
  1731. break; // We found at least on, we stop here
  1732. }
  1733. else {
  1734. $this->error = $this->db->lasterror();
  1735. dol_syslog(get_class($this) . "::delete error -1 " . $this->error, LOG_ERR);
  1736. return -1;
  1737. }
  1738. }
  1739. if ($haschild > 0) {
  1740. $this->error = "ErrorRecordHasChildren";
  1741. return $haschild;
  1742. }
  1743. else
  1744. return 0;
  1745. */
  1746. }
  1747. /**
  1748. * Function to say how many lines object contains
  1749. *
  1750. * @param int $predefined -1=All, 0=Count free product/service only, 1=Count predefined product/service only
  1751. * @return int <0 if KO, 0 if no predefined products, nb of lines with predefined products if found
  1752. */
  1753. function hasProductsOrServices($predefined = -1) {
  1754. $nb = 0;
  1755. foreach ($this->lines as $key => $val) {
  1756. $qualified = 0;
  1757. if ($predefined == -1)
  1758. $qualified = 1;
  1759. if ($predefined == 1 && $val->fk_product > 0)
  1760. $qualified = 1;
  1761. if ($predefined == 0 && $val->fk_product <= 0)
  1762. $qualified = 1;
  1763. if ($qualified)
  1764. $nb++;
  1765. }
  1766. dol_syslog(get_class($this) . '::hasProductsOrServices we found ' . $nb . ' qualified lines of products/servcies');
  1767. return $nb;
  1768. }
  1769. /**
  1770. * Function that returns the total amount of discounts applied.
  1771. *
  1772. * @return false|float False is returned if the discount couldn't be retrieved
  1773. */
  1774. function getTotalDiscount() {
  1775. $sql = 'SELECT (SUM(`subprice`) - SUM(`total_ht`)) as `discount` FROM ' . MAIN_DB_PREFIX . $this->table_element . 'det WHERE `' . $this->fk_element . '` = ' . $this->id;
  1776. $query = $this->db->query($sql);
  1777. if ($query) {
  1778. $result = $this->db->fetch_object($query);
  1779. return price2num($result->discount);
  1780. }
  1781. return false;
  1782. }
  1783. /**
  1784. * Set extra parameters
  1785. *
  1786. * @return void
  1787. */
  1788. function setExtraParameters() {
  1789. $this->db->begin();
  1790. $extraparams = (!empty($this->extraparams) ? json_encode($this->extraparams) : null);
  1791. $sql = "UPDATE " . MAIN_DB_PREFIX . $this->table_element;
  1792. $sql.= " SET extraparams = " . (!empty($extraparams) ? "'" . $this->db->escape($extraparams) . "'" : "null");
  1793. $sql.= " WHERE rowid = " . $this->id;
  1794. dol_syslog(get_class($this) . "::setExtraParameters sql=" . $sql, LOG_DEBUG);
  1795. $resql = $this->db->query($sql);
  1796. if (!$resql) {
  1797. $this->error = $this->db->lasterror();
  1798. dol_syslog(get_class($this) . "::setExtraParameters " . $this->error, LOG_ERR);
  1799. $this->db->rollback();
  1800. return -1;
  1801. } else {
  1802. $this->db->commit();
  1803. return 1;
  1804. }
  1805. }
  1806. /**
  1807. * Return if a country is inside the EEC (European Economic Community)
  1808. *
  1809. * @return boolean true = country inside EEC, false = country outside EEC
  1810. */
  1811. function isInEEC() {
  1812. // List of all country codes that are in europe for european vat rules
  1813. // List found on http://ec.europa.eu/taxation_customs/vies/lang.do?fromWhichPage=vieshome
  1814. $country_code_in_EEC = array(
  1815. 'AT', // Austria
  1816. 'BE', // Belgium
  1817. 'BG', // Bulgaria
  1818. 'CY', // Cyprus
  1819. 'CZ', // Czech republic
  1820. 'DE', // Germany
  1821. 'DK', // Danemark
  1822. 'EE', // Estonia
  1823. 'ES', // Spain
  1824. 'FI', // Finland
  1825. 'FR', // France
  1826. 'GB', // Royaume-uni
  1827. 'GR', // Greece
  1828. 'NL', // Holland
  1829. 'HU', // Hungary
  1830. 'IE', // Ireland
  1831. 'IT', // Italy
  1832. 'LT', // Lithuania
  1833. 'LU', // Luxembourg
  1834. 'LV', // Latvia
  1835. 'MC', // Monaco Seems to use same IntraVAT than France (http://www.gouv.mc/devwww/wwwnew.nsf/c3241c4782f528bdc1256d52004f970b/9e370807042516a5c1256f81003f5bb3!OpenDocument)
  1836. 'MT', // Malta
  1837. //'NO', // Norway
  1838. 'PL', // Poland
  1839. 'PT', // Portugal
  1840. 'RO', // Romania
  1841. 'SE', // Sweden
  1842. 'SK', // Slovakia
  1843. 'SI', // Slovenia
  1844. //'CH', // Switzerland - No. Swizerland in not in EEC
  1845. );
  1846. //print "dd".$this->country_code;
  1847. return in_array($this->country_id, $country_code_in_EEC);
  1848. }
  1849. // --------------------
  1850. // TODO: All functions here must be redesigned and moved as they are not business functions but output functions
  1851. // --------------------
  1852. /**
  1853. * List urls of element
  1854. *
  1855. * @param int $objectid Id of record
  1856. * @param string $objecttype Type of object
  1857. * @param int $withpicto Picto to show
  1858. * @param string $option More options
  1859. * @return void
  1860. */
  1861. function getElementUrl($objectid, $objecttype, $withpicto = 0, $option = '') {
  1862. global $conf;
  1863. // Parse element/subelement (ex: project_task)
  1864. $module = $element = $subelement = $objecttype;
  1865. if (preg_match('/^([^_]+)_([^_]+)/i', $objecttype, $regs)) {
  1866. $module = $element = $regs[1];
  1867. $subelement = $regs[2];
  1868. }
  1869. $classpath = $element . '/class';
  1870. // To work with non standard path
  1871. if ($objecttype == 'facture' || $objecttype == 'invoice') {
  1872. $classpath = 'compta/facture/class';
  1873. $module = 'facture';
  1874. $subelement = 'facture';
  1875. }
  1876. if ($objecttype == 'commande' || $objecttype == 'order') {
  1877. $classpath = 'commande/class';
  1878. $module = 'commande';
  1879. $subelement = 'commande';
  1880. }
  1881. if ($objecttype == 'propal') {
  1882. $classpath = 'comm/propal/class';
  1883. }
  1884. if ($objecttype == 'shipping') {
  1885. $classpath = 'expedition/class';
  1886. $subelement = 'expedition';
  1887. $module = 'expedition_bon';
  1888. }
  1889. if ($objecttype == 'delivery') {
  1890. $classpath = 'livraison/class';
  1891. $subelement = 'livraison';
  1892. $module = 'livraison_bon';
  1893. }
  1894. if ($objecttype == 'invoice_supplier') {
  1895. $classpath = 'fourn/class';
  1896. }
  1897. if ($objecttype == 'order_supplier') {
  1898. $classpath = 'fourn/class';
  1899. }
  1900. if ($objecttype == 'contract') {
  1901. $classpath = 'contrat/class';
  1902. $module = 'contrat';
  1903. $subelement = 'contrat';
  1904. }
  1905. if ($objecttype == 'member') {
  1906. $classpath = 'adherents/class';
  1907. $module = 'adherent';
  1908. $subelement = 'adherent';
  1909. }
  1910. if ($objecttype == 'cabinetmed_cons') {
  1911. $classpath = 'cabinetmed/class';
  1912. $module = 'cabinetmed';
  1913. $subelement = 'cabinetmedcons';
  1914. }
  1915. //print "objecttype=".$objecttype." module=".$module." subelement=".$subelement;
  1916. $classfile = strtolower($subelement);
  1917. $classname = ucfirst($subelement);
  1918. if ($objecttype == 'invoice_supplier') {
  1919. $classfile = 'fournisseur.facture';
  1920. $classname = 'FactureFournisseur';
  1921. }
  1922. if ($objecttype == 'order_supplier') {
  1923. $classfile = 'fournisseur.commande';
  1924. $classname = 'CommandeFournisseur';
  1925. }
  1926. if (!empty($conf->$module->enabled)) {
  1927. $res = dol_include_once('/' . $classpath . '/' . $classfile . '.class.php');
  1928. if ($res) {
  1929. $object = new $classname($this->db);
  1930. $ret = $object->fetch($objectid);
  1931. if ($ret > 0)
  1932. return $object->getNomUrl($withpicto, $option);
  1933. }
  1934. }
  1935. }
  1936. /* This is to show linked object block */
  1937. /**
  1938. * Show linked object block
  1939. * TODO Move this into html.class.php
  1940. * But for the moment we don't know if it's possible as we keep a method available on overloaded objects.
  1941. *
  1942. * @return void
  1943. */
  1944. function showLinkedObjectBlock() {
  1945. global $conf, $langs, $hookmanager;
  1946. global $bc;
  1947. $this->fetchObjectLinked();
  1948. // Bypass the default method
  1949. $hookmanager->initHooks(array('commonobject'));
  1950. $parameters = array();
  1951. $reshook = $hookmanager->executeHooks('showLinkedObjectBlock', $parameters, $this, $action); // Note that $action and $object may have been modified by hook
  1952. if (!$reshook) {
  1953. $num = count($this->linkedObjects);
  1954. foreach ($this->linkedObjects as $objecttype => $objects) {
  1955. $tplpath = $element = $subelement = $objecttype;
  1956. if (preg_match('/^([^_]+)_([^_]+)/i', $objecttype, $regs)) {
  1957. $element = $regs[1];
  1958. $subelement = $regs[2];
  1959. $tplpath = $element . '/' . $subelement;
  1960. }
  1961. // To work with non standard path
  1962. if ($objecttype == 'facture') {
  1963. $tplpath = 'compta/' . $element;
  1964. if (empty($conf->facture->enabled))
  1965. continue; // Do not show if module disabled
  1966. }
  1967. else if ($objecttype == 'propal') {
  1968. $tplpath = 'comm/' . $element;
  1969. if (empty($conf->propal->enabled))
  1970. continue; // Do not show if module disabled
  1971. }
  1972. else if ($objecttype == 'shipping') {
  1973. $tplpath = 'expedition';
  1974. if (empty($conf->expedition->enabled))
  1975. continue; // Do not show if module disabled
  1976. }
  1977. else if ($objecttype == 'delivery') {
  1978. $tplpath = 'livraison';
  1979. } else if ($objecttype == 'invoice_supplier') {
  1980. $tplpath = 'fourn/facture';
  1981. } else if ($objecttype == 'order_supplier') {
  1982. $tplpath = 'fourn/commande';
  1983. }
  1984. global $linkedObjectBlock;
  1985. $linkedObjectBlock = $objects;
  1986. // Output template part (modules that overwrite templates must declare this into descriptor)
  1987. $dirtpls = array_merge($conf->modules_parts['tpl'], array('/' . $tplpath . '/tpl'));
  1988. foreach ($dirtpls as $reldir) {
  1989. $res = @include dol_buildpath($reldir . '/linkedobjectblock.tpl.php');
  1990. if ($res)
  1991. break;
  1992. }
  1993. }
  1994. return $num;
  1995. }
  1996. }
  1997. /* This is to show add lines */
  1998. /**
  1999. * Show add predefined products/services form
  2000. * TODO Edit templates to use global variables and include them directly in controller call
  2001. * But for the moment we don't know if it's possible as we keep a method available on overloaded objects.
  2002. *
  2003. * @param int $dateSelector 1=Show also date range input fields
  2004. * @param Societe $seller Object thirdparty who sell
  2005. * @param Societe $buyer Object thirdparty who buy
  2006. * @return void
  2007. * @deprecated
  2008. */
  2009. function formAddPredefinedProduct($dateSelector, $seller, $buyer) {
  2010. global $conf, $langs, $object, $hookmanager;
  2011. global $form, $bcnd, $var;
  2012. // Use global variables + $dateSelector + $seller and $buyer
  2013. include(DOL_DOCUMENT_ROOT . '/core/tpl/predefinedproductline_create.tpl.php');
  2014. }
  2015. /**
  2016. * Show add free products/services form
  2017. * TODO Edit templates to use global variables and include them directly in controller call
  2018. * But for the moment we don't know if it'st possible as we keep a method available on overloaded objects.
  2019. *
  2020. * @param int $dateSelector 1=Show also date range input fields
  2021. * @param Societe $seller Object thirdparty who sell
  2022. * @param Societe $buyer Object thirdparty who buy
  2023. * @return void
  2024. * @deprecated
  2025. */
  2026. function formAddFreeProduct($dateSelector, $seller, $buyer) {
  2027. global $conf, $langs, $object, $hookmanager;
  2028. global $form, $bcnd, $var;
  2029. // Use global variables + $dateSelector + $seller and $buyer
  2030. include(DOL_DOCUMENT_ROOT . '/core/tpl/freeproductline_create.tpl.php');
  2031. }
  2032. /**
  2033. * Show add free and predefined products/services form
  2034. * TODO Edit templates to use global variables and include them directly in controller call
  2035. * But for the moment we don't know if it's possible as we keep a method available on overloaded objects.
  2036. *
  2037. * @param int $dateSelector 1=Show also date range input fields
  2038. * @param Societe $seller Object thirdparty who sell
  2039. * @param Societe $buyer Object thirdparty who buy
  2040. * @return void
  2041. */
  2042. function formAddObjectLine($dateSelector, $seller, $buyer) {
  2043. global $conf, $user, $langs, $object, $hookmanager;
  2044. global $form, $bcnd, $var;
  2045. // Output template part (modules that overwrite templates must declare this into descriptor)
  2046. // Use global variables + $dateSelector + $seller and $buyer
  2047. $dirtpls = array_merge($conf->modules_parts['tpl'], array('/core/tpl'));
  2048. foreach ($dirtpls as $reldir) {
  2049. $tpl = dol_buildpath($reldir . '/objectline_add.tpl.php');
  2050. if (empty($conf->file->strict_mode)) {
  2051. $res = @include $tpl;
  2052. } else {
  2053. $res = include $tpl; // for debug
  2054. }
  2055. if ($res)
  2056. break;
  2057. }
  2058. }
  2059. /* This is to show array of line of details */
  2060. /**
  2061. * Return HTML table for object lines
  2062. * TODO Move this into an output class file (htmlline.class.php)
  2063. * If lines are into a template, title must also be into a template
  2064. * But for the moment we don't know if it'st possible as we keep a method available on overloaded objects.
  2065. *
  2066. * @param string $action Action code
  2067. * @param string $seller Object of seller third party
  2068. * @param string $buyer Object of buyer third party
  2069. * @param string $selected Object line selected
  2070. * @param int $dateSelector 1=Show also date range input fields
  2071. * @return void
  2072. */
  2073. function printObjectLines($action, $seller, $buyer, $selected = 0, $dateSelector = 0) {
  2074. global $conf, $langs, $hookmanager;
  2075. print '<tr class="liste_titre nodrag nodrop">';
  2076. if (!empty($conf->global->MAIN_VIEW_LINE_NUMBER)) {
  2077. print '<td align="center" width="5">&nbsp;</td>';
  2078. }
  2079. print '<td>' . $langs->trans('Description') . '</td>';
  2080. print '<td align="right" width="50">' . $langs->trans('VAT') . '</td>';
  2081. print '<td align="right" width="80">' . $langs->trans('PriceUHT') . '</td>';
  2082. if ($conf->global->MAIN_FEATURES_LEVEL > 1)
  2083. print '<td align="right" width="80">&nbsp;</td>';
  2084. print '<td align="right" width="50">' . $langs->trans('Qty') . '</td>';
  2085. print '<td align="right" width="50">' . $langs->trans('ReductionShort') . '</td>';
  2086. if (!empty($conf->margin->enabled)) {
  2087. if ($conf->global->MARGIN_TYPE == "1")
  2088. print '<td align="right" width="80">' . $langs->trans('BuyingPrice') . '</td>';
  2089. else
  2090. print '<td align="right" width="80">' . $langs->trans('BuyingCost') . '</td>';
  2091. if (!empty($conf->global->DISPLAY_MARGIN_RATES))
  2092. print '<td align="right" width="50">' . $langs->trans('MarginRate') . '</td>';
  2093. if (!empty($conf->global->DISPLAY_MARK_RATES))
  2094. print '<td align="right" width="50">' . $langs->trans('MarkRate') . '</td>';
  2095. }
  2096. print '<td align="right" width="50">' . $langs->trans('TotalHTShort') . '</td>';
  2097. print '<td width="10">&nbsp;</td>';
  2098. print '<td width="10">&nbsp;</td>';
  2099. print '<td nowrap="nowrap">&nbsp;</td>'; // No width to allow autodim
  2100. print "</tr>\n";
  2101. $num = count($this->lines);
  2102. $var = true;
  2103. $i = 0;
  2104. foreach ($this->lines as $line) {
  2105. $var = !$var;
  2106. if (($line->product_type == 9 && !empty($line->special_code)) || !empty($line->fk_parent_line)) {
  2107. if (empty($line->fk_parent_line)) {
  2108. $parameters = array('line' => $line, 'var' => $var, 'num' => $num, 'i' => $i, 'dateSelector' => $dateSelector, 'seller' => $seller, 'buyer' => $buyer, 'selected' => $selected);
  2109. $reshook = $hookmanager->executeHooks('printObjectLine', $parameters, $this, $action); // Note that $action and $object may have been modified by some hooks
  2110. }
  2111. } else {
  2112. $this->printObjectLine($action, $line, $var, $num, $i, $dateSelector, $seller, $buyer, $selected);
  2113. }
  2114. $i++;
  2115. }
  2116. }
  2117. /**
  2118. * Return HTML content of a detail line
  2119. * TODO Move this into an output class file (htmlline.class.php)
  2120. *
  2121. * @param string $action GET/POST action
  2122. * @param array $line Selected object line to output
  2123. * @param string $var Is it a an odd line (true)
  2124. * @param int $num Number of line (0)
  2125. * @param int $i I
  2126. * @param int $dateSelector 1=Show also date range input fields
  2127. * @param string $seller Object of seller third party
  2128. * @param string $buyer Object of buyer third party
  2129. * @param string $selected Object line selected
  2130. * @return void
  2131. */
  2132. function printObjectLine($action, $line, $var, $num, $i, $dateSelector, $seller, $buyer, $selected = 0) {
  2133. global $conf, $langs, $user, $hookmanager;
  2134. global $form, $bc, $bcdd;
  2135. $element = $this->element;
  2136. $text = '';
  2137. // Show product and description
  2138. $type = (!empty($line->product_type) ? $line->product_type : $line->fk_product_type);
  2139. // Try to enhance type detection using date_start and date_end for free lines where type was not saved.
  2140. if (!empty($line->date_start))
  2141. $type = 1; // deprecated
  2142. if (!empty($line->date_end))
  2143. $type = 1; // deprecated
  2144. if (!empty($line->fk_product)) {
  2145. $product_static = new Product($this->db);
  2146. $product_static->fetch($line->fk_product);
  2147. // $product_static->type=$line->product_type;
  2148. // $product_static->id=$line->fk_product;
  2149. // $product_static->ref=$line->ref;
  2150. $text = $product_static->getNomUrl(1);
  2151. }
  2152. // Ligne en mode visu
  2153. if ($action != 'editline' || $selected != $line->id) {
  2154. // Produit
  2155. if (!empty($line->fk_product)) {
  2156. // Define output language
  2157. if (!empty($conf->global->MAIN_MULTILANGS) && !empty($conf->global->PRODUIT_TEXTS_IN_THIRDPARTY_LANGUAGE)) {
  2158. $this->fetch_thirdparty();
  2159. $prod = new Product($this->db);
  2160. $outputlangs = $langs;
  2161. $newlang = '';
  2162. if (empty($newlang) && GETPOST('lang_id'))
  2163. $newlang = GETPOST('lang_id');
  2164. if (empty($newlang))
  2165. $newlang = $this->client->default_lang;
  2166. if (!empty($newlang)) {
  2167. $outputlangs = new Translate();
  2168. $outputlangs->setDefaultLang($newlang);
  2169. }
  2170. $label = (!empty($prod->multilangs[$outputlangs->defaultlang]["label"])) ? $prod->multilangs[$outputlangs->defaultlang]["label"] : $line->product_label;
  2171. } else {
  2172. $label = $line->product_label;
  2173. }
  2174. // $text.= ' - '.(! empty($line->label)?$line->label:$label);
  2175. // $description=(! empty($conf->global->PRODUIT_DESC_IN_FORM)?'':dol_htmlentitiesbr($line->description));
  2176. $text .= (!empty($conf->global->PRODUIT_DESC_IN_FORM) ? '' : ' - ' . $line->description);
  2177. }
  2178. // Output template part (modules that overwrite templates must declare this into descriptor)
  2179. // Use global variables + $dateSelector + $seller and $buyer
  2180. $dirtpls = array_merge($conf->modules_parts['tpl'], array('/core/tpl'));
  2181. foreach ($dirtpls as $reldir) {
  2182. $tpl = dol_buildpath($reldir . '/objectline_view.tpl.php');
  2183. if (empty($conf->file->strict_mode)) {
  2184. $res = @include $tpl;
  2185. } else {
  2186. $res = include $tpl; // for debug
  2187. }
  2188. if ($res)
  2189. break;
  2190. }
  2191. }
  2192. // Ligne en mode update
  2193. if ($this->statut == 0 && $action == 'editline' && $selected == $line->id) {
  2194. $label = (!empty($line->label) ? $line->label : (($line->fk_product > 0) ? $line->product_label : ''));
  2195. if (!empty($conf->global->MAIN_HTML5_PLACEHOLDER))
  2196. $placeholder = ' placeholder="' . $langs->trans("Label") . '"';
  2197. else
  2198. $placeholder = ' title="' . $langs->trans("Label") . '"';
  2199. $pu_ttc = price2num($line->subprice * (1 + ($line->tva_tx / 100)), 'MU');
  2200. // Output template part (modules that overwrite templates must declare this into descriptor)
  2201. // Use global variables + $dateSelector + $seller and $buyer
  2202. $dirtpls = array_merge($conf->modules_parts['tpl'], array('/core/tpl'));
  2203. foreach ($dirtpls as $reldir) {
  2204. $tpl = dol_buildpath($reldir . '/objectline_edit.tpl.php');
  2205. if (empty($conf->file->strict_mode)) {
  2206. $res = @include $tpl;
  2207. } else {
  2208. $res = include $tpl; // for debug
  2209. }
  2210. if ($res)
  2211. break;
  2212. }
  2213. }
  2214. }
  2215. /* This is to show array of line of details of source object */
  2216. /**
  2217. * Return HTML table table of source object lines
  2218. * TODO Move this and previous function into output html class file (htmlline.class.php).
  2219. * If lines are into a template, title must also be into a template
  2220. * But for the moment we don't know if it's possible as we keep a method available on overloaded objects.
  2221. *
  2222. * @return void
  2223. */
  2224. function printOriginLinesList() {
  2225. global $langs, $hookmanager;
  2226. print '<tr class="liste_titre">';
  2227. print '<td>' . $langs->trans('Ref') . '</td>';
  2228. print '<td>' . $langs->trans('Description') . '</td>';
  2229. print '<td align="right">' . $langs->trans('VAT') . '</td>';
  2230. print '<td align="right">' . $langs->trans('PriceUHT') . '</td>';
  2231. print '<td align="right">' . $langs->trans('Qty') . '</td>';
  2232. print '<td align="right">' . $langs->trans('ReductionShort') . '</td></tr>';
  2233. $num = count($this->lines);
  2234. $var = true;
  2235. $i = 0;
  2236. foreach ($this->lines as $line) {
  2237. $var = !$var;
  2238. if (($line->product_type == 9 && !empty($line->special_code)) || !empty($line->fk_parent_line)) {
  2239. if (empty($line->fk_parent_line)) {
  2240. $parameters = array('line' => $line, 'var' => $var, 'i' => $i);
  2241. $action = '';
  2242. $reshook = $hookmanager->executeHooks('printOriginObjectLine', $parameters, $this, $action); // Note that $action and $object may have been modified by some hooks
  2243. }
  2244. } else {
  2245. $this->printOriginLine($line, $var);
  2246. }
  2247. $i++;
  2248. }
  2249. }
  2250. /**
  2251. * Return HTML with a line of table array of source object lines
  2252. * TODO Move this and previous function into output html class file (htmlline.class.php).
  2253. * If lines are into a template, title must also be into a template
  2254. * But for the moment we don't know if it's possible as we keep a method available on overloaded objects.
  2255. *
  2256. * @param array $line Line
  2257. * @param string $var Var
  2258. * @return void
  2259. */
  2260. function printOriginLine($line, $var) {
  2261. global $conf, $langs, $bc;
  2262. //var_dump($line);
  2263. $date_start = $line->date_debut_prevue;
  2264. if ($line->date_debut_reel)
  2265. $date_start = $line->date_debut_reel;
  2266. $date_end = $line->date_fin_prevue;
  2267. if ($line->date_fin_reel)
  2268. $date_end = $line->date_fin_reel;
  2269. $this->tpl['label'] = '';
  2270. if (!empty($line->fk_parent_line))
  2271. $this->tpl['label'].= img_picto('', 'rightarrow');
  2272. if (($line->info_bits & 2) == 2) { // TODO Not sure this is used for source object
  2273. $discount = new DiscountAbsolute($this->db);
  2274. $discount->fk_soc = $this->socid;
  2275. $this->tpl['label'].= $discount->getNomUrl(0, 'discount');
  2276. } else if (!empty($line->fk_product)) {
  2277. $productstatic = new Product($this->db);
  2278. $productstatic->id = $line->fk_product;
  2279. $productstatic->ref = $line->ref;
  2280. $productstatic->type = $line->fk_product_type;
  2281. $this->tpl['label'].= $productstatic->getNomUrl(1);
  2282. $this->tpl['label'].= ' - ' . (!empty($line->label) ? $line->label : $line->product_label);
  2283. // Dates
  2284. if ($line->product_type == 1 && ($date_start || $date_end)) {
  2285. $this->tpl['label'].= get_date_range($date_start, $date_end);
  2286. }
  2287. } else {
  2288. $this->tpl['label'].= ($line->product_type == -1 ? '&nbsp;' : ($line->product_type == 1 ? img_object($langs->trans(''), 'service') : img_object($langs->trans(''), 'product')));
  2289. $this->tpl['label'].= ($line->label ? '&nbsp;' . $line->label : '');
  2290. // Dates
  2291. if ($line->product_type == 1 && ($date_start || $date_end)) {
  2292. $this->tpl['label'].= get_date_range($date_start, $date_end);
  2293. }
  2294. }
  2295. if (!empty($line->desc)) {
  2296. if ($line->desc == '(CREDIT_NOTE)') { // TODO Not sure this is used for source object
  2297. $discount = new DiscountAbsolute($this->db);
  2298. $discount->fetch($line->fk_remise_except);
  2299. $this->tpl['description'] = $langs->transnoentities("DiscountFromCreditNote", $discount->getNomUrl(0));
  2300. } elseif ($line->desc == '(DEPOSIT)') { // TODO Not sure this is used for source object
  2301. $discount = new DiscountAbsolute($this->db);
  2302. $discount->fetch($line->fk_remise_except);
  2303. $this->tpl['description'] = $langs->transnoentities("DiscountFromDeposit", $discount->getNomUrl(0));
  2304. } else {
  2305. $this->tpl['description'] = dol_trunc($line->desc, 60);
  2306. }
  2307. } else {
  2308. $this->tpl['description'] = '&nbsp;';
  2309. }
  2310. $this->tpl['vat_rate'] = vatrate($line->tva_tx, true);
  2311. $this->tpl['price'] = price($line->subprice);
  2312. $this->tpl['qty'] = (($line->info_bits & 2) != 2) ? $line->qty : '&nbsp;';
  2313. $this->tpl['remise_percent'] = (($line->info_bits & 2) != 2) ? vatrate($line->remise_percent, true) : '&nbsp;';
  2314. // Output template part (modules that overwrite templates must declare this into descriptor)
  2315. // Use global variables + $dateSelector + $seller and $buyer
  2316. $dirtpls = array_merge($conf->modules_parts['tpl'], array('/core/tpl'));
  2317. foreach ($dirtpls as $reldir) {
  2318. $tpl = dol_buildpath($reldir . '/originproductline.tpl.php');
  2319. if (empty($conf->file->strict_mode)) {
  2320. $res = @include $tpl;
  2321. } else {
  2322. $res = include $tpl; // for debug
  2323. }
  2324. if ($res)
  2325. break;
  2326. }
  2327. }
  2328. function getMarginInfos($force_price = false) {
  2329. global $conf;
  2330. require_once DOL_DOCUMENT_ROOT . '/fourn/class/fournisseur.product.class.php';
  2331. $marginInfos = array(
  2332. 'pa_products' => 0,
  2333. 'pv_products' => 0,
  2334. 'margin_on_products' => 0,
  2335. 'margin_rate_products' => '',
  2336. 'mark_rate_products' => '',
  2337. 'pa_services' => 0,
  2338. 'pv_services' => 0,
  2339. 'margin_on_services' => 0,
  2340. 'margin_rate_services' => '',
  2341. 'mark_rate_services' => '',
  2342. 'pa_total' => 0,
  2343. 'pv_total' => 0,
  2344. 'total_margin' => 0,
  2345. 'total_margin_rate' => '',
  2346. 'total_mark_rate' => ''
  2347. );
  2348. foreach ($this->lines as $line) {
  2349. if (isset($line->fk_fournprice) && !$force_price) {
  2350. $product = new ProductFournisseur($this->db);
  2351. if ($product->fetch_product_fournisseur_price($line->fk_fournprice))
  2352. $line->pa_ht = $product->fourn_unitprice;
  2353. if (isset($conf->global->MARGIN_TYPE) && $conf->global->MARGIN_TYPE == "2" && $product->fourn_unitcharges > 0)
  2354. $line->pa_ht += $product->fourn_unitcharges;
  2355. }
  2356. // si prix d'achat non renseigné et devrait l'être, alors prix achat = prix vente
  2357. if ((!isset($line->pa_ht) || $line->pa_ht == 0) && $line->subprice > 0 && (isset($conf->global->ForceBuyingPriceIfNull) && $conf->global->ForceBuyingPriceIfNull == 1)) {
  2358. $line->pa_ht = $line->subprice * (1 - ($line->remise_percent / 100));
  2359. }
  2360. // calcul des marges
  2361. if (isset($line->fk_remise_except) && isset($conf->global->MARGIN_METHODE_FOR_DISCOUNT)) { // remise
  2362. if ($conf->global->MARGIN_METHODE_FOR_DISCOUNT == '1') { // remise globale considérée comme produit
  2363. $marginInfos['pa_products'] += $line->pa_ht; // ($line->pa_ht != 0)?$line->pa_ht:$line->subprice * (1 - $line->remise_percent / 100);
  2364. $marginInfos['pv_products'] += $line->subprice * (1 - $line->remise_percent / 100);
  2365. $marginInfos['pa_total'] += $line->pa_ht; // ($line->pa_ht != 0)?$line->pa_ht:$line->subprice * (1 - $line->remise_percent / 100);
  2366. $marginInfos['pv_total'] += $line->subprice * (1 - $line->remise_percent / 100);
  2367. } elseif ($conf->global->MARGIN_METHODE_FOR_DISCOUNT == '2') { // remise globale considérée comme service
  2368. $marginInfos['pa_services'] += $line->pa_ht; // ($line->pa_ht != 0)?$line->pa_ht:$line->subprice * (1 - $line->remise_percent / 100);
  2369. $marginInfos['pv_services'] += $line->subprice * (1 - ($line->remise_percent / 100));
  2370. $marginInfos['pa_total'] += $line->pa_ht; // ($line->pa_ht != 0)?$line->pa_ht:$line->subprice * (1 - $line->remise_percent / 100);
  2371. $marginInfos['pv_total'] += $line->subprice * (1 - $line->remise_percent / 100);
  2372. } elseif ($conf->global->MARGIN_METHODE_FOR_DISCOUNT == '3') { // remise globale prise en compte uniqt sur total
  2373. $marginInfos['pa_total'] += $line->pa_ht; // ($line->pa_ht != 0)?$line->pa_ht:$line->subprice * (1 - $line->remise_percent / 100);
  2374. $marginInfos['pv_total'] += $line->subprice * (1 - ($line->remise_percent / 100));
  2375. }
  2376. } else {
  2377. $type = $line->product_type ? $line->product_type : $line->fk_product_type;
  2378. if ($type == 0) { // product
  2379. $marginInfos['pa_products'] += $line->qty * $line->pa_ht;
  2380. $marginInfos['pv_products'] += $line->qty * $line->subprice * (1 - $line->remise_percent / 100);
  2381. $marginInfos['pa_total'] += $line->qty * $line->pa_ht;
  2382. $marginInfos['pv_total'] += $line->qty * $line->subprice * (1 - $line->remise_percent / 100);
  2383. } elseif ($type == 1) { // service
  2384. $marginInfos['pa_services'] += $line->qty * $line->pa_ht;
  2385. $marginInfos['pv_services'] += $line->qty * $line->subprice * (1 - ($line->remise_percent / 100));
  2386. $marginInfos['pa_total'] += $line->qty * $line->pa_ht;
  2387. $marginInfos['pv_total'] += $line->qty * $line->subprice * (1 - $line->remise_percent / 100);
  2388. }
  2389. }
  2390. }
  2391. $marginInfos['margin_on_products'] = $marginInfos['pv_products'] - $marginInfos['pa_products'];
  2392. if ($marginInfos['pa_products'] > 0)
  2393. $marginInfos['margin_rate_products'] = 100 * round($marginInfos['margin_on_products'] / $marginInfos['pa_products'], 5);
  2394. if ($marginInfos['pv_products'] > 0)
  2395. $marginInfos['mark_rate_products'] = 100 * round($marginInfos['margin_on_products'] / $marginInfos['pv_products'], 5);
  2396. $marginInfos['margin_on_services'] = $marginInfos['pv_services'] - $marginInfos['pa_services'];
  2397. if ($marginInfos['pa_services'] > 0)
  2398. $marginInfos['margin_rate_services'] = 100 * round($marginInfos['margin_on_services'] / $marginInfos['pa_services'], 5);
  2399. if ($marginInfos['pv_services'] > 0)
  2400. $marginInfos['mark_rate_services'] = 100 * round($marginInfos['margin_on_services'] / $marginInfos['pv_services'], 5);
  2401. $marginInfos['total_margin'] = $marginInfos['pv_total'] - $marginInfos['pa_total'];
  2402. if ($marginInfos['pa_total'] > 0)
  2403. $marginInfos['total_margin_rate'] = 100 * round($marginInfos['total_margin'] / $marginInfos['pa_total'], 5);
  2404. if ($marginInfos['pv_total'] > 0)
  2405. $marginInfos['total_mark_rate'] = 100 * round($marginInfos['total_margin'] / $marginInfos['pv_total'], 5);
  2406. return $marginInfos;
  2407. }
  2408. function displayMarginInfos($force_price = false) {
  2409. global $langs, $conf;
  2410. $marginInfo = $this->getMarginInfos($force_price);
  2411. print '<table class="noborder" width="100%">';
  2412. print '<tr class="liste_titre">';
  2413. print '<td width="30%">' . $langs->trans('Margins') . '</td>';
  2414. print '<td width="20%" align="right">' . $langs->trans('SellingPrice') . '</td>';
  2415. print '<td width="20%" align="right">' . $langs->trans('BuyingPrice') . '</td>';
  2416. print '<td width="20%" align="right">' . $langs->trans('Margin') . '</td>';
  2417. if (!empty($conf->global->DISPLAY_MARGIN_RATES))
  2418. print '<td align="right">' . $langs->trans('MarginRate') . '</td>';
  2419. if (!empty($conf->global->DISPLAY_MARK_RATES))
  2420. print '<td align="right">' . $langs->trans('MarkRate') . '</td>';
  2421. print '</tr>';
  2422. //if ($marginInfo['margin_on_products'] != 0 && $marginInfo['margin_on_services'] != 0) {
  2423. print '<tr class="impair">';
  2424. print '<td>' . $langs->trans('MarginOnProducts') . '</td>';
  2425. print '<td align="right">' . price($marginInfo['pv_products']) . '</td>';
  2426. print '<td align="right">' . price($marginInfo['pa_products']) . '</td>';
  2427. print '<td align="right">' . price($marginInfo['margin_on_products']) . '</td>';
  2428. if (!empty($conf->global->DISPLAY_MARGIN_RATES))
  2429. print '<td align="right">' . (($marginInfo['margin_rate_products'] == '') ? 'n/a' : price($marginInfo['margin_rate_products']) . '%') . '</td>';
  2430. if (!empty($conf->global->DISPLAY_MARK_RATES))
  2431. print '<td align="right">' . (($marginInfo['mark_rate_products'] == '') ? 'n/a' : price($marginInfo['mark_rate_products']) . '%') . '</td>';
  2432. print '</tr>';
  2433. print '<tr class="pair">';
  2434. print '<td>' . $langs->trans('MarginOnServices') . '</td>';
  2435. print '<td align="right">' . price($marginInfo['pv_services']) . '</td>';
  2436. print '<td align="right">' . price($marginInfo['pa_services']) . '</td>';
  2437. print '<td align="right">' . price($marginInfo['margin_on_services']) . '</td>';
  2438. if (!empty($conf->global->DISPLAY_MARGIN_RATES))
  2439. print '<td align="right">' . (($marginInfo['margin_rate_services'] == '') ? 'n/a' : price($marginInfo['margin_rate_services']) . '%') . '</td>';
  2440. if (!empty($conf->global->DISPLAY_MARK_RATES))
  2441. print '<td align="right">' . (($marginInfo['mark_rate_services'] == '') ? 'n/a' : price($marginInfo['mark_rate_services']) . '%') . '</td>';
  2442. print '</tr>';
  2443. //}
  2444. print '<tr class="impair">';
  2445. print '<td>' . $langs->trans('TotalMargin') . '</td>';
  2446. print '<td align="right">' . price($marginInfo['pv_total']) . '</td>';
  2447. print '<td align="right">' . price($marginInfo['pa_total']) . '</td>';
  2448. print '<td align="right">' . price($marginInfo['total_margin']) . '</td>';
  2449. if (!empty($conf->global->DISPLAY_MARGIN_RATES))
  2450. print '<td align="right">' . (($marginInfo['total_margin_rate'] == '') ? 'n/a' : price($marginInfo['total_margin_rate']) . '%') . '</td>';
  2451. if (!empty($conf->global->DISPLAY_MARK_RATES))
  2452. print '<td align="right">' . (($marginInfo['total_mark_rate'] == '') ? 'n/a' : price($marginInfo['total_mark_rate']) . '%') . '</td>';
  2453. print '</tr>';
  2454. print '</table>';
  2455. }
  2456. }
  2457. ?>