PageRenderTime 58ms CodeModel.GetById 15ms RepoModel.GetById 0ms app.codeStats 1ms

/htdocs/facture/class/facture.class.php

https://bitbucket.org/speedealing/speedealing
PHP | 3658 lines | 2637 code | 423 blank | 598 comment | 484 complexity | 15c3b06c75557c8ec7a3a29545f890f6 MD5 | raw file
Possible License(s): LGPL-3.0, LGPL-2.1, GPL-3.0, MIT

Large files files are truncated, but you can click here to view the full file

  1. <?php
  2. /* Copyright (C) 2002-2007 Rodolphe Quiedeville <rodolphe@quiedeville.org>
  3. * Copyright (C) 2004-2012 Laurent Destailleur <eldy@users.sourceforge.net>
  4. * Copyright (C) 2004 Sebastien Di Cintio <sdicintio@ressource-toi.org>
  5. * Copyright (C) 2004 Benoit Mortier <benoit.mortier@opensides.be>
  6. * Copyright (C) 2005 Marc Barilley / Ocebo <marc@ocebo.com>
  7. * Copyright (C) 2005-2012 Regis Houssin <regis.houssin@capnetworks.com>
  8. * Copyright (C) 2006 Andre Cianfarani <acianfa@free.fr>
  9. * Copyright (C) 2007 Franky Van Liedekerke <franky.van.liedekerke@telenet.be>
  10. * Copyright (C) 2010-2012 Juanjo Menent <jmenent@2byte.es>
  11. * Copyright (C) 2012 Christophe Battarel <christophe.battarel@altairis.fr>
  12. * Copyright (C) 2012 Marcos García <marcosgdf@gmail.com>
  13. * Copyright (C) 2012 David Moothen <dmoothen@websitti.fr>
  14. *
  15. * This program is free software; you can redistribute it and/or modify
  16. * it under the terms of the GNU General Public License as published by
  17. * the Free Software Foundation; either version 3 of the License, or
  18. * (at your option) any later version.
  19. *
  20. * This program is distributed in the hope that it will be useful,
  21. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  22. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  23. * GNU General Public License for more details.
  24. *
  25. * You should have received a copy of the GNU General Public License
  26. * along with this program. If not, see <http://www.gnu.org/licenses/>.
  27. */
  28. /**
  29. * \file htdocs/compta/facture/class/facture.class.php
  30. * \ingroup facture
  31. * \brief File of class to manage invoices
  32. */
  33. include_once DOL_DOCUMENT_ROOT . '/core/class/abstractinvoice.class.php';
  34. require_once DOL_DOCUMENT_ROOT . '/product/class/product.class.php';
  35. require_once DOL_DOCUMENT_ROOT . '/societe/class/client.class.php';
  36. require_once DOL_DOCUMENT_ROOT . '/margin/lib/margins.lib.php';
  37. /**
  38. * Class to manage invoices
  39. */
  40. class Facture extends AbstractInvoice {
  41. public $element = 'facture';
  42. public $table_element = 'facture';
  43. // public $table_element_line = 'facturedet';
  44. // public $fk_element = 'fk_facture';
  45. protected $ismultientitymanaged = 1; // 0=No test on entity, 1=Test with field entity, 2=Test with link by societe
  46. var $id;
  47. //! Id client
  48. var $socid;
  49. //! Objet societe client (to load with fetch_client method)
  50. var $client;
  51. var $author;
  52. var $fk_user_author;
  53. var $fk_user_valid;
  54. //! Invoice date
  55. var $date; // Invoice date
  56. var $date_creation; // Creation date
  57. var $date_validation; // Validation date
  58. var $datem;
  59. var $ref;
  60. var $ref_client;
  61. var $ref_ext;
  62. var $ref_int;
  63. public $type = "INVOICE_STANDARD";
  64. //var $amount;
  65. var $remise_absolue;
  66. var $remise_percent;
  67. var $total_ht = 0;
  68. var $total_tva = 0;
  69. var $total_ttc = 0;
  70. var $note; // deprecated
  71. var $note_private;
  72. var $note_public;
  73. //! 0=draft,
  74. //! 1=validated (need to be paid),
  75. //! 2=classified paid partially (close_code='discount_vat','badcustomer') or completely (close_code=null),
  76. //! 3=classified abandoned and no payment done (close_code='badcustomer','abandon' or 'replaced')
  77. public $Status = "DRAFT";
  78. //! Fermeture apres paiement partiel: discount_vat, badcustomer, abandon
  79. //! Fermeture alors que aucun paiement: replaced (si remplace), abandon
  80. var $close_code;
  81. //! Commentaire si mis a paye sans paiement complet
  82. var $close_note;
  83. //! 1 if invoice paid COMPLETELY, 0 otherwise (do not use it anymore, use statut and close_code
  84. var $paye;
  85. //! id of source invoice if replacement invoice or credit note
  86. var $fk_facture_source;
  87. var $origin;
  88. var $origin_id;
  89. var $linked_objects = array();
  90. var $fk_project;
  91. var $date_lim_reglement;
  92. var $cond_reglement_code;
  93. var $mode_reglement_code;
  94. var $modelpdf;
  95. var $products = array(); // deprecated
  96. var $lines = array();
  97. var $line;
  98. var $extraparams = array();
  99. //! Pour board
  100. var $nbtodo;
  101. var $nbtodolate;
  102. var $specimen;
  103. var $fac_rec;
  104. /**
  105. * Constructor
  106. *
  107. * @param DoliDB $db Database handler
  108. */
  109. function __construct($db = '') {
  110. parent::__construct($db);
  111. $this->no_save[] = 'thirdparty';
  112. $this->no_save[] = 'line';
  113. $this->fk_extrafields = new ExtraFields($db);
  114. $this->fk_extrafields->fetch(get_class($this));
  115. $this->remise = 0;
  116. $this->remise_percent = 0;
  117. $this->products = array();
  118. }
  119. /**
  120. * Create invoice in database
  121. * Note: this->ref can be set or empty. If empty, we will use "(PROV)"
  122. *
  123. * @param User $user Object user that create
  124. * @param int $notrigger 1=Does not execute triggers, 0 otherwise
  125. * @param int $forceduedate 1=Do not recalculate due date from payment condition but force it with value
  126. * @return int <0 if KO, >0 if OK
  127. */
  128. function create($user, $notrigger = 0, $forceduedate = 0) {
  129. global $langs, $conf, $mysoc, $user;
  130. $error = 0;
  131. // Clean parameters
  132. if (empty($this->type))
  133. $this->type = "INVOICE_STANDARD";
  134. $this->ref_client = trim($this->ref_client);
  135. $this->note = (isset($this->note) ? trim($this->note) : trim($this->note_private)); // deprecated
  136. $this->note_private = (isset($this->note_private) ? trim($this->note_private) : trim($this->note));
  137. $this->note_public = trim($this->note_public);
  138. $this->Status = "DRAFT";
  139. dol_syslog(get_class($this) . "::create user=" . $user->id);
  140. // Check parameters
  141. if (empty($this->date) || empty($user->id)) {
  142. $this->error = "ErrorBadParameter";
  143. dol_syslog(get_class($this) . "::create Try to create an invoice with an empty parameter (user, date, ...)", LOG_ERR);
  144. return -3;
  145. }
  146. $soc = new Societe($this->db);
  147. $result = $soc->fetch($this->socid);
  148. unset($this->socid);
  149. if ($result < 0) {
  150. $this->error = "Failed to fetch company";
  151. dol_syslog(get_class($this) . "::create " . $this->error, LOG_ERR);
  152. return -2;
  153. }
  154. $this->client = new stdClass();
  155. $this->client->id = $soc->id;
  156. $this->client->name = $soc->name;
  157. // author
  158. $this->author = new stdClass();
  159. $this->author->id = $user->id;
  160. $this->author->name = $user->login;
  161. $this->ref = $this->getNextNumRef($soc);
  162. $now = dol_now();
  163. $this->record();
  164. // Appel des triggers
  165. include_once DOL_DOCUMENT_ROOT . '/core/class/interfaces.class.php';
  166. $interface = new Interfaces($this->db);
  167. $result = $interface->run_triggers('BILL_CREATE', $this, $user, $langs, $conf);
  168. if ($result < 0) {
  169. $error++;
  170. $this->errors = $interface->errors;
  171. }
  172. // Fin appel triggers
  173. return $this->id;
  174. // Create invoice from a predefined invoice
  175. // if ($this->fac_rec > 0) {
  176. // require_once DOL_DOCUMENT_ROOT . '/compta/facture/class/facture-rec.class.php';
  177. // $_facrec = new FactureRec($this->db);
  178. // $result = $_facrec->fetch($this->fac_rec);
  179. //
  180. // $this->fk_project = $_facrec->fk_project;
  181. // $this->cond_reglement = $_facrec->cond_reglement_id;
  182. // $this->cond_reglement_id = $_facrec->cond_reglement_id;
  183. // $this->mode_reglement = $_facrec->mode_reglement_id;
  184. // $this->mode_reglement_id = $_facrec->mode_reglement_id;
  185. // $this->remise_absolue = $_facrec->remise_absolue;
  186. // $this->remise_percent = $_facrec->remise_percent;
  187. //
  188. // // Clean parametres
  189. // if (!$this->type)
  190. // $this->type = 0;
  191. // $this->ref_client = trim($this->ref_client);
  192. // $this->note = trim($this->note);
  193. // $this->note_public = trim($this->note_public);
  194. // //if (! $this->remise) $this->remise = 0;
  195. // if (!$this->mode_reglement_id)
  196. // $this->mode_reglement_id = 0;
  197. // $this->brouillon = 1;
  198. // }
  199. // Define due date if not already defined
  200. $datelim = (empty($forceduedate) ? $this->calculate_date_lim_reglement() : $forceduedate);
  201. // Insert into database
  202. $socid = $this->socid;
  203. $sql = "INSERT INTO " . MAIN_DB_PREFIX . "facture (";
  204. $sql.= " facnumber";
  205. $sql.= ", entity";
  206. $sql.= ", ref_ext";
  207. $sql.= ", type";
  208. $sql.= ", fk_soc";
  209. $sql.= ", datec";
  210. $sql.= ", remise_absolue";
  211. $sql.= ", remise_percent";
  212. $sql.= ", datef";
  213. $sql.= ", note";
  214. $sql.= ", note_public";
  215. $sql.= ", ref_client, ref_int";
  216. $sql.= ", fk_facture_source, fk_user_author, fk_projet";
  217. $sql.= ", fk_cond_reglement, fk_mode_reglement, date_lim_reglement, model_pdf";
  218. $sql.= ")";
  219. $sql.= " VALUES (";
  220. $sql.= "'(PROV)'";
  221. $sql.= ", " . $conf->entity;
  222. $sql.= ", " . ($this->ref_ext ? "'" . $this->db->escape($this->ref_ext) . "'" : "null");
  223. $sql.= ", '" . $this->type . "'";
  224. $sql.= ", '" . $socid . "'";
  225. $sql.= ", '" . $this->db->idate($now) . "'";
  226. $sql.= "," . ($this->remise_absolue > 0 ? $this->remise_absolue : 'NULL');
  227. $sql.= "," . ($this->remise_percent > 0 ? $this->remise_percent : 'NULL');
  228. $sql.= ", '" . $this->db->idate($this->date) . "'";
  229. $sql.= "," . ($this->note_private ? "'" . $this->db->escape($this->note_private) . "'" : "null");
  230. $sql.= "," . ($this->note_public ? "'" . $this->db->escape($this->note_public) . "'" : "null");
  231. $sql.= "," . ($this->ref_client ? "'" . $this->db->escape($this->ref_client) . "'" : "null");
  232. $sql.= "," . ($this->ref_int ? "'" . $this->db->escape($this->ref_int) . "'" : "null");
  233. $sql.= "," . ($this->fk_facture_source ? "'" . $this->db->escape($this->fk_facture_source) . "'" : "null");
  234. $sql.= "," . ($user->id > 0 ? "'" . $user->id . "'" : "null");
  235. $sql.= "," . ($this->fk_project ? $this->fk_project : "null");
  236. $sql.= ',' . $this->cond_reglement_id;
  237. $sql.= "," . $this->mode_reglement_id;
  238. $sql.= ", '" . $this->db->idate($datelim) . "', '" . $this->modelpdf . "')";
  239. dol_syslog(get_class($this) . "::create sql=" . $sql);
  240. $resql = $this->db->query($sql);
  241. if ($resql) {
  242. $this->id = $this->db->last_insert_id(MAIN_DB_PREFIX . 'facture');
  243. // Update ref with new one
  244. $this->ref = '(PROV' . $this->id . ')';
  245. $sql = 'UPDATE ' . MAIN_DB_PREFIX . "facture SET facnumber='" . $this->ref . "' WHERE rowid=" . $this->id;
  246. dol_syslog(get_class($this) . "::create sql=" . $sql);
  247. $resql = $this->db->query($sql);
  248. if (!$resql)
  249. $error++;
  250. // Add object linked
  251. if (!$error && $this->id && is_array($this->linked_objects) && !empty($this->linked_objects)) {
  252. foreach ($this->linked_objects as $origin => $origin_id) {
  253. $ret = $this->add_object_linked($origin, $origin_id);
  254. if (!$ret) {
  255. dol_print_error($this->db);
  256. $error++;
  257. }
  258. // TODO mutualiser
  259. if ($origin == 'commande') {
  260. // On recupere les differents contact interne et externe
  261. $order = new Commande($this->db);
  262. $order->id = $origin_id;
  263. // On recupere le commercial suivi propale
  264. $this->userid = $order->getIdcontact('internal', 'SALESREPFOLL');
  265. if ($this->userid) {
  266. //On passe le commercial suivi commande en commercial suivi paiement
  267. $this->add_contact($this->userid[0], 'SALESREPFOLL', 'internal');
  268. }
  269. // On recupere le contact client facturation commande
  270. $this->contactid = $order->getIdcontact('external', 'BILLING');
  271. if ($this->contactid) {
  272. //On passe le contact client facturation commande en contact client facturation
  273. $this->add_contact($this->contactid[0], 'BILLING', 'external');
  274. }
  275. }
  276. }
  277. }
  278. /*
  279. * Insert lines of invoices into database
  280. */
  281. if (count($this->lines) && is_object($this->lines[0])) {
  282. $fk_parent_line = 0;
  283. dol_syslog("There is " . count($this->lines) . " lines that are invoice lines objects");
  284. foreach ($this->lines as $i => $val) {
  285. $newinvoiceline = new FactureLigne($this->db);
  286. $newinvoiceline = $this->lines[$i];
  287. $newinvoiceline->fk_facture = $this->id;
  288. if ($result >= 0 && ($newinvoiceline->info_bits & 0x01) == 0) { // We keep only lines with first bit = 0
  289. // Reset fk_parent_line for no child products and special product
  290. if (($newinvoiceline->product_type != 9 && empty($newinvoiceline->fk_parent_line)) || $newinvoiceline->product_type == 9) {
  291. $fk_parent_line = 0;
  292. }
  293. $newinvoiceline->fk_parent_line = $fk_parent_line;
  294. $result = $newinvoiceline->insert();
  295. // Defined the new fk_parent_line
  296. if ($result > 0 && $newinvoiceline->product_type == 9) {
  297. $fk_parent_line = $result;
  298. }
  299. }
  300. if ($result < 0) {
  301. $this->error = $newinvoiceline->error;
  302. $error++;
  303. break;
  304. }
  305. }
  306. } else {
  307. $fk_parent_line = 0;
  308. dol_syslog("There is " . count($this->lines) . " lines that are array lines");
  309. foreach ($this->lines as $i => $val) {
  310. if (($this->lines[$i]->info_bits & 0x01) == 0) { // We keep only lines with first bit = 0
  311. // Reset fk_parent_line for no child products and special product
  312. if (($this->lines[$i]->product_type != 9 && empty($this->lines[$i]->fk_parent_line)) || $this->lines[$i]->product_type == 9) {
  313. $fk_parent_line = 0;
  314. }
  315. $result = $this->addline(
  316. $this->id, $this->lines[$i]->desc, $this->lines[$i]->subprice, $this->lines[$i]->qty, $this->lines[$i]->tva_tx, $this->lines[$i]->localtax1_tx, $this->lines[$i]->localtax2_tx, $this->lines[$i]->fk_product, $this->lines[$i]->remise_percent, $this->lines[$i]->date_start, $this->lines[$i]->date_end, $this->lines[$i]->fk_code_ventilation, $this->lines[$i]->info_bits, $this->lines[$i]->fk_remise_except, 'HT', 0, $this->lines[$i]->product_type, $this->lines[$i]->rang, $this->lines[$i]->special_code, '', 0, $fk_parent_line, $this->lines[$i]->fk_fournprice, $this->lines[$i]->pa_ht, $this->lines[$i]->label
  317. );
  318. if ($result < 0) {
  319. $this->error = $this->db->lasterror();
  320. dol_print_error($this->db);
  321. $this->db->rollback();
  322. return -1;
  323. }
  324. // Defined the new fk_parent_line
  325. if ($result > 0 && $this->lines[$i]->product_type == 9) {
  326. $fk_parent_line = $result;
  327. }
  328. }
  329. }
  330. }
  331. /*
  332. * Insert lines of predefined invoices
  333. */
  334. if (!$error && $this->fac_rec > 0) {
  335. foreach ($_facrec->lines as $i => $val) {
  336. if ($_facrec->lines[$i]->fk_product) {
  337. $prod = new Product($this->db);
  338. $res = $prod->fetch($_facrec->lines[$i]->fk_product);
  339. }
  340. $tva_tx = get_default_tva($mysoc, $soc, $prod->id);
  341. $localtax1_tx = get_localtax($tva_tx, 1, $soc);
  342. $localtax2_tx = get_localtax($tva_tx, 2, $soc);
  343. $result_insert = $this->addline(
  344. $this->id, $_facrec->lines[$i]->desc, $_facrec->lines[$i]->subprice, $_facrec->lines[$i]->qty, $tva_tx, $localtax1_tx, $localtax2_tx, $_facrec->lines[$i]->fk_product, $_facrec->lines[$i]->remise_percent, '', '', 0, 0, '', 'HT', 0, $_facrec->lines[$i]->product_type, $_facrec->lines[$i]->rang, $_facrec->lines[$i]->special_code, '', 0, 0, null, 0, $_facrec->lines[$i]->label
  345. );
  346. if ($result_insert < 0) {
  347. $error++;
  348. $this->error = $this->db->error();
  349. break;
  350. }
  351. }
  352. }
  353. if (!$error) {
  354. $result = $this->update_price(1);
  355. if ($result > 0) {
  356. // Appel des triggers
  357. include_once DOL_DOCUMENT_ROOT . '/core/class/interfaces.class.php';
  358. $interface = new Interfaces($this->db);
  359. $result = $interface->run_triggers('BILL_CREATE', $this, $user, $langs, $conf);
  360. if ($result < 0) {
  361. $error++;
  362. $this->errors = $interface->errors;
  363. }
  364. // Fin appel triggers
  365. if (!$error) {
  366. $this->db->commit();
  367. return $this->id;
  368. } else {
  369. $this->db->rollback();
  370. return -4;
  371. }
  372. } else {
  373. $this->error = $langs->trans('FailedToUpdatePrice');
  374. $this->db->rollback();
  375. return -3;
  376. }
  377. } else {
  378. dol_syslog(get_class($this) . "::create error " . $this->error, LOG_ERR);
  379. $this->db->rollback();
  380. return -2;
  381. }
  382. } else {
  383. $this->error = $this->db->error();
  384. dol_syslog(get_class($this) . "::create error " . $this->error . " sql=" . $sql, LOG_ERR);
  385. $this->db->rollback();
  386. return -1;
  387. }
  388. }
  389. /**
  390. * Create a new invoice in database from current invoice
  391. *
  392. * @param User $user Object user that ask creation
  393. * @param int $invertdetail Reverse sign of amounts for lines
  394. * @return int <0 if KO, >0 if OK
  395. */
  396. function createFromCurrent($user, $invertdetail = 0) {
  397. // Charge facture source
  398. $facture = new Facture($this->db);
  399. $facture->fk_facture_source = $this->fk_facture_source;
  400. $facture->type = $this->type;
  401. $facture->socid = $this->socid;
  402. $facture->date = $this->date;
  403. $facture->note_public = $this->note_public;
  404. $facture->note = $this->note;
  405. $facture->ref_client = $this->ref_client;
  406. $facture->modelpdf = $this->modelpdf;
  407. $facture->fk_project = $this->fk_project;
  408. $facture->cond_reglement_code = $this->cond_reglement_code;
  409. $facture->mode_reglement_code = $this->mode_reglement_code;
  410. $facture->remise_absolue = $this->remise_absolue;
  411. $facture->remise_percent = $this->remise_percent;
  412. $facture->Status = "NOT_PAID";
  413. $facture->ref = $this->getNextNumRef($this->socid);
  414. // $facture->lines = $this->lines; // Tableau des lignes de factures
  415. // $facture->products = $this->lines; // Tant que products encore utilise
  416. // // Loop on each line of new invoice
  417. // foreach ($facture->lines as $i => $line) {
  418. // if ($invertdetail) {
  419. // $facture->lines[$i]->subprice = -$facture->lines[$i]->subprice;
  420. // $facture->lines[$i]->total_ht = -$facture->lines[$i]->total_ht;
  421. // $facture->lines[$i]->total_tva = -$facture->lines[$i]->total_tva;
  422. // $facture->lines[$i]->total_localtax1 = -$facture->lines[$i]->total_localtax1;
  423. // $facture->lines[$i]->total_localtax2 = -$facture->lines[$i]->total_localtax2;
  424. // $facture->lines[$i]->total_ttc = -$facture->lines[$i]->total_ttc;
  425. // }
  426. // }
  427. dol_syslog(get_class($this) . "::createFromCurrent invertdetail=" . $invertdetail . " socid=" . $this->socid . " nboflines=" . count($facture->lines));
  428. $facid = $facture->create($user);
  429. // Copier les lignes de la facture
  430. $this->getLinesArray();
  431. foreach ($this->lines as $line) {
  432. unset($line->id);
  433. unset($line->_id);
  434. unset($line->_rev);
  435. $line->fk_facture = $facid;
  436. $line->record();
  437. }
  438. $facture->update_price();
  439. // if ($facid <= 0) {
  440. // $this->error = $facture->error;
  441. // $this->errors = $facture->errors;
  442. // }
  443. return $facid;
  444. }
  445. /**
  446. * Load an object from its id and create a new one in database
  447. *
  448. * @param int $socid Id of thirdparty
  449. * @return int New id of clone
  450. */
  451. function createFromClone($socid = 0) {
  452. global $conf, $user, $langs, $hookmanager;
  453. $error = 0;
  454. $this->db->begin();
  455. // Load source object
  456. $objFrom = dol_clone($this);
  457. // Change socid if needed
  458. if (!empty($socid) && $socid != $this->socid) {
  459. $objsoc = new Societe($this->db);
  460. if ($objsoc->fetch($socid) > 0) {
  461. $this->socid = $objsoc->id;
  462. $this->cond_reglement_id = (!empty($objsoc->cond_reglement_id) ? $objsoc->cond_reglement_id : 0);
  463. $this->mode_reglement_id = (!empty($objsoc->mode_reglement_id) ? $objsoc->mode_reglement_id : 0);
  464. $this->fk_project = '';
  465. $this->fk_delivery_address = '';
  466. }
  467. // TODO Change product price if multi-prices
  468. }
  469. $this->id = 0;
  470. $this->statut = 0;
  471. // Clear fields
  472. $this->user_author = $user->id;
  473. $this->user_valid = '';
  474. $this->fk_facture_source = 0;
  475. $this->date_creation = '';
  476. $this->date_validation = '';
  477. $this->ref_client = '';
  478. $this->close_code = '';
  479. $this->close_note = '';
  480. $this->products = $this->lines; // Tant que products encore utilise
  481. // Loop on each line of new invoice
  482. foreach ($this->lines as $i => $line) {
  483. if (($this->lines[$i]->info_bits & 0x02) == 0x02) { // We do not clone line of discounts
  484. unset($this->lines[$i]);
  485. unset($this->products[$i]); // Tant que products encore utilise
  486. }
  487. }
  488. // Create clone
  489. $result = $this->create($user);
  490. if ($result < 0)
  491. $error++;
  492. if (!$error) {
  493. // Hook of thirdparty module
  494. if (is_object($hookmanager)) {
  495. $parameters = array('objFrom' => $objFrom);
  496. $action = '';
  497. $reshook = $hookmanager->executeHooks('createFrom', $parameters, $this, $action); // Note that $action and $object may have been modified by some hooks
  498. if ($reshook < 0)
  499. $error++;
  500. }
  501. // Appel des triggers
  502. include_once DOL_DOCUMENT_ROOT . '/core/class/interfaces.class.php';
  503. $interface = new Interfaces($this->db);
  504. $result = $interface->run_triggers('BILL_CLONE', $this, $user, $langs, $conf);
  505. if ($result < 0) {
  506. $error++;
  507. $this->errors = $interface->errors;
  508. }
  509. // Fin appel triggers
  510. }
  511. // End
  512. if (!$error) {
  513. $this->db->commit();
  514. return $this->id;
  515. } else {
  516. $this->db->rollback();
  517. return -1;
  518. }
  519. }
  520. /**
  521. * Load an object from an order and create a new invoice into database
  522. *
  523. * @param Object $object Object source
  524. * @return int <0 if KO, 0 if nothing done, 1 if OK
  525. */
  526. function createFromOrder($object) {
  527. global $conf, $user, $langs, $hookmanager;
  528. $error = 0;
  529. // Closed order
  530. $this->date = dol_now();
  531. $this->source = 0;
  532. $num = count($object->lines);
  533. for ($i = 0; $i < $num; $i++) {
  534. $line = new FactureLigne($this->db);
  535. $line->libelle = $object->lines[$i]->libelle;
  536. $line->label = $object->lines[$i]->label;
  537. $line->desc = $object->lines[$i]->desc;
  538. $line->subprice = $object->lines[$i]->subprice;
  539. $line->total_ht = $object->lines[$i]->total_ht;
  540. $line->total_tva = $object->lines[$i]->total_tva;
  541. $line->total_ttc = $object->lines[$i]->total_ttc;
  542. $line->tva_tx = $object->lines[$i]->tva_tx;
  543. $line->localtax1_tx = $object->lines[$i]->localtax1_tx;
  544. $line->localtax2_tx = $object->lines[$i]->localtax2_tx;
  545. $line->qty = $object->lines[$i]->qty;
  546. $line->fk_remise_except = $object->lines[$i]->fk_remise_except;
  547. $line->remise_percent = $object->lines[$i]->remise_percent;
  548. $line->fk_product = $object->lines[$i]->fk_product;
  549. $line->info_bits = $object->lines[$i]->info_bits;
  550. $line->product_type = $object->lines[$i]->product_type;
  551. $line->rang = $object->lines[$i]->rang;
  552. $line->special_code = $object->lines[$i]->special_code;
  553. $line->fk_parent_line = $object->lines[$i]->fk_parent_line;
  554. $this->lines[$i] = $line;
  555. }
  556. $this->socid = $object->socid;
  557. $this->fk_project = $object->fk_project;
  558. $this->cond_reglement_id = $object->cond_reglement_id;
  559. $this->mode_reglement_id = $object->mode_reglement_id;
  560. $this->availability_id = $object->availability_id;
  561. $this->demand_reason_id = $object->demand_reason_id;
  562. $this->date_livraison = $object->date_livraison;
  563. $this->fk_delivery_address = $object->fk_delivery_address;
  564. $this->contact_id = $object->contactid;
  565. $this->ref_client = $object->ref_client;
  566. $this->note = $object->note;
  567. $this->note_public = $object->note_public;
  568. $this->origin = $object->element;
  569. $this->origin_id = $object->id;
  570. // Possibility to add external linked objects with hooks
  571. $this->linked_objects[$this->origin] = $this->origin_id;
  572. if (!empty($object->other_linked_objects) && is_array($object->other_linked_objects)) {
  573. $this->linked_objects = array_merge($this->linked_objects, $object->other_linked_objects);
  574. }
  575. $ret = $this->create($user);
  576. if ($ret > 0) {
  577. // Actions hooked (by external module)
  578. $hookmanager->initHooks(array('invoicedao'));
  579. $parameters = array('objFrom' => $object);
  580. $action = '';
  581. $reshook = $hookmanager->executeHooks('createFrom', $parameters, $this, $action); // Note that $action and $object may have been modified by some hooks
  582. if ($reshook < 0)
  583. $error++;
  584. if (!$error) {
  585. return 1;
  586. }
  587. else
  588. return -1;
  589. }
  590. else
  591. return -1;
  592. }
  593. /**
  594. * Return clicable link of object (with eventually picto)
  595. *
  596. * @param int $withpicto Add picto into link
  597. * @param string $option Where point the link
  598. * @param int $max Maxlength of ref
  599. * @param int $short 1=Return just URL
  600. * @param string $moretitle Add more text to title tooltip
  601. * @return string String with URL
  602. */
  603. function getNomUrl($withpicto = 0, $option = '', $max = 0, $short = 0, $moretitle = '') {
  604. global $langs;
  605. $result = '';
  606. if ($option == 'withdraw')
  607. $url = DOL_URL_ROOT . '/compta/facture/prelevement.php?facid=' . $this->id;
  608. else
  609. $url = DOL_URL_ROOT . '/facture/fiche.php?id=' . $this->id;
  610. if ($short)
  611. return $url;
  612. $picto = 'bill';
  613. if ($this->type == 1)
  614. $picto.='r'; // Replacement invoice
  615. if ($this->type == 2)
  616. $picto.='a'; // Credit note
  617. if ($this->type == 3)
  618. $picto.='d'; // Deposit invoice
  619. $label = $langs->trans("ShowInvoice") . ': ' . $this->ref;
  620. if ($this->type == 1)
  621. $label = $langs->transnoentitiesnoconv("ShowInvoiceReplace") . ': ' . $this->ref;
  622. if ($this->type == 2)
  623. $label = $langs->transnoentitiesnoconv("ShowInvoiceAvoir") . ': ' . $this->ref;
  624. if ($this->type == 3)
  625. $label = $langs->transnoentitiesnoconv("ShowInvoiceDeposit") . ': ' . $this->ref;
  626. if ($moretitle)
  627. $label.=' - ' . $moretitle;
  628. //$linkstart='<a href="'.$url.'" title="'.dol_escape_htmltag($label).'">';
  629. $linkstart = '<a href="' . $url . '">';
  630. $linkend = '</a>';
  631. if ($withpicto)
  632. $result.=($linkstart . img_object(($max ? dol_trunc($label, $max) : $label), $picto) . $linkend);
  633. if ($withpicto && $withpicto != 2)
  634. $result.=' ';
  635. if ($withpicto != 2)
  636. $result.=$linkstart . ($max ? dol_trunc($this->ref, $max) : $this->ref) . $linkend;
  637. return $result;
  638. }
  639. /**
  640. * Get object and lines from database
  641. *
  642. * @param int $rowid Id of object to load
  643. * @param string $ref Reference of invoice
  644. * @param string $ref_ext External reference of invoice
  645. * @param int $ref_int Internal reference of other object
  646. * @return int >0 if OK, <0 if KO, 0 if not found
  647. */
  648. function fetch($rowid, $ref = '', $ref_ext = '', $ref_int = '') {
  649. global $conf;
  650. return parent::fetch($rowid);
  651. }
  652. /**
  653. * Load all detailed lines into this->lines
  654. *
  655. * @return int 1 if OK, < 0 if KO
  656. */
  657. function fetch_lines() {
  658. $this->lines = array();
  659. $sql = 'SELECT l.rowid, l.fk_product, l.fk_parent_line, l.label as custom_label, l.description, l.product_type, l.price, l.qty, l.tva_tx, ';
  660. $sql.= ' l.localtax1_tx, l.localtax2_tx, l.remise, l.remise_percent, l.fk_remise_except, l.subprice,';
  661. $sql.= ' l.rang, l.special_code,';
  662. $sql.= ' l.date_start as date_start, l.date_end as date_end,';
  663. $sql.= ' l.info_bits, l.total_ht, l.total_tva, l.total_localtax1, l.total_localtax2, l.total_ttc, l.fk_code_ventilation, l.fk_export_compta, l.fk_product_fournisseur_price as fk_fournprice, l.buy_price_ht as pa_ht,';
  664. $sql.= ' p.ref as product_ref, p.fk_product_type as fk_product_type, p.label as product_label, p.description as product_desc';
  665. $sql.= ' FROM ' . MAIN_DB_PREFIX . 'facturedet as l';
  666. $sql.= ' LEFT JOIN ' . MAIN_DB_PREFIX . 'product as p ON l.fk_product = p.rowid';
  667. $sql.= ' WHERE l.fk_facture = ' . $this->id;
  668. $sql.= ' ORDER BY l.rang';
  669. dol_syslog(get_class($this) . '::fetch_lines sql=' . $sql, LOG_DEBUG);
  670. $result = $this->db->query($sql);
  671. if ($result) {
  672. $num = $this->db->num_rows($result);
  673. $i = 0;
  674. while ($i < $num) {
  675. $objp = $this->db->fetch_object($result);
  676. $line = new FactureLigne($this->db);
  677. $line->rowid = $objp->rowid;
  678. $line->label = $objp->custom_label;
  679. $line->desc = $objp->description; // Description line
  680. $line->product_type = $objp->product_type; // Type of line
  681. $line->product_ref = $objp->product_ref; // Ref product
  682. $line->libelle = $objp->product_label; // TODO deprecated
  683. $line->product_label = $objp->product_label; // Label product
  684. $line->product_desc = $objp->product_desc; // Description product
  685. $line->fk_product_type = $objp->fk_product_type; // Type of product
  686. $line->qty = $objp->qty;
  687. $line->subprice = $objp->subprice;
  688. $line->tva_tx = $objp->tva_tx;
  689. $line->localtax1_tx = $objp->localtax1_tx;
  690. $line->localtax2_tx = $objp->localtax2_tx;
  691. $line->remise_percent = $objp->remise_percent;
  692. $line->fk_remise_except = $objp->fk_remise_except;
  693. $line->fk_product = $objp->fk_product;
  694. $line->date_start = $this->db->jdate($objp->date_start);
  695. $line->date_end = $this->db->jdate($objp->date_end);
  696. $line->date_start = $this->db->jdate($objp->date_start);
  697. $line->date_end = $this->db->jdate($objp->date_end);
  698. $line->info_bits = $objp->info_bits;
  699. $line->total_ht = $objp->total_ht;
  700. $line->total_tva = $objp->total_tva;
  701. $line->total_localtax1 = $objp->total_localtax1;
  702. $line->total_localtax2 = $objp->total_localtax2;
  703. $line->total_ttc = $objp->total_ttc;
  704. $line->export_compta = $objp->fk_export_compta;
  705. $line->code_ventilation = $objp->fk_code_ventilation;
  706. $line->fk_fournprice = $objp->fk_fournprice;
  707. $marginInfos = getMarginInfos($objp->subprice, $objp->remise_percent, $objp->tva_tx, $objp->localtax1_tx, $objp->localtax2_tx, $line->fk_fournprice, $objp->pa_ht);
  708. $line->pa_ht = $marginInfos[0];
  709. $line->marge_tx = $marginInfos[1];
  710. $line->marque_tx = $marginInfos[2];
  711. $line->rang = $objp->rang;
  712. $line->special_code = $objp->special_code;
  713. $line->fk_parent_line = $objp->fk_parent_line;
  714. // Ne plus utiliser
  715. //$line->price = $objp->price;
  716. //$line->remise = $objp->remise;
  717. $this->lines[$i] = $line;
  718. $i++;
  719. }
  720. $this->db->free($result);
  721. return 1;
  722. } else {
  723. $this->error = $this->db->error();
  724. dol_syslog(get_class($this) . '::fetch_lines ' . $this->error, LOG_ERR);
  725. return -3;
  726. }
  727. }
  728. /**
  729. * Update database
  730. *
  731. * @param User $user User that modify
  732. * @param int $notrigger 0=launch triggers after, 1=disable triggers
  733. * @return int <0 if KO, >0 if OK
  734. */
  735. function update($user = 0, $notrigger = 0) {
  736. global $conf, $langs;
  737. $error = 0;
  738. // Clean parameters
  739. if (empty($this->type))
  740. $this->type = "INVOICE_STANDARD";
  741. if (isset($this->facnumber))
  742. $this->facnumber = trim($this->ref);
  743. if (isset($this->ref_client))
  744. $this->ref_client = trim($this->ref_client);
  745. if (isset($this->increment))
  746. $this->increment = trim($this->increment);
  747. if (isset($this->close_code))
  748. $this->close_code = trim($this->close_code);
  749. if (isset($this->close_note))
  750. $this->close_note = trim($this->close_note);
  751. if (isset($this->note) || isset($this->note_private))
  752. $this->note = (isset($this->note) ? trim($this->note) : trim($this->note_private)); // deprecated
  753. if (isset($this->note) || isset($this->note_private))
  754. $this->note_private = (isset($this->note_private) ? trim($this->note_private) : trim($this->note));
  755. if (isset($this->note_public))
  756. $this->note_public = trim($this->note_public);
  757. if (isset($this->modelpdf))
  758. $this->modelpdf = trim($this->modelpdf);
  759. if (isset($this->import_key))
  760. $this->import_key = trim($this->import_key);
  761. $soc = new Societe($db);
  762. $soc->fetch($this->socid);
  763. if ($this->client->id != $soc->id) {
  764. $this->client->id = $soc->id;
  765. $this->client->name = $soc->name;
  766. }
  767. $this->record();
  768. if (!$notrigger) {
  769. // Call triggers
  770. include_once DOL_DOCUMENT_ROOT . '/core/class/interfaces.class.php';
  771. $interface = new Interfaces($this->db);
  772. $result = $interface->run_triggers('BILL_MODIFY', $this, $user, $langs, $conf);
  773. if ($result < 0) {
  774. $error++;
  775. $this->errors = $interface->errors;
  776. }
  777. }
  778. // End call triggers
  779. return 1;
  780. // Check parameters
  781. // Put here code to add control on parameters values
  782. // Update request
  783. $sql = "UPDATE " . MAIN_DB_PREFIX . "facture SET";
  784. $sql.= " facnumber=" . (isset($this->ref) ? "'" . $this->db->escape($this->ref) . "'" : "null") . ",";
  785. $sql.= " type=" . (isset($this->type) ? $this->type : "null") . ",";
  786. $sql.= " ref_client=" . (isset($this->ref_client) ? "'" . $this->db->escape($this->ref_client) . "'" : "null") . ",";
  787. $sql.= " increment=" . (isset($this->increment) ? "'" . $this->db->escape($this->increment) . "'" : "null") . ",";
  788. $sql.= " fk_soc=" . (isset($this->socid) ? $this->socid : "null") . ",";
  789. $sql.= " datec=" . (strval($this->date_creation) != '' ? "'" . $this->db->idate($this->date_creation) . "'" : 'null') . ",";
  790. $sql.= " datef=" . (strval($this->date) != '' ? "'" . $this->db->idate($this->date) . "'" : 'null') . ",";
  791. $sql.= " date_valid=" . (strval($this->date_validation) != '' ? "'" . $this->db->idate($this->date_validation) . "'" : 'null') . ",";
  792. $sql.= " paye=" . (isset($this->paye) ? $this->paye : "null") . ",";
  793. $sql.= " remise_percent=" . (isset($this->remise_percent) ? $this->remise_percent : "null") . ",";
  794. $sql.= " remise_absolue=" . (isset($this->remise_absolue) ? $this->remise_absolue : "null") . ",";
  795. //$sql.= " remise=".(isset($this->remise)?$this->remise:"null").",";
  796. $sql.= " close_code=" . (isset($this->close_code) ? "'" . $this->db->escape($this->close_code) . "'" : "null") . ",";
  797. $sql.= " close_note=" . (isset($this->close_note) ? "'" . $this->db->escape($this->close_note) . "'" : "null") . ",";
  798. $sql.= " tva=" . (isset($this->total_tva) ? $this->total_tva : "null") . ",";
  799. $sql.= " localtax1=" . (isset($this->total_localtax1) ? $this->total_localtax1 : "null") . ",";
  800. $sql.= " localtax2=" . (isset($this->total_localtax2) ? $this->total_localtax2 : "null") . ",";
  801. $sql.= " total=" . (isset($this->total_ht) ? $this->total_ht : "null") . ",";
  802. $sql.= " total_ttc=" . (isset($this->total_ttc) ? $this->total_ttc : "null") . ",";
  803. $sql.= " fk_statut=" . (isset($this->statut) ? $this->statut : "null") . ",";
  804. $sql.= " fk_user_author=" . (isset($this->user_author) ? $this->user_author : "null") . ",";
  805. $sql.= " fk_user_valid=" . (isset($this->fk_user_valid) ? $this->fk_user_valid : "null") . ",";
  806. $sql.= " fk_facture_source=" . (isset($this->fk_facture_source) ? $this->fk_facture_source : "null") . ",";
  807. $sql.= " fk_projet=" . (isset($this->fk_project) ? $this->fk_project : "null") . ",";
  808. $sql.= " fk_cond_reglement=" . (isset($this->cond_reglement_id) ? $this->cond_reglement_id : "null") . ",";
  809. $sql.= " fk_mode_reglement=" . (isset($this->mode_reglement_id) ? $this->mode_reglement_id : "null") . ",";
  810. $sql.= " date_lim_reglement=" . (strval($this->date_lim_reglement) != '' ? "'" . $this->db->idate($this->date_lim_reglement) . "'" : 'null') . ",";
  811. $sql.= " note=" . (isset($this->note_private) ? "'" . $this->db->escape($this->note_private) . "'" : "null") . ",";
  812. $sql.= " note_public=" . (isset($this->note_public) ? "'" . $this->db->escape($this->note_public) . "'" : "null") . ",";
  813. $sql.= " model_pdf=" . (isset($this->modelpdf) ? "'" . $this->db->escape($this->modelpdf) . "'" : "null") . ",";
  814. $sql.= " import_key=" . (isset($this->import_key) ? "'" . $this->db->escape($this->import_key) . "'" : "null") . "";
  815. $sql.= " WHERE rowid=" . $this->id;
  816. $this->db->begin();
  817. dol_syslog(get_class($this) . "::update sql=" . $sql, LOG_DEBUG);
  818. $resql = $this->db->query($sql);
  819. if (!$resql) {
  820. $error++;
  821. $this->errors[] = "Error " . $this->db->lasterror();
  822. }
  823. if (!$error) {
  824. if (!$notrigger) {
  825. // Call triggers
  826. include_once DOL_DOCUMENT_ROOT . '/core/class/interfaces.class.php';
  827. $interface = new Interfaces($this->db);
  828. $result = $interface->run_triggers('BILL_MODIFY', $this, $user, $langs, $conf);
  829. if ($result < 0) {
  830. $error++;
  831. $this->errors = $interface->errors;
  832. }
  833. // End call triggers
  834. }
  835. }
  836. // Commit or rollback
  837. if ($error) {
  838. foreach ($this->errors as $errmsg) {
  839. dol_syslog(get_class($this) . "::update " . $errmsg, LOG_ERR);
  840. $this->error.=($this->error ? ', ' . $errmsg : $errmsg);
  841. }
  842. $this->db->rollback();
  843. return -1 * $error;
  844. } else {
  845. $this->db->commit();
  846. return 1;
  847. }
  848. }
  849. /**
  850. * Add a discount line into invoice using an existing absolute discount
  851. *
  852. * @param int $idremise Id of absolute discount
  853. * @return int >0 if OK, <0 if KO
  854. */
  855. function insert_discount($idremise) {
  856. global $langs;
  857. include_once DOL_DOCUMENT_ROOT . '/core/lib/price.lib.php';
  858. include_once DOL_DOCUMENT_ROOT . '/core/class/discount.class.php';
  859. $remise = new DiscountAbsolute($this->db);
  860. $result = $remise->fetch($idremise);
  861. if (!empty($result)) {
  862. if ($remise->fk_facture) { // Protection against multiple submission
  863. $this->error = $langs->trans("ErrorDiscountAlreadyUsed");
  864. return -5;
  865. }
  866. $facligne = new FactureLigne($this->db);
  867. $facligne->fk_facture = $this->id;
  868. $facligne->fk_remise_except = $remise->id;
  869. $facligne->description = $remise->description; // Description ligne
  870. $facligne->tva_tx = $remise->tva_tx;
  871. $facligne->subprice = -$remise->amount_ht;
  872. $facligne->fk_product = 0; // Id produit predefini
  873. $facligne->qty = 1;
  874. $facligne->remise_percent = 0;
  875. $facligne->rang = -1;
  876. $facligne->info_bits = 2;
  877. $facligne->total_ht = -$remise->amount_ht;
  878. $facligne->total_tva = -$remise->amount_tva;
  879. $facligne->total_ttc = -$remise->amount_ttc;
  880. $lineid = $facligne->insert();
  881. if (!empty($lineid)) {
  882. $result = $this->update_price();
  883. if ($result > 0) {
  884. // Create linke between discount and invoice line
  885. $result = $remise->link_to_invoice($lineid, 0);
  886. if ($result < 0) {
  887. $this->error = $remise->error;
  888. return -4;
  889. }
  890. return 1;
  891. } else {
  892. $this->error = $facligne->error;
  893. return -1;
  894. }
  895. } else {
  896. $this->error = $facligne->error;
  897. return -2;
  898. }
  899. } else {
  900. return -3;
  901. }
  902. }
  903. /**
  904. * Set customer ref
  905. *
  906. * @param string $ref_client Customer ref
  907. * @return int <0 if KO, >0 if OK
  908. */
  909. function set_ref_client($ref_client) {
  910. $sql = 'UPDATE ' . MAIN_DB_PREFIX . 'facture';
  911. if (empty($ref_client))
  912. $sql .= ' SET ref_client = NULL';
  913. else
  914. $sql .= ' SET ref_client = \'' . $this->db->escape($ref_client) . '\'';
  915. $sql .= ' WHERE rowid = ' . $this->id;
  916. if ($this->db->query($sql)) {
  917. $this->ref_client = $ref_client;
  918. return 1;
  919. } else {
  920. dol_print_error($this->db);
  921. return -1;
  922. }
  923. }
  924. /**
  925. * Delete invoice
  926. *
  927. * @param int $rowid Id of invoice to delete. If empty, we delete current instance of invoice
  928. * @param int $notrigger 1=Does not execute triggers, 0= execute triggers
  929. * @return int <0 if KO, >0 if OK
  930. */
  931. function delete($rowid = 0, $notrigger = 0) {
  932. global $user, $langs, $conf;
  933. require_once DOL_DOCUMENT_ROOT . '/core/lib/files.lib.php';
  934. if (!$rowid)
  935. $rowid = $this->id;
  936. dol_syslog(get_class($this) . "::delete rowid=" . $rowid, LOG_DEBUG);
  937. // TODO Test if there is at least on payment. If yes, refuse to delete.
  938. $error = 0;
  939. // Supprimer les lignes de la facture
  940. $this->getLinesArray();
  941. foreach ($this->lines as $line) {
  942. $line->delete();
  943. }
  944. $this->deleteDoc();
  945. if (!$error && !$notrigger) {
  946. // Appel des triggers
  947. include_once DOL_DOCUMENT_ROOT . '/core/class/interfaces.class.php';
  948. $interface = new Interfaces($this->db);
  949. $result = $interface->run_triggers('BILL_DELETE', $this, $user, $langs, $conf);
  950. if ($result < 0) {
  951. $error++;
  952. $this->errors = $interface->errors;
  953. }
  954. // Fin appel triggers
  955. }
  956. return 1;
  957. if (!$error) {
  958. // Delete linked object
  959. $res = $this->deleteObjectLinked();
  960. if ($res < 0)
  961. $error++;
  962. }
  963. if (!$error) {
  964. // If invoice was converted into a discount not yet consumed, we remove discount
  965. $sql = 'DELETE FROM ' . MAIN_DB_PREFIX . 'societe_remise_except';
  966. $sql.= ' WHERE fk_facture_source = ' . $rowid;
  967. $sql.= ' AND fk_facture_line IS NULL';
  968. $resql = $this->db->query($sql);
  969. // If invoice has consumned discounts
  970. $this->fetch_lines();
  971. $list_rowid_det = array();
  972. foreach ($this->lines as $key => $invoiceline) {
  973. $list_rowid_det[] = $invoiceline->rowid;
  974. }
  975. // Consumned discounts are freed
  976. if (count($list_rowid_det)) {
  977. $sql = 'UPDATE ' . MAIN_DB_PREFIX . 'societe_remise_except';
  978. $sql.= ' SET fk_facture = NULL, fk_facture_line = NULL';
  979. $sql.= ' WHERE fk_facture_line IN (' . join(',', $list_rowid_det) . ')';
  980. dol_syslog(get_class($this) . "::delete sql=" . $sql);
  981. if (!$this->db->query($sql)) {
  982. $this->error = $this->db->error() . " sql=" . $sql;
  983. dol_syslog(get_class($this) . "::delete " . $this->error, LOG_ERR);
  984. $this->db->rollback();
  985. return -5;
  986. }
  987. }
  988. // Delete invoice line
  989. $sql = 'DELETE FROM ' . MAIN_DB_PREFIX . 'facturedet WHERE fk_facture = ' . $rowid;
  990. if ($this->db->query($sql) && $this->delete_linked_contact()) {
  991. $sql = 'DELETE FROM ' . MAIN_DB_PREFIX . 'facture WHERE rowid = ' . $rowid;
  992. $resql = $this->db->query($sql);
  993. if ($resql) {
  994. // On efface le repertoire de pdf provisoire
  995. $ref = dol_sanitizeFileName($this->ref);
  996. if ($conf->facture->dir_output) {
  997. $dir = $conf->facture->dir_output . "/" . $ref;
  998. $file = $conf->facture->dir_output . "/" . $ref . "/" . $ref . ".pdf";
  999. if (file_exists($file)) { // We must delete all files before deleting directory
  1000. $ret = dol_delete_preview($this);
  1001. if (!dol_delete_file($file, 0, 0, 0, $this)) { // For triggers
  1002. $this->error = $langs->trans("ErrorCanNotDeleteFile", $file);
  1003. $this->db->rollback();
  1004. return 0;
  1005. }
  1006. }
  1007. if (file_exists($dir)) {
  1008. if (!dol_delete_dir_recursive($dir)) { // For remove dir and meta
  1009. $this->error = $langs->trans("ErrorCanNotDeleteDir", $dir);
  1010. $this->db->rollback();
  1011. return 0;
  1012. }
  1013. }
  1014. }
  1015. $this->db->commit();
  1016. return 1;
  1017. } else {
  1018. $this->error = $this->db->lasterror() . " sql=" . $sql;
  1019. dol_syslog(get_class($this) . "::delete " . $this->error, LOG_ERR);
  1020. $this->db->rollback();
  1021. return -6;
  1022. }
  1023. } else {
  1024. $this->error = $this->db->lasterror() . " sql=" . $sql;
  1025. dol_syslog(get_class($this) . "::delete " . $this->error, LOG_ERR);
  1026. $this->db->rollback();
  1027. return -4;
  1028. }
  1029. } else {
  1030. $this->error = $this->db->lasterror();
  1031. dol_syslog(get_class($this) . "::delete " . $this->error, LOG_ERR);
  1032. $this->db->rollback();
  1033. return -2;
  1034. }
  1035. }
  1036. /**
  1037. * Renvoi une date limite de reglement de facture en fonction des
  1038. * conditions de reglements de la facture et date de facturation
  1039. *
  1040. * @param string $cond_reglement Condition of payment (code or id) to use. If 0, we use current condition.
  1041. * @return date Date limite de reglement si ok, <0 si ko
  1042. */
  1043. function calculate_date_lim_reglement($cond_reglement = 0) {
  1044. if (empty($cond_reglement))
  1045. $cond_reglement = $this->cond_reglement_code;
  1046. $data = $this->fk_extrafields->fields->cond_reglement_code->values->{$cond_reglement};
  1047. $cdr_nbjour = $data->nbjour;
  1048. $cdr_fdm = $data->fdm;
  1049. $cdr_decalage = $data->decalage;
  1050. /* Definition de la date limite */
  1051. // 1 : ajout du nombre de jours
  1052. $datelim = $this->date + ($cdr_nbjour * 3600 * 24);
  1053. // 2 : application de la regle "fin de mois"
  1054. if ($cdr_fdm) {
  1055. $mois = date('m', $datelim);
  1056. $annee = date('Y', $datelim);
  1057. if ($mois == 12) {
  1058. $mois = 1;
  1059. $annee += 1;
  1060. } else {
  1061. $mois += 1;
  1062. }
  1063. // On se deplace au debut du mois suivant, et on retire un jour
  1064. $datelim = dol_mktime(12, 0, 0, $mois, 1, $annee);
  1065. $datelim -= (3600 * 24);
  1066. }
  1067. // 3 : application du decalage
  1068. $datelim += ($cdr_decalage * 3600 * 24);
  1069. return $datelim;
  1070. }
  1071. /**
  1072. * Tag la facture comme paye completement (close_code non renseigne) ou partiellement (close_code renseigne) + appel trigger BILL_PAYED
  1073. *
  1074. * @param User $user Objet utilisateur qui modifie
  1075. * @param string $close_code Code renseigne si on classe a payee completement alors que paiement incomplet (cas escompte par exemple)
  1076. * @param string $close_note Commentaire renseigne si on classe a payee alors que paiement incomplet (cas escompte par exemple)
  1077. * @return int <0 if KO, >0 if OK
  1078. */
  1079. function set_paid($user, $close_code = '', $close_note = '') {
  1080. global $conf, $langs;
  1081. $error = 0;
  1082. if ($close_code)
  1083. $this->Status = "PAID_PARTIALLY";
  1084. else $this->Status = "PAID";
  1085. $this->record();
  1086. // Appel des triggers
  1087. include_once DOL_DOCUMENT_ROOT . '/core/class/interfaces.class.php';
  1088. $interface = new Interfaces($this->db);
  1089. $result = $interface->run_triggers('BILL_PAYED', $this, $user, $langs, $conf);
  1090. if ($result < 0) {
  1091. $error++;
  1092. $this->errors = $interface->errors;
  1093. }
  1094. // Fin appel triggers
  1095. return 1;
  1096. if ($this->paye != 1) {
  1097. $this->db->begin();
  1098. dol_syslog(get_class($this) . "::set_paid rowid=" . $this->id, LOG_DEBUG);
  1099. $sql = 'UPDATE ' . MAIN_DB_PREFIX . 'facture SET';
  1100. $sql.= ' fk_statut=2';
  1101. if (!$close_code)
  1102. $sql.= ', paye=1';
  1103. if ($close_code)
  1104. $sql.= ", close_code='" . $this->db->escape($close_code) . "'";
  1105. if ($close_note)
  1106. $sql.= ", close_note='" . $this->db->escape($close_note) . "'";
  1107. $sql.= ' WHERE rowid = ' . $this->id;
  1108. $resql = $this->db->query($sql);
  1109. if ($resql) {
  1110. // Appel des triggers
  1111. include_once DOL_DOCUMENT_ROOT . '/core/class/interfaces.class.php';
  1112. $interface = new Interfaces($this->db);
  1113. $result = $interface->run_triggers('BILL_PAYED', $this, $user, $langs, $conf);
  1114. if ($result < 0) {
  1115. $error++;
  1116. $this->errors = $interface->errors;
  1117. }
  1118. // Fin appel triggers
  1119. } else {
  1120. $error++;
  1121. $this->error = $this->db->error();
  1122. dol_print_error($this->db);
  1123. }
  1124. if (!$error) {
  1125. $this->db->commit();
  1126. return 1;
  1127. } else {
  1128. $this->db->rollback();
  1129. return -1;
  1130. }
  1131. } else {
  1132. return 0;
  1133. }
  1134. }
  1135. /**
  1136. * Tag la facture comme non payee completement + appel trigger BILL_UNPAYED
  1137. * Fonction utilisee quand un paiement prelevement est refuse,
  1138. * ou quand une facture annulee et reouverte.
  1139. *
  1140. * @param User $user Object user that change status
  1141. * @return int <0 if KO, >0 if OK
  1142. */
  1143. function set_unpaid($user) {
  1144. global $conf, $langs;
  1145. $error = 0;
  1146. if ($this->getSommePaiement() > 0)
  1147. $this->Status = "STARTED";
  1148. else
  1149. $this->Status = "NOT_PAID";
  1150. $this->record();
  1151. // Appel des triggers
  1152. include_once DOL_DOCUMENT_ROOT . '/core/class/interfaces.class.php';
  1153. $interface = new Interfaces($this->db);
  1154. $result = $interface->run_triggers('BILL_UNPAYED', $this, $user, $langs, $conf);
  1155. if ($result < 0) {
  1156. $error++;
  1157. $this->errors = $interface->errors;
  1158. }
  1159. // Fin appel triggers
  1160. return 1;
  1161. $this->db->begin();
  1162. $sql = 'UPDATE ' . MAIN_DB_PREFIX . 'facture';
  1163. $sql.= ' SET paye=0, fk_statut=1, close_code=null, close_note=null';
  1164. $sql.= ' WHERE rowid = ' . $this->id;
  1165. dol_syslog(get_class($this) . "::set_unpaid sql=" . $sql);
  1166. $resql = $this->db->query($sql);
  1167. if ($resql) {
  1168. // Appel des triggers
  1169. include_once DOL_DOCUMENT_ROOT . '/core/class/interfaces.class.php';
  1170. $interface = new Interfaces($this->db);
  1171. $result = $interface->run_triggers('BILL_UNPAYED', $this, $user, $langs, $conf);
  1172. if ($result < 0) {
  1173. $error++;
  1174. $this->errors = $interface->errors;
  1175. }
  1176. // Fin appel triggers
  1177. } else {
  1178. $error++;
  1179. $this->error = $this->db->error();
  1180. dol_print_error($this->db);
  1181. }
  1182. if (!$error) {
  1183. $this->db->commit();
  1184. return 1;
  1185. } else {
  1186. $this->db->rollback();
  1187. return -1;
  1188. }
  1189. }
  1190. /**
  1191. * Tag invoice as canceled, with no payment on it (example for replacement invoice or payment never received) + call trigger BILL_CANCEL
  1192. * Warning, if option to decrease stock on invoice was set, this function does not change stock (it might be a cancel because
  1193. * of no payment even if merchandises were sent).
  1194. *
  1195. * @param User $user Object user making change
  1196. * @param string $close_code Code de fermeture
  1197. * @param string $close_note Comment
  1198. * @return int <0 if KO, >0 if OK
  1199. */
  1200. function set_canceled($user, $close_code = '', $close_note = '') {
  1201. global $conf, $langs;
  1202. $error = 0;
  1203. $this->Status = "CANCELED";
  1204. $this->record();
  1205. // Appel des triggers
  1206. include_once DOL_DOCUMENT_ROOT . '/core/class/interfaces.class.php';
  1207. $interface = new Interfaces($this->db);
  1208. $result = $interface->run_triggers('BILL_CANCEL', $this, $user, $langs, $conf);
  1209. if ($result < 0) {
  1210. $error++;
  1211. $this->errors = $interface->errors;
  1212. }
  1213. // Fin appel triggers
  1214. return 1;
  1215. dol_syslog(get_class($this) . "::set_canceled rowid=" . $this->id, LOG_DEBUG);
  1216. $this->db->begin();
  1217. $sql = 'UPDATE ' . MAIN_DB_PREFIX . 'facture SET';
  1218. $sql.= ' fk_statut=3';
  1219. if ($close_code)
  1220. $sql.= ", close_code='" . $this->db->escape($close_code) . "'";
  1221. if ($close_note)
  1222. $sql.= ", close_note='" . $this->db->escape($close_note) . "'";
  1223. $sql.= ' WHERE rowid = ' . $this->id;
  1224. $resql = $this->db->query($sql);
  1225. if ($resql) {
  1226. // On desaffecte de la facture les remises liees
  1227. // car elles n'ont pas ete utilisees vu que la facture est abandonnee.
  1228. $sql = 'UPDATE ' . MAIN_DB_PREFIX . 'societe_remise_except';
  1229. $sql.= ' SET fk_facture = NULL';
  1230. $sql.= ' WHERE fk_facture = ' . $this->id;
  1231. $resql = $this->db->query($sql);
  1232. if ($resql) {
  1233. // Appel des triggers
  1234. include_once DOL_DOCUMENT_ROOT . '/core/class/interfaces.class.php';
  1235. $interface = new Interfaces($this->db);
  1236. $result = $interface->run_triggers('BILL_CANCEL', $this, $user, $langs

Large files files are truncated, but you can click here to view the full file