PageRenderTime 60ms CodeModel.GetById 14ms RepoModel.GetById 1ms app.codeStats 0ms

/htdocs/comm/propal/class/propal.class.php

https://github.com/zeert/dolibarr
PHP | 2847 lines | 2041 code | 327 blank | 479 comment | 290 complexity | 45466bd1f91db1ac48682924c957143c MD5 | raw file
Possible License(s): LGPL-2.0
  1. <?php
  2. /* Copyright (C) 2002-2004 Rodolphe Quiedeville <rodolphe@quiedeville.org>
  3. * Copyright (C) 2004 Eric Seigne <eric.seigne@ryxeo.com>
  4. * Copyright (C) 2004-2011 Laurent Destailleur <eldy@users.sourceforge.net>
  5. * Copyright (C) 2005 Marc Barilley <marc@ocebo.com>
  6. * Copyright (C) 2005-2012 Regis Houssin <regis@dolibarr.fr>
  7. * Copyright (C) 2006 Andre Cianfarani <acianfa@free.fr>
  8. * Copyright (C) 2008 Raphael Bertrand <raphael.bertrand@resultic.fr>
  9. * Copyright (C) 2010-2011 Juanjo Menent <jmenent@2byte.es>
  10. * Copyright (C) 2010-2011 Philippe Grand <philippe.grand@atoo-net.com>
  11. * Copyright (C) 2012 Christophe Battarel <christophe.battarel@altairis.fr>
  12. *
  13. * This program is free software; you can redistribute it and/or modify
  14. * it under the terms of the GNU General Public License as published by
  15. * the Free Software Foundation; either version 2 of the License, or
  16. * (at your option) any later version.
  17. *
  18. * This program is distributed in the hope that it will be useful,
  19. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  20. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  21. * GNU General Public License for more details.
  22. *
  23. * You should have received a copy of the GNU General Public License
  24. * along with this program. If not, see <http://www.gnu.org/licenses/>.
  25. */
  26. /**
  27. * \file htdocs/comm/propal/class/propal.class.php
  28. * \brief Fichier de la classe des propales
  29. */
  30. require_once(DOL_DOCUMENT_ROOT ."/core/class/commonobject.class.php");
  31. require_once(DOL_DOCUMENT_ROOT ."/product/class/product.class.php");
  32. require_once(DOL_DOCUMENT_ROOT ."/contact/class/contact.class.php");
  33. require_once(DOL_DOCUMENT_ROOT ."/margin/lib/margins.lib.php");
  34. /**
  35. * \class Propal
  36. * \brief Classe permettant la gestion des propales
  37. */
  38. class Propal extends CommonObject
  39. {
  40. public $element='propal';
  41. public $table_element='propal';
  42. public $table_element_line='propaldet';
  43. public $fk_element='fk_propal';
  44. protected $ismultientitymanaged = 1; // 0=No test on entity, 1=Test with field entity, 2=Test with link by societe
  45. var $id;
  46. var $socid; // Id client
  47. var $client; // Objet societe client (a charger par fetch_client)
  48. var $contactid;
  49. var $fk_project;
  50. var $author;
  51. var $ref;
  52. var $ref_client;
  53. var $statut; // 0 (draft), 1 (validated), 2 (signed), 3 (not signed), 4 (billed)
  54. var $datec; // Date of creation
  55. var $datev; // Date of validation
  56. var $date; // Date of proposal
  57. var $datep; // Same than date
  58. var $date_livraison;
  59. var $fin_validite;
  60. var $user_author_id;
  61. var $user_valid_id;
  62. var $user_close_id;
  63. var $total_ht; // Total net of tax
  64. var $total_tva; // Total VAT
  65. var $total_localtax1; // Total Local Taxes 1
  66. var $total_localtax2; // Total Local Taxes 2
  67. var $total_ttc; // Total with tax
  68. var $price; // deprecated (for compatibility)
  69. var $tva; // deprecated (for compatibility)
  70. var $total; // deprecated (for compatibility)
  71. var $cond_reglement_id;
  72. var $cond_reglement_code;
  73. var $mode_reglement_id;
  74. var $mode_reglement_code;
  75. var $remise;
  76. var $remise_percent;
  77. var $remise_absolue;
  78. var $note; // deprecated (for compatibility)
  79. var $note_private;
  80. var $note_public;
  81. var $fk_delivery_address; // deprecated (for compatibility)
  82. var $fk_address;
  83. var $address_type;
  84. var $adresse;
  85. var $availability_id;
  86. var $availability_code;
  87. var $demand_reason_id;
  88. var $demand_reason_code;
  89. var $products=array();
  90. var $extraparams=array();
  91. var $lines = array();
  92. var $line;
  93. var $origin;
  94. var $origin_id;
  95. var $labelstatut=array();
  96. var $labelstatut_short=array();
  97. // Pour board
  98. var $nbtodo;
  99. var $nbtodolate;
  100. var $specimen;
  101. /**
  102. * Constructor
  103. *
  104. * @param DoliDB $db Database handler
  105. * @param int $socid Id third party
  106. * @param int $propalid Id proposal
  107. */
  108. function __construct($db, $socid="", $propalid=0)
  109. {
  110. global $conf,$langs;
  111. $this->db = $db;
  112. $this->socid = $socid;
  113. $this->id = $propalid;
  114. $this->products = array();
  115. $this->remise = 0;
  116. $this->remise_percent = 0;
  117. $this->remise_absolue = 0;
  118. $this->duree_validite=$conf->global->PROPALE_VALIDITY_DURATION;
  119. $langs->load("propal");
  120. $this->labelstatut[0]=(! empty($conf->global->PROPAL_STATUS_DRAFT_LABEL) ? $conf->global->PROPAL_STATUS_DRAFT_LABEL : $langs->trans("PropalStatusDraft"));
  121. $this->labelstatut[1]=(! empty($conf->global->PROPAL_STATUS_VALIDATED_LABEL) ? $conf->global->PROPAL_STATUS_VALIDATED_LABEL : $langs->trans("PropalStatusValidated"));
  122. $this->labelstatut[2]=(! empty($conf->global->PROPAL_STATUS_SIGNED_LABEL) ? $conf->global->PROPAL_STATUS_SIGNED_LABEL : $langs->trans("PropalStatusSigned"));
  123. $this->labelstatut[3]=(! empty($conf->global->PROPAL_STATUS_NOTSIGNED_LABEL) ? $conf->global->PROPAL_STATUS_NOTSIGNED_LABEL : $langs->trans("PropalStatusNotSigned"));
  124. $this->labelstatut[4]=(! empty($conf->global->PROPAL_STATUS_BILLED_LABEL) ? $conf->global->PROPAL_STATUS_BILLED_LABEL : $langs->trans("PropalStatusBilled"));
  125. $this->labelstatut_short[0]=(! empty($conf->global->PROPAL_STATUS_DRAFTSHORT_LABEL) ? $conf->global->PROPAL_STATUS_DRAFTSHORT_LABEL : $langs->trans("PropalStatusDraftShort"));
  126. $this->labelstatut_short[1]=(! empty($conf->global->PROPAL_STATUS_VALIDATEDSHORT_LABEL) ? $conf->global->PROPAL_STATUS_VALIDATEDSHORT_LABEL : $langs->trans("Opened"));
  127. $this->labelstatut_short[2]=(! empty($conf->global->PROPAL_STATUS_SIGNEDSHORT_LABEL) ? $conf->global->PROPAL_STATUS_SIGNEDSHORT_LABEL : $langs->trans("PropalStatusSignedShort"));
  128. $this->labelstatut_short[3]=(! empty($conf->global->PROPAL_STATUS_NOTSIGNEDSHORT_LABEL) ? $conf->global->PROPAL_STATUS_NOTSIGNEDSHORT_LABEL : $langs->trans("PropalStatusNotSignedShort"));
  129. $this->labelstatut_short[4]=(! empty($conf->global->PROPAL_STATUS_BILLEDSHORT_LABEL) ? $conf->global->PROPAL_STATUS_BILLEDSHORT_LABEL : $langs->trans("PropalStatusBilledShort"));
  130. }
  131. /**
  132. * Add line into array products
  133. * $this->client doit etre charge
  134. *
  135. * @param int $idproduct Product Id to add
  136. * @param int $qty Quantity
  137. * @param int $remise_percent Discount effected on Product
  138. * @return int <0 if KO, >0 if OK
  139. *
  140. * TODO Remplacer les appels a cette fonction par generation objet Ligne
  141. * insere dans tableau $this->products
  142. */
  143. function add_product($idproduct, $qty, $remise_percent=0)
  144. {
  145. global $conf, $mysoc;
  146. if (! $qty) $qty = 1;
  147. dol_syslog("Propal::add_product $idproduct, $qty, $remise_percent");
  148. if ($idproduct > 0)
  149. {
  150. $prod=new Product($this->db);
  151. $prod->fetch($idproduct);
  152. $productdesc = $prod->description;
  153. $tva_tx = get_default_tva($mysoc,$this->client,$prod->id);
  154. // local taxes
  155. $localtax1_tx = get_default_localtax($mysoc,$this->client,1,$prod->tva_tx);
  156. $localtax2_tx = get_default_localtax($mysoc,$this->client,2,$prod->tva_tx);
  157. // multiprix
  158. if($conf->global->PRODUIT_MULTIPRICES && $this->client->price_level)
  159. {
  160. $price = $prod->multiprices[$this->client->price_level];
  161. }
  162. else
  163. {
  164. $price = $prod->price;
  165. }
  166. $line = new PropaleLigne($this->db);
  167. $line->fk_product=$idproduct;
  168. $line->desc=$productdesc;
  169. $line->qty=$qty;
  170. $line->subprice=$price;
  171. $line->remise_percent=$remise_percent;
  172. $line->tva_tx=$tva_tx;
  173. $this->products[]=$line;
  174. }
  175. }
  176. /**
  177. * Adding line of fixed discount in the proposal in DB
  178. *
  179. * @param int $idremise Id of fixed discount
  180. * @return int >0 if OK, <0 if KO
  181. */
  182. function insert_discount($idremise)
  183. {
  184. global $langs;
  185. include_once(DOL_DOCUMENT_ROOT.'/core/lib/price.lib.php');
  186. include_once(DOL_DOCUMENT_ROOT.'/core/class/discount.class.php');
  187. $this->db->begin();
  188. $remise=new DiscountAbsolute($this->db);
  189. $result=$remise->fetch($idremise);
  190. if ($result > 0)
  191. {
  192. if ($remise->fk_facture) // Protection against multiple submission
  193. {
  194. $this->error=$langs->trans("ErrorDiscountAlreadyUsed");
  195. $this->db->rollback();
  196. return -5;
  197. }
  198. $propalligne=new PropaleLigne($this->db);
  199. $propalligne->fk_propal=$this->id;
  200. $propalligne->fk_remise_except=$remise->id;
  201. $propalligne->desc=$remise->description; // Description ligne
  202. $propalligne->tva_tx=$remise->tva_tx;
  203. $propalligne->subprice=-$remise->amount_ht;
  204. $propalligne->fk_product=0; // Id produit predefini
  205. $propalligne->qty=1;
  206. $propalligne->remise=0;
  207. $propalligne->remise_percent=0;
  208. $propalligne->rang=-1;
  209. $propalligne->info_bits=2;
  210. // TODO deprecated
  211. $propalligne->price=-$remise->amount_ht;
  212. $propalligne->total_ht = -$remise->amount_ht;
  213. $propalligne->total_tva = -$remise->amount_tva;
  214. $propalligne->total_ttc = -$remise->amount_ttc;
  215. $result=$propalligne->insert();
  216. if ($result > 0)
  217. {
  218. $result=$this->update_price(1);
  219. if ($result > 0)
  220. {
  221. $this->db->commit();
  222. return 1;
  223. }
  224. else
  225. {
  226. $this->db->rollback();
  227. return -1;
  228. }
  229. }
  230. else
  231. {
  232. $this->error=$propalligne->error;
  233. $this->db->rollback();
  234. return -2;
  235. }
  236. }
  237. else
  238. {
  239. $this->db->rollback();
  240. return -2;
  241. }
  242. }
  243. /**
  244. * Add a proposal line into database (linked to product/service or not)
  245. * Les parametres sont deja cense etre juste et avec valeurs finales a l'appel
  246. * de cette methode. Aussi, pour le taux tva, il doit deja avoir ete defini
  247. * par l'appelant par la methode get_default_tva(societe_vendeuse,societe_acheteuse,'',produit)
  248. * et le desc doit deja avoir la bonne valeur (a l'appelant de gerer le multilangue)
  249. *
  250. * @param int $propalid Id de la propale
  251. * @param string $desc Description de la ligne
  252. * @param double $pu_ht Prix unitaire
  253. * @param double $qty Quantite
  254. * @param double $txtva Taux de tva
  255. * @param double $txlocaltax1 Local tax 1 rate
  256. * @param double $txlocaltax2 Local tax 2 rate
  257. * @param int $fk_product Id du produit/service predefini
  258. * @param double $remise_percent Pourcentage de remise de la ligne
  259. * @param string $price_base_type HT or TTC
  260. * @param dobule $pu_ttc Prix unitaire TTC
  261. * @param int $info_bits Bits de type de lignes
  262. * @param int $type Type of line (product, service)
  263. * @param int $rang Position of line
  264. * @param int $special_code Special code
  265. * @param int $fk_parent_line Id of parent line
  266. * @param int $fk_fournprice Id supplier price
  267. * @param int $pa_ht Buying price without tax
  268. * @return int >0 if OK, <0 if KO
  269. *
  270. * @see add_product
  271. */
  272. function addline($propalid, $desc, $pu_ht, $qty, $txtva, $txlocaltax1=0, $txlocaltax2=0, $fk_product=0, $remise_percent=0, $price_base_type='HT', $pu_ttc=0, $info_bits=0, $type=0, $rang=-1, $special_code=0, $fk_parent_line=0, $fk_fournprice=null, $pa_ht = 0)
  273. {
  274. global $conf;
  275. dol_syslog("Propal::Addline propalid=$propalid, desc=$desc, pu_ht=$pu_ht, qty=$qty, txtva=$txtva, fk_product=$fk_product, remise_except=$remise_percent, price_base_type=$price_base_type, pu_ttc=$pu_ttc, info_bits=$info_bits, type=$type");
  276. include_once(DOL_DOCUMENT_ROOT.'/core/lib/price.lib.php');
  277. // Clean parameters
  278. if (empty($remise_percent)) $remise_percent=0;
  279. if (empty($qty)) $qty=0;
  280. if (empty($info_bits)) $info_bits=0;
  281. if (empty($rang)) $rang=0;
  282. if (empty($fk_parent_line) || $fk_parent_line < 0) $fk_parent_line=0;
  283. $remise_percent=price2num($remise_percent);
  284. $qty=price2num($qty);
  285. $pu_ht=price2num($pu_ht);
  286. $pu_ttc=price2num($pu_ttc);
  287. $txtva=price2num($txtva);
  288. $txlocaltax1=price2num($txlocaltax1);
  289. $txlocaltax2=price2num($txlocaltax2);
  290. $pa_ht=price2num($pa_ht);
  291. if ($price_base_type=='HT')
  292. {
  293. $pu=$pu_ht;
  294. }
  295. else
  296. {
  297. $pu=$pu_ttc;
  298. }
  299. // Check parameters
  300. if ($type < 0) return -1;
  301. if ($this->statut == 0)
  302. {
  303. $this->db->begin();
  304. // Calcul du total TTC et de la TVA pour la ligne a partir de
  305. // qty, pu, remise_percent et txtva
  306. // TRES IMPORTANT: C'est au moment de l'insertion ligne qu'on doit stocker
  307. // la part ht, tva et ttc, et ce au niveau de la ligne qui a son propre taux tva.
  308. $tabprice=calcul_price_total($qty, $pu, $remise_percent, $txtva, $txlocaltax1, $txlocaltax2, 0, $price_base_type, $info_bits);
  309. $total_ht = $tabprice[0];
  310. $total_tva = $tabprice[1];
  311. $total_ttc = $tabprice[2];
  312. $total_localtax1 = $tabprice[9];
  313. $total_localtax2 = $tabprice[10];
  314. // Rang to use
  315. $rangtouse = $rang;
  316. if ($rangtouse == -1)
  317. {
  318. $rangmax = $this->line_max($fk_parent_line);
  319. $rangtouse = $rangmax + 1;
  320. }
  321. // TODO A virer
  322. // Anciens indicateurs: $price, $remise (a ne plus utiliser)
  323. $price = $pu;
  324. $remise = 0;
  325. if ($remise_percent > 0)
  326. {
  327. $remise = round(($pu * $remise_percent / 100), 2);
  328. $price = $pu - $remise;
  329. }
  330. // Insert line
  331. $this->line=new PropaleLigne($this->db);
  332. $this->line->fk_propal=$propalid;
  333. $this->line->desc=$desc;
  334. $this->line->qty=$qty;
  335. $this->line->tva_tx=$txtva;
  336. $this->line->localtax1_tx=$txlocaltax1;
  337. $this->line->localtax2_tx=$txlocaltax2;
  338. $this->line->fk_product=$fk_product;
  339. $this->line->remise_percent=$remise_percent;
  340. $this->line->subprice=$pu_ht;
  341. $this->line->rang=$rangtouse;
  342. $this->line->info_bits=$info_bits;
  343. $this->line->total_ht=$total_ht;
  344. $this->line->total_tva=$total_tva;
  345. $this->line->total_localtax1=$total_localtax1;
  346. $this->line->total_localtax2=$total_localtax2;
  347. $this->line->total_ttc=$total_ttc;
  348. $this->line->product_type=$type;
  349. $this->line->special_code=$special_code;
  350. $this->line->fk_parent_line=$fk_parent_line;
  351. // infos marge
  352. $this->line->fk_fournprice = $fk_fournprice;
  353. $this->line->pa_ht = $pa_ht;
  354. // Mise en option de la ligne
  355. //if ($conf->global->PROPALE_USE_OPTION_LINE && !$qty) $ligne->special_code=3;
  356. if (empty($qty) && empty($special_code)) $this->line->special_code=3;
  357. // TODO deprecated
  358. $this->line->price=$price;
  359. $this->line->remise=$remise;
  360. $result=$this->line->insert();
  361. if ($result > 0)
  362. {
  363. // Reorder if child line
  364. if (! empty($fk_parent_line)) $this->line_order(true,'DESC');
  365. // Mise a jour informations denormalisees au niveau de la propale meme
  366. $result=$this->update_price(1);
  367. if ($result > 0)
  368. {
  369. $this->db->commit();
  370. return $this->line->rowid;
  371. }
  372. else
  373. {
  374. $this->error=$this->db->error();
  375. dol_syslog("Error sql=$sql, error=".$this->error,LOG_ERR);
  376. $this->db->rollback();
  377. return -1;
  378. }
  379. }
  380. else
  381. {
  382. $this->error=$this->line->error;
  383. $this->db->rollback();
  384. return -2;
  385. }
  386. }
  387. }
  388. /**
  389. * Update a proposal line
  390. *
  391. * @param int $rowid Id de la ligne
  392. * @param double $pu Prix unitaire (HT ou TTC selon price_base_type)
  393. * @param double $qty Quantity
  394. * @param double $remise_percent Remise effectuee sur le produit
  395. * @param double $txtva Taux de TVA
  396. * @param double $txlocaltax1 Local tax 1 rate
  397. * @param double $txlocaltax2 Local tax 2 rate
  398. * @param string $desc Description
  399. * @param double $price_base_type HT ou TTC
  400. * @param int $info_bits Miscellanous informations
  401. * @param int $special_code Set special code ('' = we don't change it)
  402. * @param int $fk_parent_line Id of line parent
  403. * @param int $skip_update_total Skip update total
  404. * @param int $fk_fournprice Id supplier price
  405. * @param int $pa_ht Buying price without tax
  406. * @return int 0 if OK, <0 if KO
  407. */
  408. function updateline($rowid, $pu, $qty, $remise_percent, $txtva, $txlocaltax1=0, $txlocaltax2=0, $desc='', $price_base_type='HT', $info_bits=0, $special_code=0, $fk_parent_line=0, $skip_update_total=0, $fk_fournprice=null, $pa_ht=0)
  409. {
  410. global $conf,$user,$langs;
  411. dol_syslog(get_class($this)."::updateLine $rowid, $pu, $qty, $remise_percent, $txtva, $desc, $price_base_type, $info_bits");
  412. include_once(DOL_DOCUMENT_ROOT.'/core/lib/price.lib.php');
  413. // Clean parameters
  414. $remise_percent=price2num($remise_percent);
  415. $qty=price2num($qty);
  416. $pu = price2num($pu);
  417. $txtva = price2num($txtva);
  418. $txlocaltax1=price2num($txlocaltax1);
  419. $txlocaltax2=price2num($txlocaltax2);
  420. $pa_ht=price2num($pa_ht);
  421. if (empty($qty) && empty($special_code)) $special_code=3; // Set option tag
  422. if (! empty($qty) && $special_code == 3) $special_code=0; // Remove option tag
  423. if ($this->statut == 0)
  424. {
  425. $this->db->begin();
  426. // Calcul du total TTC et de la TVA pour la ligne a partir de
  427. // qty, pu, remise_percent et txtva
  428. // TRES IMPORTANT: C'est au moment de l'insertion ligne qu'on doit stocker
  429. // la part ht, tva et ttc, et ce au niveau de la ligne qui a son propre taux tva.
  430. $tabprice=calcul_price_total($qty, $pu, $remise_percent, $txtva, $txlocaltax1, $txlocaltax2, 0, $price_base_type, $info_bits);
  431. $total_ht = $tabprice[0];
  432. $total_tva = $tabprice[1];
  433. $total_ttc = $tabprice[2];
  434. $total_localtax1 = $tabprice[9];
  435. $total_localtax2 = $tabprice[10];
  436. // Anciens indicateurs: $price, $remise (a ne plus utiliser)
  437. $price = $pu;
  438. if ($remise_percent > 0)
  439. {
  440. $remise = round(($pu * $remise_percent / 100), 2);
  441. $price = $pu - $remise;
  442. }
  443. // Update line
  444. $this->line=new PropaleLigne($this->db);
  445. // Stock previous line records
  446. $staticline=new PropaleLigne($this->db);
  447. $staticline->fetch($rowid);
  448. $this->line->oldline = $staticline;
  449. // Reorder if fk_parent_line change
  450. if (! empty($fk_parent_line) && ! empty($staticline->fk_parent_line) && $fk_parent_line != $staticline->fk_parent_line)
  451. {
  452. $rangmax = $this->line_max($fk_parent_line);
  453. $this->line->rang = $rangmax + 1;
  454. }
  455. $this->line->rowid = $rowid;
  456. $this->line->desc = $desc;
  457. $this->line->qty = $qty;
  458. $this->line->tva_tx = $txtva;
  459. $this->line->localtax1_tx = $txlocaltax1;
  460. $this->line->localtax2_tx = $txlocaltax2;
  461. $this->line->remise_percent = $remise_percent;
  462. $this->line->subprice = $pu;
  463. $this->line->info_bits = $info_bits;
  464. $this->line->total_ht = $total_ht;
  465. $this->line->total_tva = $total_tva;
  466. $this->line->total_localtax1 = $total_localtax1;
  467. $this->line->total_localtax2 = $total_localtax2;
  468. $this->line->total_ttc = $total_ttc;
  469. $this->line->special_code = $special_code;
  470. $this->line->fk_parent_line = $fk_parent_line;
  471. $this->line->skip_update_total = $skip_update_total;
  472. // infos marge
  473. $this->line->fk_fournprice = $fk_fournprice;
  474. $this->line->pa_ht = $pa_ht;
  475. // TODO deprecated
  476. $this->line->price=$price;
  477. $this->line->remise=$remise;
  478. $result=$this->line->update();
  479. if ($result > 0)
  480. {
  481. // Reorder if child line
  482. if (! empty($fk_parent_line)) $this->line_order(true,'DESC');
  483. $this->update_price(1);
  484. $this->fk_propal = $this->id;
  485. $this->rowid = $rowid;
  486. $this->db->commit();
  487. return $result;
  488. }
  489. else
  490. {
  491. $this->error=$this->db->error();
  492. $this->db->rollback();
  493. dol_syslog("Propal::UpdateLine Error=".$this->error, LOG_ERR);
  494. return -1;
  495. }
  496. }
  497. else
  498. {
  499. dol_syslog("Propal::UpdateLigne Erreur -2 Propal en mode incompatible pour cette action");
  500. return -2;
  501. }
  502. }
  503. /**
  504. * Delete detail line
  505. *
  506. * @param int $lineid Id of line to delete
  507. * @return int >0 if OK, <0 if KO
  508. */
  509. function deleteline($lineid)
  510. {
  511. if ($this->statut == 0)
  512. {
  513. $line=new PropaleLigne($this->db);
  514. // For triggers
  515. $line->fetch($lineid);
  516. if ($line->delete() > 0)
  517. {
  518. $this->update_price(1);
  519. return 1;
  520. }
  521. else
  522. {
  523. return -1;
  524. }
  525. }
  526. else
  527. {
  528. return -2;
  529. }
  530. }
  531. /**
  532. * Create commercial proposal into database
  533. * this->ref can be set or empty. If empty, we will use "(PROVid)"
  534. *
  535. * @param User $user User that create
  536. * @param int $notrigger 1=Does not execute triggers, 0= execuete triggers
  537. * @return int <0 if KO, >=0 if OK
  538. */
  539. function create($user='', $notrigger=0)
  540. {
  541. global $langs,$conf,$mysoc;
  542. $error=0;
  543. $now=dol_now();
  544. // Clean parameters
  545. if (empty($this->date)) $this->date=$this->datep;
  546. $this->fin_validite = $this->date + ($this->duree_validite * 24 * 3600);
  547. if (empty($this->availability_id)) $this->availability_id=0;
  548. if (empty($this->demand_reason_id)) $this->demand_reason_id=0;
  549. dol_syslog(get_class($this)."::create");
  550. // Check parameters
  551. $soc = new Societe($this->db);
  552. $result=$soc->fetch($this->socid);
  553. if ($result < 0)
  554. {
  555. $this->error="Failed to fetch company";
  556. dol_syslog(get_class($this)."::create ".$this->error, LOG_ERR);
  557. return -3;
  558. }
  559. if (empty($this->date))
  560. {
  561. $this->error="Date of proposal is required";
  562. dol_syslog(get_class($this)."::create ".$this->error, LOG_ERR);
  563. return -4;
  564. }
  565. if (! empty($this->ref))
  566. {
  567. $result=$this->verifyNumRef(); // Check ref is not yet used
  568. }
  569. $this->db->begin();
  570. $this->fetch_thirdparty();
  571. // Insert into database
  572. $sql = "INSERT INTO ".MAIN_DB_PREFIX."propal (";
  573. $sql.= "fk_soc";
  574. $sql.= ", price";
  575. $sql.= ", remise";
  576. $sql.= ", remise_percent";
  577. $sql.= ", remise_absolue";
  578. $sql.= ", tva";
  579. $sql.= ", total";
  580. $sql.= ", datep";
  581. $sql.= ", datec";
  582. $sql.= ", ref";
  583. $sql.= ", fk_user_author";
  584. $sql.= ", note";
  585. $sql.= ", note_public";
  586. $sql.= ", model_pdf";
  587. $sql.= ", fin_validite";
  588. $sql.= ", fk_cond_reglement";
  589. $sql.= ", fk_mode_reglement";
  590. $sql.= ", ref_client";
  591. $sql.= ", date_livraison";
  592. $sql.= ", fk_availability";
  593. $sql.= ", fk_input_reason";
  594. $sql.= ", fk_projet";
  595. $sql.= ", entity";
  596. $sql.= ") ";
  597. $sql.= " VALUES (";
  598. $sql.= $this->socid;
  599. $sql.= ", 0";
  600. $sql.= ", ".$this->remise;
  601. $sql.= ", ".($this->remise_percent?$this->remise_percent:'null');
  602. $sql.= ", ".($this->remise_absolue?$this->remise_absolue:'null');
  603. $sql.= ", 0";
  604. $sql.= ", 0";
  605. $sql.= ", '".$this->db->idate($this->date)."'";
  606. $sql.= ", '".$this->db->idate($now)."'";
  607. $sql.= ", '(PROV)'";
  608. $sql.= ", ".($user->id > 0 ? "'".$user->id."'":"null");
  609. $sql.= ", '".$this->db->escape($this->note)."'";
  610. $sql.= ", '".$this->db->escape($this->note_public)."'";
  611. $sql.= ", '".$this->modelpdf."'";
  612. $sql.= ", ".($this->fin_validite!=''?"'".$this->db->idate($this->fin_validite)."'":"null");
  613. $sql.= ", ".$this->cond_reglement_id;
  614. $sql.= ", ".$this->mode_reglement_id;
  615. $sql.= ", '".$this->db->escape($this->ref_client)."'";
  616. $sql.= ", ".($this->date_livraison!=''?"'".$this->db->idate($this->date_livraison)."'":"null");
  617. $sql.= ", ".$this->availability_id;
  618. $sql.= ", ".$this->demand_reason_id;
  619. $sql.= ", ".($this->fk_project?$this->fk_project:"null");
  620. $sql.= ", ".$conf->entity;
  621. $sql.= ")";
  622. dol_syslog(get_class($this)."::create sql=".$sql, LOG_DEBUG);
  623. $resql=$this->db->query($sql);
  624. if ($resql)
  625. {
  626. $this->id = $this->db->last_insert_id(MAIN_DB_PREFIX."propal");
  627. if ($this->id)
  628. {
  629. if (empty($this->ref)) $this->ref='(PROV'.$this->id.')';
  630. $sql = 'UPDATE '.MAIN_DB_PREFIX."propal SET ref='".$this->ref."' WHERE rowid=".$this->id;
  631. dol_syslog(get_class($this)."::create sql=".$sql);
  632. $resql=$this->db->query($sql);
  633. if (! $resql) $error++;
  634. /*
  635. * Insertion du detail des produits dans la base
  636. */
  637. if (! $error)
  638. {
  639. $fk_parent_line=0;
  640. $num=count($this->lines);
  641. for ($i=0;$i<$num;$i++)
  642. {
  643. // Reset fk_parent_line for no child products and special product
  644. if (($this->lines[$i]->product_type != 9 && empty($this->lines[$i]->fk_parent_line)) || $this->lines[$i]->product_type == 9) {
  645. $fk_parent_line = 0;
  646. }
  647. $result = $this->addline(
  648. $this->id,
  649. $this->lines[$i]->desc,
  650. $this->lines[$i]->subprice,
  651. $this->lines[$i]->qty,
  652. $this->lines[$i]->tva_tx,
  653. $this->lines[$i]->localtax1_tx,
  654. $this->lines[$i]->localtax2_tx,
  655. $this->lines[$i]->fk_product,
  656. $this->lines[$i]->remise_percent,
  657. 'HT',
  658. 0,
  659. 0,
  660. $this->lines[$i]->product_type,
  661. $this->lines[$i]->rang,
  662. $this->lines[$i]->special_code,
  663. $fk_parent_line,
  664. $this->lines[$i]->fk_fournprice,
  665. $this->lines[$i]->pa_ht
  666. );
  667. if ($result < 0)
  668. {
  669. $error++;
  670. $this->error=$this->db->error;
  671. dol_print_error($this->db);
  672. break;
  673. }
  674. // Defined the new fk_parent_line
  675. if ($result > 0 && $this->lines[$i]->product_type == 9) {
  676. $fk_parent_line = $result;
  677. }
  678. }
  679. }
  680. // Add linked object
  681. if (! $error && $this->origin && $this->origin_id)
  682. {
  683. $ret = $this->add_object_linked();
  684. if (! $ret) dol_print_error($this->db);
  685. }
  686. // Set delivery address
  687. if (! $error && $this->fk_delivery_address)
  688. {
  689. $sql = "UPDATE ".MAIN_DB_PREFIX."propal";
  690. $sql.= " SET fk_adresse_livraison = ".$this->fk_delivery_address;
  691. $sql.= " WHERE ref = '".$this->ref."'";
  692. $sql.= " AND entity = ".$conf->entity;
  693. $result=$this->db->query($sql);
  694. }
  695. if (! $error)
  696. {
  697. // Mise a jour infos denormalisees
  698. $resql=$this->update_price(1);
  699. if ($resql)
  700. {
  701. if (! $notrigger)
  702. {
  703. // Appel des triggers
  704. include_once(DOL_DOCUMENT_ROOT . "/core/class/interfaces.class.php");
  705. $interface=new Interfaces($this->db);
  706. $result=$interface->run_triggers('PROPAL_CREATE',$this,$user,$langs,$conf);
  707. if ($result < 0) {
  708. $error++; $this->errors=$interface->errors;
  709. }
  710. // Fin appel triggers
  711. }
  712. }
  713. else
  714. {
  715. $error++;
  716. }
  717. }
  718. }
  719. else
  720. {
  721. $error++;
  722. }
  723. if (! $error)
  724. {
  725. $this->db->commit();
  726. dol_syslog(get_class($this)."::create done id=".$this->id);
  727. return $this->id;
  728. }
  729. else
  730. {
  731. $this->error=$this->db->error();
  732. dol_syslog(get_class($this)."::create -2 ".$this->error, LOG_ERR);
  733. $this->db->rollback();
  734. return -2;
  735. }
  736. }
  737. else
  738. {
  739. $this->error=$this->db->error();
  740. dol_syslog(get_class($this)."::create -1 ".$this->error, LOG_ERR);
  741. $this->db->rollback();
  742. return -1;
  743. }
  744. }
  745. /**
  746. * Insert into DB a proposal object completely defined by its data members (ex, results from copy).
  747. *
  748. * @param User $user User that create
  749. * @return int Id of the new object if ok, <0 if ko
  750. * @see create
  751. */
  752. function create_from($user)
  753. {
  754. $this->products=$this->lines;
  755. return $this->create();
  756. }
  757. /**
  758. * Load an object from its id and create a new one in database
  759. *
  760. * @param int $socid Id of thirdparty
  761. * @param HookManager $hookmanager Hook manager instance
  762. * @return int New id of clone
  763. */
  764. function createFromClone($socid=0,$hookmanager=false)
  765. {
  766. global $user,$langs,$conf;
  767. $error=0;
  768. $now=dol_now();
  769. $this->db->begin();
  770. // Load source object
  771. $objFrom = dol_clone($this);
  772. $objsoc=new Societe($this->db);
  773. // Change socid if needed
  774. if (! empty($socid) && $socid != $this->socid)
  775. {
  776. if ($objsoc->fetch($socid) > 0)
  777. {
  778. $this->socid = $objsoc->id;
  779. $this->cond_reglement_id = (! empty($objsoc->cond_reglement_id) ? $objsoc->cond_reglement_id : 0);
  780. $this->mode_reglement_id = (! empty($objsoc->mode_reglement_id) ? $objsoc->mode_reglement_id : 0);
  781. $this->fk_project = '';
  782. $this->fk_delivery_address = '';
  783. }
  784. // TODO Change product price if multi-prices
  785. }
  786. else
  787. {
  788. $objsoc->fetch($this->socid);
  789. }
  790. $this->id=0;
  791. $this->statut=0;
  792. if (empty($conf->global->PROPALE_ADDON) || ! is_readable(DOL_DOCUMENT_ROOT ."/core/modules/propale/".$conf->global->PROPALE_ADDON.".php"))
  793. {
  794. $this->error='ErrorSetupNotComplete';
  795. return -1;
  796. }
  797. // Clear fields
  798. $this->user_author = $user->id;
  799. $this->user_valid = '';
  800. $this->date = $now;
  801. $this->datep = $now; // deprecated
  802. $this->fin_validite = $this->date + ($this->duree_validite * 24 * 3600);
  803. $this->ref_client = '';
  804. // Set ref
  805. require_once(DOL_DOCUMENT_ROOT ."/core/modules/propale/".$conf->global->PROPALE_ADDON.".php");
  806. $obj = $conf->global->PROPALE_ADDON;
  807. $modPropale = new $obj;
  808. $this->ref = $modPropale->getNextValue($objsoc,$this);
  809. // Create clone
  810. $result=$this->create($user);
  811. if ($result < 0) $error++;
  812. if (! $error)
  813. {
  814. // Hook of thirdparty module
  815. if (is_object($hookmanager))
  816. {
  817. $parameters=array('objFrom'=>$objFrom);
  818. $action='';
  819. $reshook=$hookmanager->executeHooks('createFrom',$parameters,$this,$action); // Note that $action and $object may have been modified by some hooks
  820. if ($reshook < 0) $error++;
  821. }
  822. // Appel des triggers
  823. include_once(DOL_DOCUMENT_ROOT . "/core/class/interfaces.class.php");
  824. $interface=new Interfaces($this->db);
  825. $result=$interface->run_triggers('PROPAL_CLONE',$this,$user,$langs,$conf);
  826. if ($result < 0) {
  827. $error++; $this->errors=$interface->errors;
  828. }
  829. // Fin appel triggers
  830. }
  831. // End
  832. if (! $error)
  833. {
  834. $this->db->commit();
  835. return $this->id;
  836. }
  837. else
  838. {
  839. $this->db->rollback();
  840. return -1;
  841. }
  842. }
  843. /**
  844. * Load a proposal from database and its ligne array
  845. *
  846. * @param int $rowid id of object to load
  847. * @param string $ref Ref of proposal
  848. * @return int >0 if OK, <0 if KO
  849. */
  850. function fetch($rowid,$ref='')
  851. {
  852. global $conf;
  853. $sql = "SELECT p.rowid, p.ref, p.remise, p.remise_percent, p.remise_absolue, p.fk_soc";
  854. $sql.= ", p.total, p.tva, p.localtax1, p.localtax2, p.total_ht";
  855. $sql.= ", p.datec";
  856. $sql.= ", p.date_valid as datev";
  857. $sql.= ", p.datep as dp";
  858. $sql.= ", p.fin_validite as dfv";
  859. $sql.= ", p.date_livraison as date_livraison";
  860. $sql.= ", p.model_pdf, p.ref_client, p.extraparams";
  861. $sql.= ", p.note as note_private, p.note_public";
  862. $sql.= ", p.fk_projet, p.fk_statut";
  863. $sql.= ", p.fk_user_author, p.fk_user_valid, p.fk_user_cloture";
  864. $sql.= ", p.fk_adresse_livraison";
  865. $sql.= ", p.fk_availability";
  866. $sql.= ", p.fk_input_reason";
  867. $sql.= ", p.fk_cond_reglement";
  868. $sql.= ", p.fk_mode_reglement";
  869. $sql.= ", c.label as statut_label";
  870. $sql.= ", ca.code as availability_code, ca.label as availability";
  871. $sql.= ", dr.code as demand_reason_code, dr.label as demand_reason";
  872. $sql.= ", cr.code as cond_reglement_code, cr.libelle as cond_reglement, cr.libelle_facture as cond_reglement_libelle_doc";
  873. $sql.= ", cp.code as mode_reglement_code, cp.libelle as mode_reglement";
  874. $sql.= " FROM ".MAIN_DB_PREFIX."c_propalst as c, ".MAIN_DB_PREFIX."propal as p";
  875. $sql.= ' LEFT JOIN '.MAIN_DB_PREFIX.'c_paiement as cp ON p.fk_mode_reglement = cp.id';
  876. $sql.= ' LEFT JOIN '.MAIN_DB_PREFIX.'c_payment_term as cr ON p.fk_cond_reglement = cr.rowid';
  877. $sql.= ' LEFT JOIN '.MAIN_DB_PREFIX.'c_availability as ca ON p.fk_availability = ca.rowid';
  878. $sql.= ' LEFT JOIN '.MAIN_DB_PREFIX.'c_input_reason as dr ON p.fk_input_reason = dr.rowid';
  879. $sql.= " WHERE p.fk_statut = c.id";
  880. $sql.= " AND p.entity = ".$conf->entity;
  881. if ($ref) $sql.= " AND p.ref='".$ref."'";
  882. else $sql.= " AND p.rowid=".$rowid;
  883. dol_syslog(get_class($this)."::fetch sql=".$sql, LOG_DEBUG);
  884. $resql=$this->db->query($sql);
  885. if ($resql)
  886. {
  887. if ($this->db->num_rows($resql))
  888. {
  889. $obj = $this->db->fetch_object($resql);
  890. $this->id = $obj->rowid;
  891. $this->ref = $obj->ref;
  892. $this->ref_client = $obj->ref_client;
  893. $this->remise = $obj->remise;
  894. $this->remise_percent = $obj->remise_percent;
  895. $this->remise_absolue = $obj->remise_absolue;
  896. $this->total = $obj->total; // TODO obsolete
  897. $this->total_ht = $obj->total_ht;
  898. $this->total_tva = $obj->tva;
  899. $this->total_localtax1 = $obj->localtax1;
  900. $this->total_localtax2 = $obj->localtax2;
  901. $this->total_ttc = $obj->total;
  902. $this->socid = $obj->fk_soc;
  903. $this->fk_project = $obj->fk_projet;
  904. $this->modelpdf = $obj->model_pdf;
  905. $this->note = $obj->note_private; // TODO obsolete
  906. $this->note_private = $obj->note_private;
  907. $this->note_public = $obj->note_public;
  908. $this->statut = $obj->fk_statut;
  909. $this->statut_libelle = $obj->statut_label;
  910. $this->datec = $this->db->jdate($obj->datec); // TODO obsolete
  911. $this->datev = $this->db->jdate($obj->datev); // TODO obsolete
  912. $this->date_creation = $this->db->jdate($obj->datec); //Creation date
  913. $this->date_validation = $this->db->jdate($obj->datev); //Validation date
  914. $this->date = $this->db->jdate($obj->dp); // Proposal date
  915. $this->datep = $this->db->jdate($obj->dp); // deprecated
  916. $this->fin_validite = $this->db->jdate($obj->dfv);
  917. $this->date_livraison = $this->db->jdate($obj->date_livraison);
  918. $this->availability_id = $obj->fk_availability;
  919. $this->availability_code = $obj->availability_code;
  920. $this->availability = $obj->availability;
  921. $this->demand_reason_id = $obj->fk_input_reason;
  922. $this->demand_reason_code = $obj->demand_reason_code;
  923. $this->demand_reason = $obj->demand_reason;
  924. $this->fk_delivery_address = $obj->fk_adresse_livraison; // TODO obsolete
  925. $this->fk_address = $obj->fk_adresse_livraison;
  926. $this->mode_reglement_id = $obj->fk_mode_reglement;
  927. $this->mode_reglement_code = $obj->mode_reglement_code;
  928. $this->mode_reglement = $obj->mode_reglement;
  929. $this->cond_reglement_id = $obj->fk_cond_reglement;
  930. $this->cond_reglement_code = $obj->cond_reglement_code;
  931. $this->cond_reglement = $obj->cond_reglement;
  932. $this->cond_reglement_doc = $obj->cond_reglement_libelle_doc;
  933. $this->extraparams = (array) json_decode($obj->extraparams, true);
  934. $this->user_author_id = $obj->fk_user_author;
  935. $this->user_valid_id = $obj->fk_user_valid;
  936. $this->user_close_id = $obj->fk_user_cloture;
  937. if ($obj->fk_statut == 0)
  938. {
  939. $this->brouillon = 1;
  940. }
  941. $this->db->free($resql);
  942. $this->lines = array();
  943. /*
  944. * Lignes propales liees a un produit ou non
  945. */
  946. $sql = "SELECT d.rowid, d.fk_propal, d.fk_parent_line, d.description, d.price, d.tva_tx, d.localtax1_tx, d.localtax2_tx, d.qty, d.fk_remise_except, d.remise_percent, d.subprice, d.fk_product,";
  947. $sql.= " d.info_bits, d.total_ht, d.total_tva, d.total_localtax1, d.total_localtax2, d.total_ttc, d.fk_product_fournisseur_price as fk_fournprice, d.buy_price_ht as pa_ht, d.special_code, d.rang, d.product_type,";
  948. $sql.= ' p.ref as product_ref, p.description as product_desc, p.fk_product_type, p.label as product_label';
  949. $sql.= " FROM ".MAIN_DB_PREFIX."propaldet as d";
  950. $sql.= " LEFT JOIN ".MAIN_DB_PREFIX."product as p ON d.fk_product = p.rowid";
  951. $sql.= " WHERE d.fk_propal = ".$this->id;
  952. $sql.= " ORDER by d.rang";
  953. $result = $this->db->query($sql);
  954. if ($result)
  955. {
  956. $num = $this->db->num_rows($result);
  957. $i = 0;
  958. while ($i < $num)
  959. {
  960. $objp = $this->db->fetch_object($result);
  961. $line = new PropaleLigne($this->db);
  962. $line->rowid = $objp->rowid;
  963. $line->fk_propal = $objp->fk_propal;
  964. $line->fk_parent_line = $objp->fk_parent_line;
  965. $line->product_type = $objp->product_type;
  966. $line->desc = $objp->description; // Description ligne
  967. $line->qty = $objp->qty;
  968. $line->tva_tx = $objp->tva_tx;
  969. $line->localtax1_tx = $objp->localtax1_tx;
  970. $line->localtax2_tx = $objp->localtax2_tx;
  971. $line->subprice = $objp->subprice;
  972. $line->fk_remise_except = $objp->fk_remise_except;
  973. $line->remise_percent = $objp->remise_percent;
  974. $line->price = $objp->price; // TODO deprecated
  975. $line->info_bits = $objp->info_bits;
  976. $line->total_ht = $objp->total_ht;
  977. $line->total_tva = $objp->total_tva;
  978. $line->total_localtax1 = $objp->total_localtax1;
  979. $line->total_localtax2 = $objp->total_localtax2;
  980. $line->total_ttc = $objp->total_ttc;
  981. $line->fk_fournprice = $objp->fk_fournprice;
  982. $marginInfos = getMarginInfos($objp->subprice, $objp->remise_percent, $objp->tva_tx, $objp->localtax1_tx, $objp->localtax2_tx, $line->fk_fournprice, $objp->pa_ht);
  983. $line->pa_ht = $marginInfos[0];
  984. $line->marge_tx = $marginInfos[1];
  985. $line->marque_tx = $marginInfos[2];
  986. $line->special_code = $objp->special_code;
  987. $line->rang = $objp->rang;
  988. $line->fk_product = $objp->fk_product;
  989. $line->ref = $objp->product_ref; // TODO deprecated
  990. $line->product_ref = $objp->product_ref;
  991. $line->libelle = $objp->product_label; // TODO deprecated
  992. $line->label = $objp->product_label; // TODO deprecated
  993. $line->product_label = $objp->product_label;
  994. $line->product_desc = $objp->product_desc; // Description produit
  995. $line->fk_product_type = $objp->fk_product_type;
  996. $this->lines[$i] = $line;
  997. //dol_syslog("1 ".$line->fk_product);
  998. //print "xx $i ".$this->lines[$i]->fk_product;
  999. $i++;
  1000. }
  1001. $this->db->free($result);
  1002. }
  1003. else
  1004. {
  1005. $this->error=$this->db->error();
  1006. dol_syslog(get_class($this)."::fetch Error ".$this->error, LOG_ERR);
  1007. return -1;
  1008. }
  1009. return 1;
  1010. }
  1011. $this->error="Record Not Found";
  1012. return 0;
  1013. }
  1014. else
  1015. {
  1016. $this->error=$this->db->error();
  1017. dol_syslog(get_class($this)."::fetch Error ".$this->error, LOG_ERR);
  1018. return -1;
  1019. }
  1020. }
  1021. /**
  1022. * Set status to validated
  1023. *
  1024. * @param User $user Object user that validate
  1025. * @param int $notrigger 1=Does not execute triggers, 0= execuete triggers
  1026. * @return int <0 if KO, >=0 if OK
  1027. */
  1028. function valid($user, $notrigger=0)
  1029. {
  1030. global $conf,$langs;
  1031. $error=0;
  1032. $now=dol_now();
  1033. if ($user->rights->propale->valider)
  1034. {
  1035. $this->db->begin();
  1036. $sql = "UPDATE ".MAIN_DB_PREFIX."propal";
  1037. $sql.= " SET fk_statut = 1, date_valid='".$this->db->idate($now)."', fk_user_valid=".$user->id;
  1038. $sql.= " WHERE rowid = ".$this->id." AND fk_statut = 0";
  1039. dol_syslog(get_class($this).'::valid sql='.$sql);
  1040. if ($this->db->query($sql))
  1041. {
  1042. if (! $notrigger)
  1043. {
  1044. // Appel des triggers
  1045. include_once(DOL_DOCUMENT_ROOT . "/core/class/interfaces.class.php");
  1046. $interface=new Interfaces($this->db);
  1047. $result=$interface->run_triggers('PROPAL_VALIDATE',$this,$user,$langs,$conf);
  1048. if ($result < 0) {
  1049. $error++; $this->errors=$interface->errors;
  1050. }
  1051. // Fin appel triggers
  1052. }
  1053. if (! $error)
  1054. {
  1055. $this->brouillon=0;
  1056. $this->statut = 1;
  1057. $this->user_valid_id=$user->id;
  1058. $this->datev=$now;
  1059. $this->db->commit();
  1060. return 1;
  1061. }
  1062. else
  1063. {
  1064. $this->db->rollback();
  1065. return -2;
  1066. }
  1067. }
  1068. else
  1069. {
  1070. $this->db->rollback();
  1071. return -1;
  1072. }
  1073. }
  1074. }
  1075. /**
  1076. * Define proposal date
  1077. *
  1078. * @param User $user Object user that modify
  1079. * @param timestamp $date Date
  1080. * @return int <0 if KO, >0 if OK
  1081. */
  1082. function set_date($user, $date)
  1083. {
  1084. if (empty($date))
  1085. {
  1086. $this->error='ErrorBadParameter';
  1087. dol_syslog(get_class($this)."::set_date ".$this->error, LOG_ERR);
  1088. return -1;
  1089. }
  1090. if ($user->rights->propale->creer)
  1091. {
  1092. $sql = "UPDATE ".MAIN_DB_PREFIX."propal SET datep = '".$this->db->idate($date)."'";
  1093. $sql.= " WHERE rowid = ".$this->id." AND fk_statut = 0";
  1094. dol_syslog(get_class($this)."::set_date sql=".$sql);
  1095. if ($this->db->query($sql) )
  1096. {
  1097. $this->date = $date;
  1098. $this->datep = $date; // deprecated
  1099. return 1;
  1100. }
  1101. else
  1102. {
  1103. $this->error=$this->db->lasterror();
  1104. dol_syslog(get_class($this)."::set_date ".$this->error, LOG_ERR);
  1105. return -1;
  1106. }
  1107. }
  1108. }
  1109. /**
  1110. * Define end validity date
  1111. *
  1112. * @param User $user Object user that modify
  1113. * @param timestamp $date_fin_validite End of validity date
  1114. * @return int <0 if KO, >0 if OK
  1115. */
  1116. function set_echeance($user, $date_fin_validite)
  1117. {
  1118. if ($user->rights->propale->creer)
  1119. {
  1120. $sql = "UPDATE ".MAIN_DB_PREFIX."propal SET fin_validite = ".($date_fin_validite!=''?"'".$this->db->idate($date_fin_validite)."'":'null');
  1121. $sql.= " WHERE rowid = ".$this->id." AND fk_statut = 0";
  1122. if ($this->db->query($sql) )
  1123. {
  1124. $this->fin_validite = $date_fin_validite;
  1125. return 1;
  1126. }
  1127. else
  1128. {
  1129. $this->error=$this->db->error();
  1130. dol_syslog(get_class($this)."::set_echeance Erreur SQL".$this->error, LOG_ERR);
  1131. return -1;
  1132. }
  1133. }
  1134. }
  1135. /**
  1136. * Set delivery date
  1137. *
  1138. * @param User $user Object user that modify
  1139. * @param timestamp $date_livraison Delivery date
  1140. * @return int <0 if ko, >0 if ok
  1141. */
  1142. function set_date_livraison($user, $date_livraison)
  1143. {
  1144. if ($user->rights->propale->creer)
  1145. {
  1146. $sql = "UPDATE ".MAIN_DB_PREFIX."propal ";
  1147. $sql.= " SET date_livraison = ".($date_livraison!=''?"'".$this->db->idate($date_livraison)."'":'null');
  1148. $sql.= " WHERE rowid = ".$this->id;
  1149. if ($this->db->query($sql))
  1150. {
  1151. $this->date_livraison = $date_livraison;
  1152. return 1;
  1153. }
  1154. else
  1155. {
  1156. $this->error=$this->db->error();
  1157. dol_syslog(get_class($this)."::set_date_livraison Erreur SQL");
  1158. return -1;
  1159. }
  1160. }
  1161. }
  1162. /**
  1163. * Set delivery
  1164. *
  1165. * @param User $user Object user that modify
  1166. * @param int $id Availability id
  1167. * @return int <0 if KO, >0 if OK
  1168. */
  1169. function set_availability($user, $id)
  1170. {
  1171. if ($user->rights->propale->creer)
  1172. {
  1173. $sql = "UPDATE ".MAIN_DB_PREFIX."propal ";
  1174. $sql.= " SET fk_availability = '".$id."'";
  1175. $sql.= " WHERE rowid = ".$this->id;
  1176. if ($this->db->query($sql))
  1177. {
  1178. $this->fk_availability = $id;
  1179. return 1;
  1180. }
  1181. else
  1182. {
  1183. $this->error=$this->db->error();
  1184. dol_syslog("Propal::set_availability Erreur SQL");
  1185. return -1;
  1186. }
  1187. }
  1188. }
  1189. /**
  1190. * Set source of demand
  1191. *
  1192. * @param User $user Object user that modify
  1193. * @param int $id Input reason id
  1194. * @return int <0 if KO, >0 if OK
  1195. */
  1196. function set_demand_reason($user, $id)
  1197. {
  1198. if ($user->rights->propale->creer)
  1199. {
  1200. $sql = "UPDATE ".MAIN_DB_PREFIX."propal ";
  1201. $sql.= " SET fk_input_reason = '".$id."'";
  1202. $sql.= " WHERE rowid = ".$this->id;
  1203. if ($this->db->query($sql))
  1204. {
  1205. $this->fk_input_reason = $id;
  1206. return 1;
  1207. }
  1208. else
  1209. {
  1210. $this->error=$this->db->error();
  1211. dol_syslog("Propal::set_demand_reason Erreur SQL");
  1212. return -1;
  1213. }
  1214. }
  1215. }
  1216. /**
  1217. * Set customer reference number
  1218. *
  1219. * @param User $user Object user that modify
  1220. * @param string $ref_client Customer reference
  1221. * @return int <0 if ko, >0 if ok
  1222. */
  1223. function set_ref_client($user, $ref_client)
  1224. {
  1225. if ($user->rights->propale->creer)
  1226. {
  1227. dol_syslog('Propale::set_ref_client this->id='.$this->id.', ref_client='.$ref_client);
  1228. $sql = 'UPDATE '.MAIN_DB_PREFIX.'propal SET ref_client = '.(empty($ref_client) ? 'NULL' : '\''.$this->db->escape($ref_client).'\'');
  1229. $sql.= ' WHERE rowid = '.$this->id;
  1230. if ($this->db->query($sql) )
  1231. {
  1232. $this->ref_client = $ref_client;
  1233. return 1;
  1234. }
  1235. else
  1236. {
  1237. $this->error=$this->db->error();
  1238. dol_syslog('Propale::set_ref_client Erreur '.$this->error.' - '.$sql);
  1239. return -2;
  1240. }
  1241. }
  1242. else
  1243. {
  1244. return -1;
  1245. }
  1246. }
  1247. /**
  1248. * Set an overall discount on the proposal
  1249. *
  1250. * @param User $user Object user that modify
  1251. * @param double $remise Amount discount
  1252. * @return int <0 if ko, >0 if ok
  1253. */
  1254. function set_remise_percent($user, $remise)
  1255. {
  1256. $remise=trim($remise)?trim($remise):0;
  1257. if ($user->rights->propale->creer)
  1258. {
  1259. $remise = price2num($remise);
  1260. $sql = "UPDATE ".MAIN_DB_PREFIX."propal SET remise_percent = ".$remise;
  1261. $sql.= " WHERE rowid = ".$this->id." AND fk_statut = 0";
  1262. if ($this->db->query($sql) )
  1263. {
  1264. $this->remise_percent = $remise;
  1265. $this->update_price(1);
  1266. return 1;
  1267. }
  1268. else
  1269. {
  1270. $this->error=$this->db->error();
  1271. dol_syslog("Propal::set_remise_percent Error sql=$sql");
  1272. return -1;
  1273. }
  1274. }
  1275. }
  1276. /**
  1277. * Set an absolute overall discount on the proposal
  1278. *
  1279. * @param User $user Object user that modify
  1280. * @param double $remise Amount discount
  1281. * @return int <0 if ko, >0 if ok
  1282. */
  1283. function set_remise_absolue($user, $remise)
  1284. {
  1285. $remise=trim($remise)?trim($remise):0;
  1286. if ($user->rights->propale->creer)
  1287. {
  1288. $remise = price2num($remise);
  1289. $sql = "UPDATE ".MAIN_DB_PREFIX."propal ";
  1290. $sql.= " SET remise_absolue = ".$remise;
  1291. $sql.= " WHERE rowid = ".$this->id." AND fk_statut = 0";
  1292. if ($this->db->query($sql) )
  1293. {
  1294. $this->remise_absolue = $remise;
  1295. $this->update_price(1);
  1296. return 1;
  1297. }
  1298. else
  1299. {
  1300. $this->error=$this->db->error();
  1301. dol_syslog("Propal::set_remise_absolue Error sql=$sql");
  1302. return -1;
  1303. }
  1304. }
  1305. }
  1306. /**
  1307. * Close the commercial proposal
  1308. *
  1309. * @param User $user Object user that close
  1310. * @param int $statut Statut
  1311. * @param text $note Comment
  1312. * @return int <0 if KO, >0 if OK
  1313. */
  1314. function reopen($user, $statut, $note)
  1315. {
  1316. global $langs,$conf;
  1317. $this->statut = $statut;
  1318. $error=0;
  1319. $now=dol_now();
  1320. $this->db->begin();
  1321. $sql = "UPDATE ".MAIN_DB_PREFIX."propal";
  1322. $sql.= " SET fk_statut = ".$statut.", note = '".$this->db->escape($note)."', date_cloture=".$this->db->idate($now).", fk_user_cloture=".$user->id;
  1323. $sql.= " WHERE rowid = ".$this->id;
  1324. $resql=$this->db->query($sql);
  1325. if ($resql)
  1326. {
  1327. }
  1328. }
  1329. /**
  1330. * Close the commercial proposal
  1331. *
  1332. * @param User $user Object user that close
  1333. * @param int $statut Statut
  1334. * @param text $note Comment
  1335. * @return int <0 if KO, >0 if OK
  1336. */
  1337. function cloture($user, $statut, $note)
  1338. {
  1339. global $langs,$conf;
  1340. $this->statut = $statut;
  1341. $error=0;
  1342. $now=dol_now();
  1343. $this->db->begin();
  1344. $sql = "UPDATE ".MAIN_DB_PREFIX."propal";
  1345. $sql.= " SET fk_statut = ".$statut.", note = '".$this->db->escape($note)."', date_cloture=".$this->db->idate($now).", fk_user_cloture=".$user->id;
  1346. $sql.= " WHERE rowid = ".$this->id;
  1347. $resql=$this->db->query($sql);
  1348. if ($resql)
  1349. {
  1350. if ($statut == 2)
  1351. {
  1352. // Classe la societe rattachee comme client
  1353. $soc=new Societe($this->db);
  1354. $soc->id = $this->socid;
  1355. $result=$soc->set_as_client();
  1356. if ($result < 0)
  1357. {
  1358. $this->error=$this->db->error();
  1359. $this->db->rollback();
  1360. return -2;
  1361. }
  1362. // Appel des triggers
  1363. include_once(DOL_DOCUMENT_ROOT . "/core/class/interfaces.class.php");
  1364. $interface=new Interfaces($this->db);
  1365. $result=$interface->run_triggers('PROPAL_CLOSE_SIGNED',$this,$user,$langs,$conf);
  1366. if ($result < 0) {
  1367. $error++; $this->errors=$interface->errors;
  1368. }
  1369. // Fin appel triggers
  1370. }
  1371. else
  1372. {
  1373. // Appel des triggers
  1374. include_once(DOL_DOCUMENT_ROOT . "/core/class/interfaces.class.php");
  1375. $interface=new Interfaces($this->db);
  1376. $result=$interface->run_triggers('PROPAL_CLOSE_REFUSED',$this,$user,$langs,$conf);
  1377. if ($result < 0) {
  1378. $error++; $this->errors=$interface->errors;
  1379. }
  1380. // Fin appel triggers
  1381. }
  1382. $this->db->commit();
  1383. return 1;
  1384. }
  1385. else
  1386. {
  1387. $this->error=$this->db->error();
  1388. $this->db->rollback();
  1389. return -1;
  1390. }
  1391. }
  1392. /**
  1393. * Class invoiced the Propal
  1394. *
  1395. * @return int <0 si ko, >0 si ok
  1396. */
  1397. function classifyBilled()
  1398. {
  1399. $sql = 'UPDATE '.MAIN_DB_PREFIX.'propal SET fk_statut = 4';
  1400. $sql .= ' WHERE rowid = '.$this->id.' AND fk_statut > 0 ;';
  1401. if ($this->db->query($sql) )
  1402. {
  1403. $this->statut=4;
  1404. return 1;
  1405. }
  1406. else
  1407. {
  1408. dol_print_error($this->db);
  1409. }
  1410. }
  1411. /**
  1412. * Class invoiced the Propal
  1413. *
  1414. * @return int <0 si ko, >0 si ok
  1415. */
  1416. function classer_facturee()
  1417. {
  1418. return $this->classifyBilled();
  1419. }
  1420. /**
  1421. * Set draft status
  1422. *
  1423. * @param User $user Object user that modify
  1424. * @return int <0 if KO, >0 if OK
  1425. */
  1426. function set_draft($user)
  1427. {
  1428. global $conf,$langs;
  1429. $sql = "UPDATE ".MAIN_DB_PREFIX."propal SET fk_statut = 0";
  1430. $sql.= " WHERE rowid = ".$this->id;
  1431. if ($this->db->query($sql))
  1432. {
  1433. $this->statut = 0;
  1434. $this->brouillon = 1;
  1435. return 1;
  1436. }
  1437. else
  1438. {
  1439. return -1;
  1440. }
  1441. }
  1442. /**
  1443. * Return list of proposal (eventually filtered on user) into an array
  1444. *
  1445. * @param int $shortlist 0=Return array[id]=ref, 1=Return array[](id=>id,ref=>ref)
  1446. * @param int $draft 0=not draft, 1=draft
  1447. * @param int $notcurrentuser 0=current user, 1=not current user
  1448. * @param int $socid Id third pary
  1449. * @param int $limit For pagination
  1450. * @param int $offset For pagination
  1451. * @param string $sortfield Sort criteria
  1452. * @param string $sortorder Sort order
  1453. * @return int -1 if KO, array with result if OK
  1454. */
  1455. function liste_array($shortlist=0, $draft=0, $notcurrentuser=0, $socid=0, $limit=0, $offset=0, $sortfield='p.datep', $sortorder='DESC')
  1456. {
  1457. global $conf,$user;
  1458. $ga = array();
  1459. $sql = "SELECT s.nom, s.rowid, p.rowid as propalid, p.fk_statut, p.total_ht, p.ref, p.remise, ";
  1460. $sql.= " p.datep as dp, p.fin_validite as datelimite";
  1461. $sql.= " FROM ".MAIN_DB_PREFIX."societe as s, ".MAIN_DB_PREFIX."propal as p, ".MAIN_DB_PREFIX."c_propalst as c";
  1462. $sql.= " WHERE p.entity = ".$conf->entity;
  1463. $sql.= " AND p.fk_soc = s.rowid";
  1464. $sql.= " AND p.fk_statut = c.id";
  1465. if ($socid) $sql.= " AND s.rowid = ".$socid;
  1466. if ($draft) $sql.= " AND p.fk_statut = 0";
  1467. if ($notcurrentuser) $sql.= " AND p.fk_user_author <> ".$user->id;
  1468. $sql.= $this->db->order($sortfield,$sortorder);
  1469. $sql.= $this->db->plimit($limit,$offset);
  1470. $result=$this->db->query($sql);
  1471. if ($result)
  1472. {
  1473. $num = $this->db->num_rows($result);
  1474. if ($num)
  1475. {
  1476. $i = 0;
  1477. while ($i < $num)
  1478. {
  1479. $obj = $this->db->fetch_object($result);
  1480. if ($shortlist)
  1481. {
  1482. $ga[$obj->propalid] = $obj->ref;
  1483. }
  1484. else
  1485. {
  1486. $ga[$i]['id'] = $obj->propalid;
  1487. $ga[$i]['ref'] = $obj->ref;
  1488. }
  1489. $i++;
  1490. }
  1491. }
  1492. return $ga;
  1493. }
  1494. else
  1495. {
  1496. dol_print_error($this->db);
  1497. return -1;
  1498. }
  1499. }
  1500. /**
  1501. * Returns an array with the numbers of related invoices
  1502. *
  1503. * @return array Array of invoices
  1504. */
  1505. function getInvoiceArrayList()
  1506. {
  1507. return $this->InvoiceArrayList($this->id);
  1508. }
  1509. /**
  1510. * Returns an array with id and ref of related invoices
  1511. *
  1512. * @param int $id Id propal
  1513. * @return array Array of invoices id
  1514. */
  1515. function InvoiceArrayList($id)
  1516. {
  1517. $ga = array();
  1518. $linkedInvoices = array();
  1519. $this->fetchObjectLinked($id,$this->element);
  1520. foreach($this->linkedObjectsIds as $objecttype => $objectid)
  1521. {
  1522. $numi=count($objectid);
  1523. for ($i=0;$i<$numi;$i++)
  1524. {
  1525. // Cas des factures liees directement
  1526. if ($objecttype == 'facture')
  1527. {
  1528. $linkedInvoices[] = $objectid[$i];
  1529. }
  1530. // Cas des factures liees via la commande
  1531. else
  1532. {
  1533. $this->fetchObjectLinked($objectid[$i],$objecttype);
  1534. foreach($this->linkedObjectsIds as $subobjecttype => $subobjectid)
  1535. {
  1536. $numj=count($subobjectid);
  1537. for ($j=0;$j<$numj;$j++)
  1538. {
  1539. $linkedInvoices[] = $subobjectid[$j];
  1540. }
  1541. }
  1542. }
  1543. }
  1544. }
  1545. if (count($linkedInvoices) > 0)
  1546. {
  1547. $sql= "SELECT rowid as facid, facnumber, total, datef as df, fk_user_author, fk_statut, paye";
  1548. $sql.= " FROM ".MAIN_DB_PREFIX."facture";
  1549. $sql.= " WHERE rowid IN (".implode(',',$linkedInvoices).")";
  1550. dol_syslog("Propal::InvoiceArrayList sql=".$sql);
  1551. $resql=$this->db->query($sql);
  1552. if ($resql)
  1553. {
  1554. $tab_sqlobj=array();
  1555. $nump = $this->db->num_rows($resql);
  1556. for ($i = 0;$i < $nump;$i++)
  1557. {
  1558. $sqlobj = $this->db->fetch_object($resql);
  1559. $tab_sqlobj[] = $sqlobj;
  1560. }
  1561. $this->db->free($resql);
  1562. $nump = count($tab_sqlobj);
  1563. if ($nump)
  1564. {
  1565. $i = 0;
  1566. while ($i < $nump)
  1567. {
  1568. $obj = array_shift($tab_sqlobj);
  1569. $ga[$i] = $obj;
  1570. $i++;
  1571. }
  1572. }
  1573. return $ga;
  1574. }
  1575. else
  1576. {
  1577. return -1;
  1578. }
  1579. }
  1580. else return $ga;
  1581. }
  1582. /**
  1583. * Delete proposal
  1584. *
  1585. * @param User $user Object user that delete
  1586. * @param int $notrigger 1=Does not execute triggers, 0= execuete triggers
  1587. * @return int 1 if ok, otherwise if error
  1588. */
  1589. function delete($user, $notrigger=0)
  1590. {
  1591. global $conf,$langs;
  1592. require_once(DOL_DOCUMENT_ROOT."/core/lib/files.lib.php");
  1593. $error=0;
  1594. $this->db->begin();
  1595. if (! $error && ! $notrigger)
  1596. {
  1597. // Call triggers
  1598. include_once(DOL_DOCUMENT_ROOT . "/core/class/interfaces.class.php");
  1599. $interface=new Interfaces($this->db);
  1600. $result=$interface->run_triggers('PROPAL_DELETE',$this,$user,$langs,$conf);
  1601. if ($result < 0) {
  1602. $error++; $this->errors=$interface->errors;
  1603. }
  1604. // End call triggers
  1605. }
  1606. if (! $error)
  1607. {
  1608. $sql = "DELETE FROM ".MAIN_DB_PREFIX."propaldet WHERE fk_propal = ".$this->id;
  1609. if ($this->db->query($sql))
  1610. {
  1611. $sql = "DELETE FROM ".MAIN_DB_PREFIX."propal WHERE rowid = ".$this->id;
  1612. if ($this->db->query($sql))
  1613. {
  1614. // Delete linked object
  1615. $res = $this->deleteObjectLinked();
  1616. if ($res < 0) $error++;
  1617. // Delete linked contacts
  1618. $res = $this->delete_linked_contact();
  1619. if ($res < 0) $error++;
  1620. if (! $error)
  1621. {
  1622. // We remove directory
  1623. $ref = dol_sanitizeFileName($this->ref);
  1624. if ($conf->propal->dir_output)
  1625. {
  1626. $dir = $conf->propal->dir_output . "/" . $ref ;
  1627. $file = $dir . "/" . $ref . ".pdf";
  1628. if (file_exists($file))
  1629. {
  1630. dol_delete_preview($this);
  1631. if (! dol_delete_file($file,0,0,0,$this)) // For triggers
  1632. {
  1633. $this->error='ErrorFailToDeleteFile';
  1634. $this->errors=array('ErrorFailToDeleteFile');
  1635. $this->db->rollback();
  1636. return 0;
  1637. }
  1638. }
  1639. if (file_exists($dir))
  1640. {
  1641. $res=@dol_delete_dir_recursive($dir);
  1642. if (! $res)
  1643. {
  1644. $this->error='ErrorFailToDeleteDir';
  1645. $this->errors=array('ErrorFailToDeleteDir');
  1646. $this->db->rollback();
  1647. return 0;
  1648. }
  1649. }
  1650. }
  1651. }
  1652. if (! $error)
  1653. {
  1654. dol_syslog(get_class($this)."::delete $this->id by $user->id", LOG_DEBUG);
  1655. $this->db->commit();
  1656. return 1;
  1657. }
  1658. else
  1659. {
  1660. $this->error=$this->db->lasterror();
  1661. dol_syslog(get_class($this)."::delete ".$this->error, LOG_ERR);
  1662. $this->db->rollback();
  1663. return 0;
  1664. }
  1665. }
  1666. else
  1667. {
  1668. $this->error=$this->db->lasterror();
  1669. dol_syslog(get_class($this)."::delete ".$this->error, LOG_ERR);
  1670. $this->db->rollback();
  1671. return -3;
  1672. }
  1673. }
  1674. else
  1675. {
  1676. $this->error=$this->db->lasterror();
  1677. dol_syslog(get_class($this)."::delete ".$this->error, LOG_ERR);
  1678. $this->db->rollback();
  1679. return -2;
  1680. }
  1681. }
  1682. else
  1683. {
  1684. $this->error=$this->db->lasterror();
  1685. dol_syslog(get_class($this)."::delete ".$this->error, LOG_ERR);
  1686. $this->db->rollback();
  1687. return -1;
  1688. }
  1689. }
  1690. /**
  1691. * Change the delivery time
  1692. *
  1693. * @param int $availability_id Id of new delivery time
  1694. * @return int >0 if OK, <0 if KO
  1695. */
  1696. function availability($availability_id)
  1697. {
  1698. dol_syslog('Propale::availability('.$availability_id.')');
  1699. if ($this->statut >= 0)
  1700. {
  1701. $sql = 'UPDATE '.MAIN_DB_PREFIX.'propal';
  1702. $sql .= ' SET fk_availability = '.$availability_id;
  1703. $sql .= ' WHERE rowid='.$this->id;
  1704. if ( $this->db->query($sql) )
  1705. {
  1706. $this->availability_id = $availability_id;
  1707. return 1;
  1708. }
  1709. else
  1710. {
  1711. dol_syslog('Propale::availability Erreur '.$sql.' - '.$this->db->error());
  1712. $this->error=$this->db->error();
  1713. return -1;
  1714. }
  1715. }
  1716. else
  1717. {
  1718. dol_syslog('Propale::availability, etat propale incompatible');
  1719. $this->error='Etat propale incompatible '.$this->statut;
  1720. return -2;
  1721. }
  1722. }
  1723. /**
  1724. * Change source demand
  1725. *
  1726. * @param int $demand_reason_id Id of new source demand
  1727. * @return int >0 si ok, <0 si ko
  1728. */
  1729. function demand_reason($demand_reason_id)
  1730. {
  1731. dol_syslog('Propale::demand_reason('.$demand_reason_id.')');
  1732. if ($this->statut >= 0)
  1733. {
  1734. $sql = 'UPDATE '.MAIN_DB_PREFIX.'propal';
  1735. $sql .= ' SET fk_input_reason = '.$demand_reason_id;
  1736. $sql .= ' WHERE rowid='.$this->id;
  1737. if ( $this->db->query($sql) )
  1738. {
  1739. $this->demand_reason_id = $demand_reason_id;
  1740. return 1;
  1741. }
  1742. else
  1743. {
  1744. dol_syslog('Propale::demand_reason Erreur '.$sql.' - '.$this->db->error());
  1745. $this->error=$this->db->error();
  1746. return -1;
  1747. }
  1748. }
  1749. else
  1750. {
  1751. dol_syslog('Propale::demand_reason, etat propale incompatible');
  1752. $this->error='Etat propale incompatible '.$this->statut;
  1753. return -2;
  1754. }
  1755. }
  1756. /**
  1757. * Object Proposal Information
  1758. *
  1759. * @param int $id Proposal id
  1760. * @return void
  1761. */
  1762. function info($id)
  1763. {
  1764. $sql = "SELECT c.rowid, ";
  1765. $sql.= " c.datec, c.date_valid as datev, c.date_cloture as dateo,";
  1766. $sql.= " c.fk_user_author, c.fk_user_valid, c.fk_user_cloture";
  1767. $sql.= " FROM ".MAIN_DB_PREFIX."propal as c";
  1768. $sql.= " WHERE c.rowid = ".$id;
  1769. $result = $this->db->query($sql);
  1770. if ($result)
  1771. {
  1772. if ($this->db->num_rows($result))
  1773. {
  1774. $obj = $this->db->fetch_object($result);
  1775. $this->id = $obj->rowid;
  1776. $this->date_creation = $this->db->jdate($obj->datec);
  1777. $this->date_validation = $this->db->jdate($obj->datev);
  1778. $this->date_cloture = $this->db->jdate($obj->dateo);
  1779. $cuser = new User($this->db);
  1780. $cuser->fetch($obj->fk_user_author);
  1781. $this->user_creation = $cuser;
  1782. if ($obj->fk_user_valid)
  1783. {
  1784. $vuser = new User($this->db);
  1785. $vuser->fetch($obj->fk_user_valid);
  1786. $this->user_validation = $vuser;
  1787. }
  1788. if ($obj->fk_user_cloture)
  1789. {
  1790. $cluser = new User($this->db);
  1791. $cluser->fetch($obj->fk_user_cloture);
  1792. $this->user_cloture = $cluser;
  1793. }
  1794. }
  1795. $this->db->free($result);
  1796. }
  1797. else
  1798. {
  1799. dol_print_error($this->db);
  1800. }
  1801. }
  1802. /**
  1803. * Return label of status of proposal (draft, validated, ...)
  1804. *
  1805. * @param int $mode 0=long label, 1=short label, 2=Picto + short label, 3=Picto, 4=Picto + long label, 5=Short label + Picto
  1806. * @return string Label
  1807. */
  1808. function getLibStatut($mode=0)
  1809. {
  1810. return $this->LibStatut($this->statut,$mode);
  1811. }
  1812. /**
  1813. * Return label of a status (draft, validated, ...)
  1814. *
  1815. * @param int $statut id statut
  1816. * @param int $mode 0=long label, 1=short label, 2=Picto + short label, 3=Picto, 4=Picto + long label, 5=Short label + Picto
  1817. * @return string Label
  1818. */
  1819. function LibStatut($statut,$mode=1)
  1820. {
  1821. global $langs;
  1822. $langs->load("propal");
  1823. if ($mode == 0)
  1824. {
  1825. return $this->labelstatut[$statut];
  1826. }
  1827. if ($mode == 1)
  1828. {
  1829. return $this->labelstatut_short[$statut];
  1830. }
  1831. if ($mode == 2)
  1832. {
  1833. if ($statut==0) return img_picto($langs->trans('PropalStatusDraftShort'),'statut0').' '.$this->labelstatut_short[$statut];
  1834. if ($statut==1) return img_picto($langs->trans('PropalStatusOpenedShort'),'statut1').' '.$this->labelstatut_short[$statut];
  1835. if ($statut==2) return img_picto($langs->trans('PropalStatusSignedShort'),'statut3').' '.$this->labelstatut_short[$statut];
  1836. if ($statut==3) return img_picto($langs->trans('PropalStatusNotSignedShort'),'statut5').' '.$this->labelstatut_short[$statut];
  1837. if ($statut==4) return img_picto($langs->trans('PropalStatusBilledShort'),'statut6').' '.$this->labelstatut_short[$statut];
  1838. }
  1839. if ($mode == 3)
  1840. {
  1841. if ($statut==0) return img_picto($langs->trans('PropalStatusDraftShort'),'statut0');
  1842. if ($statut==1) return img_picto($langs->trans('PropalStatusOpenedShort'),'statut1');
  1843. if ($statut==2) return img_picto($langs->trans('PropalStatusSignedShort'),'statut3');
  1844. if ($statut==3) return img_picto($langs->trans('PropalStatusNotSignedShort'),'statut5');
  1845. if ($statut==4) return img_picto($langs->trans('PropalStatusBilledShort'),'statut6');
  1846. }
  1847. if ($mode == 4)
  1848. {
  1849. if ($statut==0) return img_picto($langs->trans('PropalStatusDraft'),'statut0').' '.$this->labelstatut[$statut];
  1850. if ($statut==1) return img_picto($langs->trans('PropalStatusOpened'),'statut1').' '.$this->labelstatut[$statut];
  1851. if ($statut==2) return img_picto($langs->trans('PropalStatusSigned'),'statut3').' '.$this->labelstatut[$statut];
  1852. if ($statut==3) return img_picto($langs->trans('PropalStatusNotSigned'),'statut5').' '.$this->labelstatut[$statut];
  1853. if ($statut==4) return img_picto($langs->trans('PropalStatusBilled'),'statut6').' '.$this->labelstatut[$statut];
  1854. }
  1855. if ($mode == 5)
  1856. {
  1857. if ($statut==0) return $this->labelstatut_short[$statut].' '.img_picto($langs->trans('PropalStatusDraftShort'),'statut0');
  1858. if ($statut==1) return $this->labelstatut_short[$statut].' '.img_picto($langs->trans('PropalStatusOpenedShort'),'statut1');
  1859. if ($statut==2) return $this->labelstatut_short[$statut].' '.img_picto($langs->trans('PropalStatusSignedShort'),'statut3');
  1860. if ($statut==3) return $this->labelstatut_short[$statut].' '.img_picto($langs->trans('PropalStatusNotSignedShort'),'statut5');
  1861. if ($statut==4) return $this->labelstatut_short[$statut].' '.img_picto($langs->trans('PropalStatusBilledShort'),'statut6');
  1862. }
  1863. }
  1864. /**
  1865. * Load indicators for dashboard (this->nbtodo and this->nbtodolate)
  1866. *
  1867. * @param User $user Object user
  1868. * @param int $mode "opened" for proposal to close, "signed" for proposal to invoice
  1869. * @return int <0 if KO, >0 if OK
  1870. */
  1871. function load_board($user,$mode)
  1872. {
  1873. global $conf, $user;
  1874. $now=dol_now();
  1875. $this->nbtodo=$this->nbtodolate=0;
  1876. $clause = " WHERE";
  1877. $sql = "SELECT p.rowid, p.ref, p.datec as datec, p.fin_validite as datefin";
  1878. $sql.= " FROM ".MAIN_DB_PREFIX."propal as p";
  1879. if (!$user->rights->societe->client->voir && !$user->societe_id)
  1880. {
  1881. $sql.= " LEFT JOIN ".MAIN_DB_PREFIX."societe_commerciaux as sc ON p.fk_soc = sc.fk_soc";
  1882. $sql.= " WHERE sc.fk_user = " .$user->id;
  1883. $clause = " AND";
  1884. }
  1885. $sql.= $clause." p.entity = ".$conf->entity;
  1886. if ($mode == 'opened') $sql.= " AND p.fk_statut = 1";
  1887. if ($mode == 'signed') $sql.= " AND p.fk_statut = 2";
  1888. if ($user->societe_id) $sql.= " AND p.fk_soc = ".$user->societe_id;
  1889. $resql=$this->db->query($sql);
  1890. if ($resql)
  1891. {
  1892. if ($mode == 'opened') $delay_warning=$conf->propal->cloture->warning_delay;
  1893. if ($mode == 'signed') $delay_warning=$conf->propal->facturation->warning_delay;
  1894. while ($obj=$this->db->fetch_object($resql))
  1895. {
  1896. $this->nbtodo++;
  1897. if ($mode == 'opened')
  1898. {
  1899. $datelimit = $this->db->jdate($obj->datefin);
  1900. if ($datelimit < ($now - $delay_warning))
  1901. {
  1902. $this->nbtodolate++;
  1903. }
  1904. }
  1905. // TODO Definir regle des propales a facturer en retard
  1906. // if ($mode == 'signed' && ! count($this->FactureListeArray($obj->rowid))) $this->nbtodolate++;
  1907. }
  1908. return 1;
  1909. }
  1910. else
  1911. {
  1912. $this->error=$this->db->error();
  1913. return -1;
  1914. }
  1915. }
  1916. /**
  1917. * Initialise an instance with random values.
  1918. * Used to build previews or test instances.
  1919. * id must be 0 if object instance is a specimen.
  1920. *
  1921. * @return void
  1922. */
  1923. function initAsSpecimen()
  1924. {
  1925. global $user,$langs,$conf;
  1926. // Charge tableau des produits prodids
  1927. $prodids = array();
  1928. $sql = "SELECT rowid";
  1929. $sql.= " FROM ".MAIN_DB_PREFIX."product";
  1930. $sql.= " WHERE entity IN (".getEntity('product', 1).")";
  1931. $resql = $this->db->query($sql);
  1932. if ($resql)
  1933. {
  1934. $num_prods = $this->db->num_rows($resql);
  1935. $i = 0;
  1936. while ($i < $num_prods)
  1937. {
  1938. $i++;
  1939. $row = $this->db->fetch_row($resql);
  1940. $prodids[$i] = $row[0];
  1941. }
  1942. }
  1943. // Initialise parametres
  1944. $this->id=0;
  1945. $this->ref = 'SPECIMEN';
  1946. $this->ref_client='NEMICEPS';
  1947. $this->specimen=1;
  1948. $this->socid = 1;
  1949. $this->date = time();
  1950. $this->fin_validite = $this->date+3600*24*30;
  1951. $this->cond_reglement_id = 1;
  1952. $this->cond_reglement_code = 'RECEP';
  1953. $this->mode_reglement_id = 7;
  1954. $this->mode_reglement_code = 'CHQ';
  1955. $this->availability_id = 1;
  1956. $this->availability_code = 'DSP';
  1957. $this->demand_reason_id = 1;
  1958. $this->demand_reason_code = 'SRC_00';
  1959. $this->note_public='This is a comment (public)';
  1960. $this->note='This is a comment (private)';
  1961. // Lines
  1962. $nbp = 5;
  1963. $xnbp = 0;
  1964. while ($xnbp < $nbp)
  1965. {
  1966. $line=new PropaleLigne($this->db);
  1967. $line->desc=$langs->trans("Description")." ".$xnbp;
  1968. $line->qty=1;
  1969. $line->subprice=100;
  1970. $line->price=100;
  1971. $line->tva_tx=19.6;
  1972. $line->localtax1_tx=0;
  1973. $line->localtax2_tx=0;
  1974. if ($xnbp == 2)
  1975. {
  1976. $line->total_ht=50;
  1977. $line->total_ttc=59.8;
  1978. $line->total_tva=9.8;
  1979. $line->remise_percent=50;
  1980. }
  1981. else
  1982. {
  1983. $line->total_ht=100;
  1984. $line->total_ttc=119.6;
  1985. $line->total_tva=19.6;
  1986. $line->remise_percent=00;
  1987. }
  1988. $prodid = rand(1, $num_prods);
  1989. $line->fk_product=$prodids[$prodid];
  1990. $this->lines[$xnbp]=$line;
  1991. $this->total_ht += $line->total_ht;
  1992. $this->total_tva += $line->total_tva;
  1993. $this->total_ttc += $line->total_ttc;
  1994. $xnbp++;
  1995. }
  1996. }
  1997. /**
  1998. * Charge indicateurs this->nb de tableau de bord
  1999. *
  2000. * @return int <0 if ko, >0 if ok
  2001. */
  2002. function load_state_board()
  2003. {
  2004. global $conf, $user;
  2005. $this->nb=array();
  2006. $clause = "WHERE";
  2007. $sql = "SELECT count(p.rowid) as nb";
  2008. $sql.= " FROM ".MAIN_DB_PREFIX."propal as p";
  2009. $sql.= " LEFT JOIN ".MAIN_DB_PREFIX."societe as s ON p.fk_soc = s.rowid";
  2010. if (!$user->rights->societe->client->voir && !$user->societe_id)
  2011. {
  2012. $sql.= " LEFT JOIN ".MAIN_DB_PREFIX."societe_commerciaux as sc ON s.rowid = sc.fk_soc";
  2013. $sql.= " WHERE sc.fk_user = " .$user->id;
  2014. $clause = "AND";
  2015. }
  2016. $sql.= " ".$clause." p.entity = ".$conf->entity;
  2017. $resql=$this->db->query($sql);
  2018. if ($resql)
  2019. {
  2020. while ($obj=$this->db->fetch_object($resql))
  2021. {
  2022. $this->nb["proposals"]=$obj->nb;
  2023. }
  2024. return 1;
  2025. }
  2026. else
  2027. {
  2028. dol_print_error($this->db);
  2029. $this->error=$this->db->error();
  2030. return -1;
  2031. }
  2032. }
  2033. /**
  2034. * Returns the reference to the following non used Proposal used depending on the active numbering module
  2035. * defined into PROPALE_ADDON
  2036. *
  2037. * @param Societe $soc Object thirdparty
  2038. * @return string Reference libre pour la propale
  2039. */
  2040. function getNextNumRef($soc)
  2041. {
  2042. global $conf, $db, $langs;
  2043. $langs->load("propal");
  2044. $dir = DOL_DOCUMENT_ROOT . "/core/modules/propale/";
  2045. if (! empty($conf->global->PROPALE_ADDON))
  2046. {
  2047. $file = $conf->global->PROPALE_ADDON.".php";
  2048. // Chargement de la classe de numerotation
  2049. $classname = $conf->global->PROPALE_ADDON;
  2050. require_once($dir.$file);
  2051. $obj = new $classname();
  2052. $numref = "";
  2053. $numref = $obj->getNextValue($soc,$this);
  2054. if ( $numref != "")
  2055. {
  2056. return $numref;
  2057. }
  2058. else
  2059. {
  2060. $this->error=$obj->error;
  2061. //dol_print_error($db,"Propale::getNextNumRef ".$obj->error);
  2062. return "";
  2063. }
  2064. }
  2065. else
  2066. {
  2067. $langs->load("errors");
  2068. print $langs->trans("Error")." ".$langs->trans("ErrorModuleSetupNotComplete");
  2069. return "";
  2070. }
  2071. }
  2072. /**
  2073. * Return clicable link of object (with eventually picto)
  2074. *
  2075. * @param int $withpicto Add picto into link
  2076. * @param string $option Where point the link ('compta', 'expedition', 'document', ...)
  2077. * @param string $get_params Parametres added to url
  2078. * @return string String with URL
  2079. */
  2080. function getNomUrl($withpicto=0,$option='', $get_params='')
  2081. {
  2082. global $langs;
  2083. $result='';
  2084. if ($option == '')
  2085. {
  2086. $lien = '<a href="'.DOL_URL_ROOT.'/comm/propal.php?id='.$this->id. $get_params .'">';
  2087. }
  2088. if ($option == 'compta') // deprecated
  2089. {
  2090. $lien = '<a href="'.DOL_URL_ROOT.'/comm/propal.php?id='.$this->id. $get_params .'">';
  2091. }
  2092. if ($option == 'expedition')
  2093. {
  2094. $lien = '<a href="'.DOL_URL_ROOT.'/expedition/propal.php?id='.$this->id. $get_params .'">';
  2095. }
  2096. if ($option == 'document')
  2097. {
  2098. $lien = '<a href="'.DOL_URL_ROOT.'/comm/propal/document.php?id='.$this->id. $get_params .'">';
  2099. }
  2100. $lienfin='</a>';
  2101. $picto='propal';
  2102. $label=$langs->trans("ShowPropal").': '.$this->ref;
  2103. if ($withpicto) $result.=($lien.img_object($label,$picto).$lienfin);
  2104. if ($withpicto && $withpicto != 2) $result.=' ';
  2105. $result.=$lien.$this->ref.$lienfin;
  2106. return $result;
  2107. }
  2108. /**
  2109. * Retrieve an array of propal lines
  2110. *
  2111. * @return int <0 if ko, >0 if ok
  2112. */
  2113. function getLinesArray()
  2114. {
  2115. $sql = 'SELECT pt.rowid, pt.description, pt.fk_product, pt.fk_remise_except,';
  2116. $sql.= ' pt.qty, pt.tva_tx, pt.remise_percent, pt.subprice, pt.info_bits,';
  2117. $sql.= ' pt.total_ht, pt.total_tva, pt.total_ttc, pt.fk_product_fournisseur_price as fk_fournprice, pt.buy_price_ht as pa_ht, pt.special_code, pt.localtax1_tx, pt.localtax2_tx,';
  2118. $sql.= ' pt.date_start, pt.date_end, pt.product_type, pt.rang, pt.fk_parent_line,';
  2119. $sql.= ' p.label as product_label, p.ref, p.fk_product_type, p.rowid as prodid,';
  2120. $sql.= ' p.description as product_desc';
  2121. $sql.= ' FROM '.MAIN_DB_PREFIX.'propaldet as pt';
  2122. $sql.= ' LEFT JOIN '.MAIN_DB_PREFIX.'product as p ON pt.fk_product=p.rowid';
  2123. $sql.= ' WHERE pt.fk_propal = '.$this->id;
  2124. $sql.= ' ORDER BY pt.rang ASC, pt.rowid';
  2125. $resql = $this->db->query($sql);
  2126. if ($resql)
  2127. {
  2128. $num = $this->db->num_rows($resql);
  2129. $i = 0;
  2130. while ($i < $num)
  2131. {
  2132. $obj = $this->db->fetch_object($resql);
  2133. $this->lines[$i] = (object) array();
  2134. $this->lines[$i]->id = $obj->rowid; // for backward compatibility
  2135. $this->lines[$i]->rowid = $obj->rowid;
  2136. $this->lines[$i]->description = $obj->description;
  2137. $this->lines[$i]->fk_product = $obj->fk_product;
  2138. $this->lines[$i]->ref = $obj->ref;
  2139. $this->lines[$i]->product_label = $obj->product_label;
  2140. $this->lines[$i]->product_desc = $obj->product_desc;
  2141. $this->lines[$i]->fk_product_type = $obj->fk_product_type; // deprecated
  2142. $this->lines[$i]->product_type = $obj->product_type;
  2143. $this->lines[$i]->qty = $obj->qty;
  2144. $this->lines[$i]->subprice = $obj->subprice;
  2145. $this->lines[$i]->fk_remise_except = $obj->fk_remise_except;
  2146. $this->lines[$i]->remise_percent = $obj->remise_percent;
  2147. $this->lines[$i]->tva_tx = $obj->tva_tx;
  2148. $this->lines[$i]->info_bits = $obj->info_bits;
  2149. $this->lines[$i]->total_ht = $obj->total_ht;
  2150. $this->lines[$i]->total_tva = $obj->total_tva;
  2151. $this->lines[$i]->total_ttc = $obj->total_ttc;
  2152. $this->lines[$i]->fk_fournprice = $obj->fk_fournprice;
  2153. $marginInfos = getMarginInfos($obj->subprice, $obj->remise_percent, $obj->tva_tx, $obj->localtax1_tx, $obj->localtax2_tx, $this->lines[$i]->fk_fournprice, $obj->pa_ht);
  2154. $this->lines[$i]->pa_ht = $marginInfos[0];
  2155. $this->lines[$i]->marge_tx = $marginInfos[1];
  2156. $this->lines[$i]->marque_tx = $marginInfos[2];
  2157. $this->lines[$i]->fk_parent_line = $obj->fk_parent_line;
  2158. $this->lines[$i]->special_code = $obj->special_code;
  2159. $this->lines[$i]->rang = $obj->rang;
  2160. $this->lines[$i]->date_start = $this->db->jdate($obj->date_start);
  2161. $this->lines[$i]->date_end = $this->db->jdate($obj->date_end);
  2162. $i++;
  2163. }
  2164. $this->db->free($resql);
  2165. return 1;
  2166. }
  2167. else
  2168. {
  2169. $this->error=$this->db->error();
  2170. dol_syslog(get_class($this)."::getLinesArray Error sql=$sql, error=".$this->error,LOG_ERR);
  2171. return -1;
  2172. }
  2173. }
  2174. }
  2175. /**
  2176. * \class PropaleLigne
  2177. * \brief Class to manage commercial proposal lines
  2178. */
  2179. class PropaleLigne
  2180. {
  2181. var $db;
  2182. var $error;
  2183. var $oldline;
  2184. // From llx_propaldet
  2185. var $rowid;
  2186. var $fk_propal;
  2187. var $fk_parent_line;
  2188. var $desc; // Description ligne
  2189. var $fk_product; // Id produit predefini
  2190. var $product_type = 0; // Type 0 = product, 1 = Service
  2191. var $qty;
  2192. var $tva_tx;
  2193. var $subprice;
  2194. var $remise_percent;
  2195. var $fk_remise_except;
  2196. var $rang = 0;
  2197. var $fk_fournprice;
  2198. var $pa_ht;
  2199. var $marge_tx;
  2200. var $marque_tx;
  2201. var $special_code; // Liste d'options non cumulabels:
  2202. // 1: frais de port
  2203. // 2: ecotaxe
  2204. // 3: ??
  2205. var $info_bits = 0; // Liste d'options cumulables:
  2206. // Bit 0: 0 si TVA normal - 1 si TVA NPR
  2207. // Bit 1: 0 ligne normale - 1 si ligne de remise fixe
  2208. var $total_ht; // Total HT de la ligne toute quantite et incluant la remise ligne
  2209. var $total_tva; // Total TVA de la ligne toute quantite et incluant la remise ligne
  2210. var $total_ttc; // Total TTC de la ligne toute quantite et incluant la remise ligne
  2211. // Ne plus utiliser
  2212. var $remise;
  2213. var $price;
  2214. // From llx_product
  2215. var $ref; // Reference produit
  2216. var $libelle; // Label produit
  2217. var $product_desc; // Description produit
  2218. var $localtax1_tx;
  2219. var $localtax2_tx;
  2220. var $total_localtax1;
  2221. var $total_localtax2;
  2222. var $skip_update_total; // Skip update price total for special lines
  2223. /**
  2224. * Class line Contructor
  2225. *
  2226. * @param DoliDB $db Database handler
  2227. */
  2228. function __construct($db)
  2229. {
  2230. $this->db= $db;
  2231. }
  2232. /**
  2233. * Retrieve the propal line object
  2234. *
  2235. * @param int $rowid Propal line id
  2236. * @return int <0 if KO, >0 if OK
  2237. */
  2238. function fetch($rowid)
  2239. {
  2240. $sql = 'SELECT pd.rowid, pd.fk_propal, pd.fk_parent_line, pd.fk_product, pd.description, pd.price, pd.qty, pd.tva_tx,';
  2241. $sql.= ' pd.remise, pd.remise_percent, pd.fk_remise_except, pd.subprice,';
  2242. $sql.= ' pd.info_bits, pd.total_ht, pd.total_tva, pd.total_ttc, pd.fk_product_fournisseur_price as fk_fournprice, pd.buy_price_ht as pa_ht, pd.special_code, pd.rang,';
  2243. $sql.= ' pd.localtax1_tx, pd.localtax2_tx, pd.total_localtax1, pd.total_localtax2,';
  2244. $sql.= ' p.ref as product_ref, p.label as product_libelle, p.description as product_desc';
  2245. $sql.= ' FROM '.MAIN_DB_PREFIX.'propaldet as pd';
  2246. $sql.= ' LEFT JOIN '.MAIN_DB_PREFIX.'product as p ON pd.fk_product = p.rowid';
  2247. $sql.= ' WHERE pd.rowid = '.$rowid;
  2248. $result = $this->db->query($sql);
  2249. if ($result)
  2250. {
  2251. $objp = $this->db->fetch_object($result);
  2252. $this->rowid = $objp->rowid;
  2253. $this->fk_propal = $objp->fk_propal;
  2254. $this->fk_parent_line = $objp->fk_parent_line;
  2255. $this->desc = $objp->description;
  2256. $this->qty = $objp->qty;
  2257. $this->price = $objp->price; // deprecated
  2258. $this->subprice = $objp->subprice;
  2259. $this->tva_tx = $objp->tva_tx;
  2260. $this->remise = $objp->remise;
  2261. $this->remise_percent = $objp->remise_percent;
  2262. $this->fk_remise_except = $objp->fk_remise_except;
  2263. $this->fk_product = $objp->fk_product;
  2264. $this->info_bits = $objp->info_bits;
  2265. $this->total_ht = $objp->total_ht;
  2266. $this->total_tva = $objp->total_tva;
  2267. $this->total_ttc = $objp->total_ttc;
  2268. $this->fk_fournprice = $objp->fk_fournprice;
  2269. $marginInfos = getMarginInfos($objp->subprice, $objp->remise_percent, $objp->tva_tx, $objp->localtax1_tx, $objp->localtax2_tx, $this->fk_fournprice, $objp->pa_ht);
  2270. $this->pa_ht = $marginInfos[0];
  2271. $this->marge_tx = $marginInfos[1];
  2272. $this->marque_tx = $marginInfos[2];
  2273. $this->special_code = $objp->special_code;
  2274. $this->rang = $objp->rang;
  2275. $this->ref = $objp->product_ref; // deprecated
  2276. $this->product_ref = $objp->product_ref;
  2277. $this->libelle = $objp->product_libelle; // deprecated
  2278. $this->product_label = $objp->product_libelle;
  2279. $this->product_desc = $objp->product_desc;
  2280. $this->db->free($result);
  2281. }
  2282. else
  2283. {
  2284. dol_print_error($this->db);
  2285. }
  2286. }
  2287. /**
  2288. * Insert object line propal in database
  2289. *
  2290. * @param int $notrigger 1=Does not execute triggers, 0= execuete triggers
  2291. * @return int <0 if KO, >0 if OK
  2292. */
  2293. function insert($notrigger=0)
  2294. {
  2295. global $conf,$langs,$user;
  2296. $error=0;
  2297. dol_syslog("PropaleLigne::insert rang=".$this->rang);
  2298. // Clean parameters
  2299. if (empty($this->tva_tx)) $this->tva_tx=0;
  2300. if (empty($this->localtax1_tx)) $this->localtax1_tx=0;
  2301. if (empty($this->localtax2_tx)) $this->localtax2_tx=0;
  2302. if (empty($this->total_localtax1)) $this->total_localtax1=0;
  2303. if (empty($this->total_localtax2)) $this->total_localtax2=0;
  2304. if (empty($this->rang)) $this->rang=0;
  2305. if (empty($this->remise)) $this->remise=0;
  2306. if (empty($this->remise_percent)) $this->remise_percent=0;
  2307. if (empty($this->info_bits)) $this->info_bits=0;
  2308. if (empty($this->special_code)) $this->special_code=0;
  2309. if (empty($this->fk_parent_line)) $this->fk_parent_line=0;
  2310. if (empty($this->pa_ht)) $this->pa_ht=0;
  2311. // si prix d'achat non renseign� et utilis� pour calcul des marges alors prix achat = prix vente (idem pour remises)
  2312. if ($this->pa_ht == 0) {
  2313. if ($this->subprice < 0 || (isset($conf->global->CalculateMarginsOnLinesWithoutBuyingPrice) && $conf->global->CalculateMarginsOnLinesWithoutBuyingPrice == 1))
  2314. $this->pa_ht = $this->subprice * (1 - $this->remise_percent / 100);
  2315. }
  2316. // Check parameters
  2317. if ($this->product_type < 0) return -1;
  2318. $this->db->begin();
  2319. // Insert line into database
  2320. $sql = 'INSERT INTO '.MAIN_DB_PREFIX.'propaldet';
  2321. $sql.= ' (fk_propal, fk_parent_line, description, fk_product, product_type, fk_remise_except, qty, tva_tx, localtax1_tx, localtax2_tx,';
  2322. $sql.= ' subprice, remise_percent, ';
  2323. $sql.= ' info_bits, ';
  2324. $sql.= ' total_ht, total_tva, total_localtax1, total_localtax2, total_ttc, fk_product_fournisseur_price, buy_price_ht, special_code, rang)';
  2325. $sql.= " VALUES (".$this->fk_propal.",";
  2326. $sql.= " ".($this->fk_parent_line>0?"'".$this->fk_parent_line."'":"null").",";
  2327. $sql.= " '".$this->db->escape($this->desc)."',";
  2328. $sql.= " ".($this->fk_product?"'".$this->fk_product."'":"null").",";
  2329. $sql.= " '".$this->product_type."',";
  2330. $sql.= " ".($this->fk_remise_except?"'".$this->fk_remise_except."'":"null").",";
  2331. $sql.= " ".price2num($this->qty).",";
  2332. $sql.= " ".price2num($this->tva_tx).",";
  2333. $sql.= " ".price2num($this->localtax1_tx).",";
  2334. $sql.= " ".price2num($this->localtax2_tx).",";
  2335. $sql.= " ".($this->subprice?price2num($this->subprice):'null').",";
  2336. $sql.= " ".price2num($this->remise_percent).",";
  2337. $sql.= " '".$this->info_bits."',";
  2338. $sql.= " ".price2num($this->total_ht).",";
  2339. $sql.= " ".price2num($this->total_tva).",";
  2340. $sql.= " ".price2num($this->total_localtax1).",";
  2341. $sql.= " ".price2num($this->total_localtax2).",";
  2342. $sql.= " ".price2num($this->total_ttc).",";
  2343. $sql.= " ".(isset($this->fk_fournprice)?"'".$this->fk_fournprice."'":"null").",";
  2344. $sql.= " ".(isset($this->pa_ht)?"'".price2num($this->pa_ht)."'":"null").",";
  2345. $sql.= ' '.$this->special_code.',';
  2346. $sql.= ' '.$this->rang;
  2347. $sql.= ')';
  2348. dol_syslog(get_class($this).'::insert sql='.$sql, LOG_DEBUG);
  2349. $resql=$this->db->query($sql);
  2350. if ($resql)
  2351. {
  2352. $this->rowid=$this->db->last_insert_id(MAIN_DB_PREFIX.'propaldet');
  2353. if (! $notrigger)
  2354. {
  2355. // Appel des triggers
  2356. include_once(DOL_DOCUMENT_ROOT . "/core/class/interfaces.class.php");
  2357. $interface=new Interfaces($this->db);
  2358. $result = $interface->run_triggers('LINEPROPAL_INSERT',$this,$user,$langs,$conf);
  2359. if ($result < 0) {
  2360. $error++; $this->errors=$interface->errors;
  2361. }
  2362. // Fin appel triggers
  2363. }
  2364. $this->db->commit();
  2365. return 1;
  2366. }
  2367. else
  2368. {
  2369. $this->error=$this->db->error()." sql=".$sql;
  2370. dol_syslog(get_class($this).'::insert Error '.$this->error, LOG_ERR);
  2371. $this->db->rollback();
  2372. return -1;
  2373. }
  2374. }
  2375. /**
  2376. * Delete line in database
  2377. *
  2378. * @return int <0 if ko, >0 if ok
  2379. */
  2380. function delete()
  2381. {
  2382. global $conf,$langs,$user;
  2383. $error=0;
  2384. $this->db->begin();
  2385. $sql = "DELETE FROM ".MAIN_DB_PREFIX."propaldet WHERE rowid = ".$this->rowid;
  2386. dol_syslog("PropaleLigne::delete sql=".$sql, LOG_DEBUG);
  2387. if ($this->db->query($sql) )
  2388. {
  2389. // Appel des triggers
  2390. include_once(DOL_DOCUMENT_ROOT . "/core/class/interfaces.class.php");
  2391. $interface=new Interfaces($this->db);
  2392. $result = $interface->run_triggers('LINEPROPAL_DELETE',$this,$user,$langs,$conf);
  2393. if ($result < 0) {
  2394. $error++; $this->errors=$interface->errors;
  2395. }
  2396. // Fin appel triggers
  2397. $this->db->commit();
  2398. return 1;
  2399. }
  2400. else
  2401. {
  2402. $this->error=$this->db->error()." sql=".$sql;
  2403. dol_syslog("PropaleLigne::delete Error ".$this->error, LOG_ERR);
  2404. $this->db->rollback();
  2405. return -1;
  2406. }
  2407. }
  2408. /**
  2409. * Update propal line object into DB
  2410. *
  2411. * @param int $notrigger 1=Does not execute triggers, 0= execuete triggers
  2412. * @return int <0 if ko, >0 if ok
  2413. */
  2414. function update($notrigger=0)
  2415. {
  2416. global $conf,$langs,$user;
  2417. $error=0;
  2418. // Clean parameters
  2419. if (empty($this->tva_tx)) $this->tva_tx=0;
  2420. if (empty($this->localtax1_tx)) $this->localtax1_tx=0;
  2421. if (empty($this->localtax2_tx)) $this->localtax2_tx=0;
  2422. if (empty($this->total_localtax1)) $this->total_localtax1=0;
  2423. if (empty($this->total_localtax2)) $this->total_localtax2=0;
  2424. if (empty($this->marque_tx)) $this->marque_tx=0;
  2425. if (empty($this->marge_tx)) $this->marge_tx=0;
  2426. if (empty($this->remise)) $this->remise=0;
  2427. if (empty($this->remise_percent)) $this->remise_percent=0;
  2428. if (empty($this->info_bits)) $this->info_bits=0;
  2429. if (empty($this->special_code)) $this->special_code=0;
  2430. if (empty($this->fk_parent_line)) $this->fk_parent_line=0;
  2431. if (empty($this->pa_ht)) $this->pa_ht=0;
  2432. // si prix d'achat non renseign� et utilis� pour calcul des marges alors prix achat = prix vente (idem pour remises)
  2433. if ($this->pa_ht == 0) {
  2434. if ($this->subprice < 0 || ($conf->global->CalculateMarginsOnLinesWithoutBuyingPrice == 1))
  2435. $this->pa_ht = $this->subprice * (1 - $this->remise_percent / 100);
  2436. }
  2437. $this->db->begin();
  2438. // Mise a jour ligne en base
  2439. $sql = "UPDATE ".MAIN_DB_PREFIX."propaldet SET";
  2440. $sql.= " description='".$this->db->escape($this->desc)."'";
  2441. $sql.= " , tva_tx='".price2num($this->tva_tx)."'";
  2442. $sql.= " , localtax1_tx=".price2num($this->localtax1_tx);
  2443. $sql.= " , localtax2_tx=".price2num($this->localtax2_tx);
  2444. $sql.= " , qty='".price2num($this->qty)."'";
  2445. $sql.= " , subprice=".price2num($this->subprice)."";
  2446. $sql.= " , remise_percent=".price2num($this->remise_percent)."";
  2447. $sql.= " , price=".price2num($this->price).""; // TODO A virer
  2448. $sql.= " , remise=".price2num($this->remise).""; // TODO A virer
  2449. $sql.= " , info_bits='".$this->info_bits."'";
  2450. if (empty($this->skip_update_total))
  2451. {
  2452. $sql.= " , total_ht=".price2num($this->total_ht)."";
  2453. $sql.= " , total_tva=".price2num($this->total_tva)."";
  2454. $sql.= " , total_ttc=".price2num($this->total_ttc)."";
  2455. }
  2456. $sql.= " , fk_product_fournisseur_price='".$this->fk_fournprice."'";
  2457. $sql.= " , buy_price_ht='".price2num($this->pa_ht)."'";
  2458. $sql.= " , info_bits=".$this->info_bits;
  2459. if (strlen($this->special_code)) $sql.= " , special_code=".$this->special_code;
  2460. $sql.= " , fk_parent_line=".($this->fk_parent_line>0?$this->fk_parent_line:"null");
  2461. if (! empty($this->rang)) $sql.= ", rang=".$this->rang;
  2462. $sql.= " WHERE rowid = ".$this->rowid;
  2463. dol_syslog(get_class($this)."::update sql=".$sql, LOG_DEBUG);
  2464. $resql=$this->db->query($sql);
  2465. if ($resql)
  2466. {
  2467. if (! $notrigger)
  2468. {
  2469. // Appel des triggers
  2470. include_once(DOL_DOCUMENT_ROOT . "/core/class/interfaces.class.php");
  2471. $interface=new Interfaces($this->db);
  2472. $result = $interface->run_triggers('LINEPROPAL_UPDATE',$this,$user,$langs,$conf);
  2473. if ($result < 0) {
  2474. $error++; $this->errors=$interface->errors;
  2475. }
  2476. // Fin appel triggers
  2477. }
  2478. $this->db->commit();
  2479. return 1;
  2480. }
  2481. else
  2482. {
  2483. $this->error=$this->db->error();
  2484. dol_syslog(get_class($this)."::update Error ".$this->error, LOG_ERR);
  2485. $this->db->rollback();
  2486. return -2;
  2487. }
  2488. }
  2489. /**
  2490. * Update DB line fields total_xxx
  2491. * Used by migration
  2492. *
  2493. * @return int <0 if ko, >0 if ok
  2494. */
  2495. function update_total()
  2496. {
  2497. $this->db->begin();
  2498. // Mise a jour ligne en base
  2499. $sql = "UPDATE ".MAIN_DB_PREFIX."propaldet SET";
  2500. $sql.= " total_ht=".price2num($this->total_ht,'MT')."";
  2501. $sql.= ",total_tva=".price2num($this->total_tva,'MT')."";
  2502. $sql.= ",total_ttc=".price2num($this->total_ttc,'MT')."";
  2503. $sql.= " WHERE rowid = ".$this->rowid;
  2504. dol_syslog("PropaleLigne::update_total sql=$sql");
  2505. $resql=$this->db->query($sql);
  2506. if ($resql)
  2507. {
  2508. $this->db->commit();
  2509. return 1;
  2510. }
  2511. else
  2512. {
  2513. $this->error=$this->db->error();
  2514. dol_syslog("PropaleLigne::update_total Error ".$this->error, LOG_ERR);
  2515. $this->db->rollback();
  2516. return -2;
  2517. }
  2518. }
  2519. }
  2520. ?>