facture-rec.class.php 38 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193
  1. <?php
  2. /* Copyright (C) 2003-2005 Rodolphe Quiedeville <rodolphe@quiedeville.org>
  3. * Copyright (C) 2004-2015 Laurent Destailleur <eldy@users.sourceforge.net>
  4. * Copyright (C) 2009-2012 Regis Houssin <regis.houssin@capnetworks.com>
  5. * Copyright (C) 2010-2011 Juanjo Menent <jmenent@2byte.es>
  6. * Copyright (C) 2012 Cedric Salvador <csalvador@gpcsolutions.fr>
  7. * Copyright (C) 2013 Florian Henry <florian.henry@open-concept.pro>
  8. * Copyright (C) 2015 Marcos García <marcosgdf@gmail.com>
  9. *
  10. * This program is free software; you can redistribute it and/or modify
  11. * it under the terms of the GNU General Public License as published by
  12. * the Free Software Foundation; either version 3 of the License, or
  13. * (at your option) any later version.
  14. *
  15. * This program is distributed in the hope that it will be useful,
  16. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  17. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  18. * GNU General Public License for more details.
  19. *
  20. * You should have received a copy of the GNU General Public License
  21. * along with this program. If not, see <http://www.gnu.org/licenses/>.
  22. */
  23. /**
  24. * \file htdocs/compta/facture/class/facture-rec.class.php
  25. * \ingroup facture
  26. * \brief Fichier de la classe des factures recurentes
  27. */
  28. require_once DOL_DOCUMENT_ROOT.'/core/class/notify.class.php';
  29. require_once DOL_DOCUMENT_ROOT.'/product/class/product.class.php';
  30. require_once DOL_DOCUMENT_ROOT.'/compta/facture/class/facture.class.php';
  31. require_once DOL_DOCUMENT_ROOT.'/core/lib/date.lib.php';
  32. /**
  33. * Classe de gestion des factures recurrentes/Modeles
  34. */
  35. class FactureRec extends CommonInvoice
  36. {
  37. public $element='facturerec';
  38. public $table_element='facture_rec';
  39. public $table_element_line='facturedet_rec';
  40. public $fk_element='fk_facture';
  41. var $number;
  42. var $date;
  43. var $amount;
  44. var $remise;
  45. var $tva;
  46. var $total;
  47. var $db_table;
  48. var $propalid;
  49. var $date_last_gen;
  50. var $date_when;
  51. var $nb_gen_done;
  52. var $nb_gen_max;
  53. var $rang;
  54. var $special_code;
  55. var $usenewprice=0;
  56. /**
  57. * Constructor
  58. *
  59. * @param DoliDB $db Database handler
  60. */
  61. function __construct($db)
  62. {
  63. $this->db = $db;
  64. }
  65. /**
  66. * Create a predefined invoice
  67. *
  68. * @param User $user User object
  69. * @param int $facid Id of source invoice
  70. * @return int <0 if KO, id of invoice created if OK
  71. */
  72. function create($user, $facid)
  73. {
  74. global $conf;
  75. $error=0;
  76. $now=dol_now();
  77. // Clean parameters
  78. $this->titre=trim($this->titre);
  79. $this->usenewprice=empty($this->usenewprice)?0:$this->usenewprice;
  80. // No frequency defined then no next date to execution
  81. if (empty($this->frequency))
  82. {
  83. $this->frequency=0;
  84. $this->date_when=NULL;
  85. }
  86. $this->frequency=abs($this->frequency);
  87. $this->nb_gen_done=0;
  88. $this->nb_gen_max=empty($this->nb_gen_max)?0:$this->nb_gen_max;
  89. $this->auto_validate=empty($this->auto_validate)?0:$this->auto_validate;
  90. $this->db->begin();
  91. // Charge facture modele
  92. $facsrc=new Facture($this->db);
  93. $result=$facsrc->fetch($facid);
  94. if ($result > 0)
  95. {
  96. // On positionne en mode brouillon la facture
  97. $this->brouillon = 1;
  98. $sql = "INSERT INTO ".MAIN_DB_PREFIX."facture_rec (";
  99. $sql.= "titre";
  100. $sql.= ", fk_soc";
  101. $sql.= ", entity";
  102. $sql.= ", datec";
  103. $sql.= ", amount";
  104. $sql.= ", remise";
  105. $sql.= ", note_private";
  106. $sql.= ", note_public";
  107. $sql.= ", fk_user_author";
  108. $sql.= ", fk_projet";
  109. $sql.= ", fk_account";
  110. $sql.= ", fk_cond_reglement";
  111. $sql.= ", fk_mode_reglement";
  112. $sql.= ", usenewprice";
  113. $sql.= ", frequency";
  114. $sql.= ", unit_frequency";
  115. $sql.= ", date_when";
  116. $sql.= ", date_last_gen";
  117. $sql.= ", nb_gen_done";
  118. $sql.= ", nb_gen_max";
  119. $sql.= ", auto_validate";
  120. $sql.= ") VALUES (";
  121. $sql.= "'".$this->titre."'";
  122. $sql.= ", ".$facsrc->socid;
  123. $sql.= ", ".$conf->entity;
  124. $sql.= ", '".$this->db->idate($now)."'";
  125. $sql.= ", ".(!empty($facsrc->amount)?$facsrc->amount:'0');
  126. $sql.= ", ".(!empty($facsrc->remise)?$this->remise:'0');
  127. $sql.= ", ".(!empty($this->note_private)?("'".$this->db->escape($this->note_private)."'"):"NULL");
  128. $sql.= ", ".(!empty($this->note_public)?("'".$this->db->escape($this->note_public)."'"):"NULL");
  129. $sql.= ", '".$user->id."'";
  130. $sql.= ", ".(! empty($facsrc->fk_project)?"'".$facsrc->fk_project."'":"null");
  131. $sql.= ", ".(! empty($facsrc->fk_account)?"'".$facsrc->fk_account."'":"null");
  132. $sql.= ", '".$facsrc->cond_reglement_id."'";
  133. $sql.= ", '".$facsrc->mode_reglement_id."'";
  134. $sql.= ", ".$this->usenewprice;
  135. $sql.= ", ".$this->frequency;
  136. $sql.= ", '".$this->db->escape($this->unit_frequency)."'";
  137. $sql.= ", ".(!empty($this->date_when)?"'".$this->db->idate($this->date_when)."'":'NULL');
  138. $sql.= ", ".(!empty($this->date_last_gen)?"'".$this->db->idate($this->date_last_gen)."'":'NULL');
  139. $sql.= ", ".$this->nb_gen_done;
  140. $sql.= ", ".$this->nb_gen_max;
  141. $sql.= ", ".$this->auto_validate;
  142. $sql.= ")";
  143. if ($this->db->query($sql))
  144. {
  145. $this->id = $this->db->last_insert_id(MAIN_DB_PREFIX."facture_rec");
  146. // Add lines
  147. $num=count($facsrc->lines);
  148. for ($i = 0; $i < $num; $i++)
  149. {
  150. $result_insert = $this->addline(
  151. $facsrc->lines[$i]->desc,
  152. $facsrc->lines[$i]->subprice,
  153. $facsrc->lines[$i]->qty,
  154. $facsrc->lines[$i]->tva_tx,
  155. $facsrc->lines[$i]->fk_product,
  156. $facsrc->lines[$i]->remise_percent,
  157. 'HT',
  158. 0,
  159. '',
  160. 0,
  161. $facsrc->lines[$i]->product_type,
  162. $facsrc->lines[$i]->rang,
  163. $facsrc->lines[$i]->special_code,
  164. $facsrc->lines[$i]->label,
  165. $facsrc->lines[$i]->fk_unit
  166. );
  167. if ($result_insert < 0)
  168. {
  169. $error++;
  170. }
  171. }
  172. // Add object linked
  173. if (! $error && $this->id && is_array($this->linked_objects) && ! empty($this->linked_objects))
  174. {
  175. foreach($this->linked_objects as $origin => $origin_id)
  176. {
  177. $ret = $this->add_object_linked($origin, $origin_id);
  178. if (! $ret)
  179. {
  180. $this->error=$this->db->lasterror();
  181. $error++;
  182. }
  183. }
  184. }
  185. if ($error)
  186. {
  187. $this->db->rollback();
  188. }
  189. else
  190. {
  191. $this->db->commit();
  192. return $this->id;
  193. }
  194. }
  195. else
  196. {
  197. $this->error=$this->db->lasterror();
  198. $this->db->rollback();
  199. return -2;
  200. }
  201. }
  202. else
  203. {
  204. $this->db->rollback();
  205. return -1;
  206. }
  207. }
  208. /**
  209. * Load object and lines
  210. *
  211. * @param int $rowid Id of object to load
  212. * @param string $ref Reference of recurring invoice
  213. * @param string $ref_ext External reference of invoice
  214. * @param int $ref_int Internal reference of other object
  215. * @return int >0 if OK, <0 if KO, 0 if not found
  216. */
  217. function fetch($rowid, $ref='', $ref_ext='', $ref_int='')
  218. {
  219. $sql = 'SELECT f.rowid, f.titre, f.fk_soc, f.amount, f.tva, f.total, f.total_ttc, f.remise_percent, f.remise_absolue, f.remise';
  220. $sql.= ', f.date_lim_reglement as dlr';
  221. $sql.= ', f.note_private, f.note_public, f.fk_user_author';
  222. $sql.= ', f.fk_mode_reglement, f.fk_cond_reglement, f.fk_projet';
  223. $sql.= ', f.fk_account';
  224. $sql.= ', f.frequency, f.unit_frequency, f.date_when, f.date_last_gen, f.nb_gen_done, f.nb_gen_max, f.usenewprice, f.auto_validate';
  225. $sql.= ', p.code as mode_reglement_code, p.libelle as mode_reglement_libelle';
  226. $sql.= ', c.code as cond_reglement_code, c.libelle as cond_reglement_libelle, c.libelle_facture as cond_reglement_libelle_doc';
  227. //$sql.= ', el.fk_source';
  228. $sql.= ' FROM '.MAIN_DB_PREFIX.'facture_rec as f';
  229. $sql.= ' LEFT JOIN '.MAIN_DB_PREFIX.'c_payment_term as c ON f.fk_cond_reglement = c.rowid';
  230. $sql.= ' LEFT JOIN '.MAIN_DB_PREFIX.'c_paiement as p ON f.fk_mode_reglement = p.id';
  231. //$sql.= " LEFT JOIN ".MAIN_DB_PREFIX."element_element as el ON el.fk_target = f.rowid AND el.targettype = 'facture'";
  232. if ($rowid) $sql.= ' WHERE f.rowid='.$rowid;
  233. elseif ($ref) $sql.= " WHERE f.titre='".$this->db->escape($ref)."'";
  234. /* This field are not used for template invoice
  235. if ($ref_ext) $sql.= " AND f.ref_ext='".$this->db->escape($ref_ext)."'";
  236. if ($ref_int) $sql.= " AND f.ref_int='".$this->db->escape($ref_int)."'";
  237. */
  238. $result = $this->db->query($sql);
  239. if ($result)
  240. {
  241. if ($this->db->num_rows($result))
  242. {
  243. $obj = $this->db->fetch_object($result);
  244. $this->id = $obj->rowid;
  245. $this->titre = $obj->titre;
  246. $this->ref = $obj->titre;
  247. $this->ref_client = $obj->ref_client;
  248. $this->type = $obj->type;
  249. $this->datep = $obj->dp;
  250. $this->date = $obj->df;
  251. $this->amount = $obj->amount;
  252. $this->remise_percent = $obj->remise_percent;
  253. $this->remise_absolue = $obj->remise_absolue;
  254. $this->remise = $obj->remise;
  255. $this->total_ht = $obj->total;
  256. $this->total_tva = $obj->tva;
  257. $this->total_ttc = $obj->total_ttc;
  258. $this->paye = $obj->paye;
  259. $this->close_code = $obj->close_code;
  260. $this->close_note = $obj->close_note;
  261. $this->socid = $obj->fk_soc;
  262. $this->statut = $obj->fk_statut;
  263. $this->date_lim_reglement = $this->db->jdate($obj->dlr);
  264. $this->mode_reglement_id = $obj->fk_mode_reglement;
  265. $this->mode_reglement_code = $obj->mode_reglement_code;
  266. $this->mode_reglement = $obj->mode_reglement_libelle;
  267. $this->cond_reglement_id = $obj->fk_cond_reglement;
  268. $this->cond_reglement_code = $obj->cond_reglement_code;
  269. $this->cond_reglement = $obj->cond_reglement_libelle;
  270. $this->cond_reglement_doc = $obj->cond_reglement_libelle_doc;
  271. $this->fk_project = $obj->fk_projet;
  272. $this->fk_account = $obj->fk_account;
  273. $this->fk_facture_source = $obj->fk_facture_source;
  274. $this->note_private = $obj->note_private;
  275. $this->note_public = $obj->note_public;
  276. $this->user_author = $obj->fk_user_author;
  277. $this->modelpdf = $obj->model_pdf;
  278. $this->rang = $obj->rang;
  279. $this->special_code = $obj->special_code;
  280. $this->frequency = $obj->frequency;
  281. $this->unit_frequency = $obj->unit_frequency;
  282. $this->date_when = $this->db->jdate($obj->date_when);
  283. $this->date_last_gen = $this->db->jdate($obj->date_last_gen);
  284. $this->nb_gen_done = $obj->nb_gen_done;
  285. $this->nb_gen_max = $obj->nb_gen_max;
  286. $this->usenewprice = $obj->usenewprice;
  287. $this->auto_validate = $obj->auto_validate;
  288. if ($this->statut == self::STATUS_DRAFT) $this->brouillon = 1;
  289. /*
  290. * Lines
  291. */
  292. $result=$this->fetch_lines();
  293. if ($result < 0)
  294. {
  295. $this->error=$this->db->lasterror();
  296. return -3;
  297. }
  298. return 1;
  299. }
  300. else
  301. {
  302. $this->error='Bill with id '.$rowid.' or ref '.$ref.' not found sql='.$sql;
  303. dol_syslog('Facture::Fetch Error '.$this->error, LOG_ERR);
  304. return -2;
  305. }
  306. }
  307. else
  308. {
  309. $this->error=$this->db->error();
  310. return -1;
  311. }
  312. }
  313. /**
  314. * Create an array of invoice lines
  315. *
  316. * @return int >0 if OK, <0 if KO
  317. */
  318. function getLinesArray()
  319. {
  320. return $this->fetch_lines();
  321. }
  322. /**
  323. * Recupere les lignes de factures predefinies dans this->lines
  324. *
  325. * @return int 1 if OK, < 0 if KO
  326. */
  327. function fetch_lines()
  328. {
  329. $this->lines=array();
  330. $sql = 'SELECT l.rowid, l.fk_product, l.product_type, l.label as custom_label, l.description, l.product_type, l.price, l.qty, l.tva_tx, ';
  331. $sql.= ' l.remise, l.remise_percent, l.subprice,';
  332. $sql.= ' l.info_bits, l.total_ht, l.total_tva, l.total_ttc,';
  333. //$sql.= ' l.situation_percent, l.fk_prev_id,';
  334. //$sql.= ' l.localtax1_tx, l.localtax2_tx, l.localtax1_type, l.localtax2_type, l.remise_percent, l.fk_remise_except, l.subprice,';
  335. $sql.= ' l.rang, l.special_code,';
  336. //$sql.= ' l.info_bits, l.total_ht, l.total_tva, l.total_localtax1, l.total_localtax2, l.total_ttc, l.fk_code_ventilation, l.fk_product_fournisseur_price as fk_fournprice, l.buy_price_ht as pa_ht,';
  337. $sql.= ' l.fk_unit, l.fk_contract_line,';
  338. //$sql.= ' l.fk_multicurrency, l.multicurrency_code, l.multicurrency_subprice, l.multicurrency_total_ht, l.multicurrency_total_tva, l.multicurrency_total_ttc,';
  339. $sql.= ' p.ref as product_ref, p.fk_product_type as fk_product_type, p.label as product_label, p.description as product_desc';
  340. $sql.= ' FROM '.MAIN_DB_PREFIX.'facturedet_rec as l';
  341. $sql.= ' LEFT JOIN '.MAIN_DB_PREFIX.'product as p ON l.fk_product = p.rowid';
  342. $sql.= ' WHERE l.fk_facture = '.$this->id;
  343. $sql.= ' ORDER BY l.rang';
  344. dol_syslog('FactureRec::fetch_lines', LOG_DEBUG);
  345. $result = $this->db->query($sql);
  346. if ($result)
  347. {
  348. $num = $this->db->num_rows($result);
  349. $i = 0;
  350. while ($i < $num)
  351. {
  352. $objp = $this->db->fetch_object($result);
  353. $line = new FactureLigne($this->db);
  354. $line->id = $objp->rowid;
  355. $line->rowid = $objp->rowid;
  356. $line->label = $objp->custom_label; // Label line
  357. $line->desc = $objp->description; // Description line
  358. $line->description = $objp->description; // Description line
  359. $line->product_type = $objp->product_type; // Type of line
  360. $line->ref = $objp->product_ref; // Ref product
  361. $line->product_ref = $objp->product_ref; // Ref product
  362. $line->libelle = $objp->product_label; // deprecated
  363. $line->product_label = $objp->product_label; // Label product
  364. $line->product_desc = $objp->product_desc; // Description product
  365. $line->fk_product_type = $objp->fk_product_type; // Type of product
  366. $line->qty = $objp->qty;
  367. $line->subprice = $objp->subprice;
  368. $line->tva_tx = $objp->tva_tx;
  369. $line->remise_percent = $objp->remise_percent;
  370. $line->fk_remise_except = $objp->fk_remise_except;
  371. $line->fk_product = $objp->fk_product;
  372. $line->info_bits = $objp->info_bits;
  373. $line->total_ht = $objp->total_ht;
  374. $line->total_tva = $objp->total_tva;
  375. $line->total_ttc = $objp->total_ttc;
  376. $line->code_ventilation = $objp->fk_code_ventilation;
  377. $line->rang = $objp->rang;
  378. $line->special_code = $objp->special_code;
  379. $line->fk_unit = $objp->fk_unit;
  380. $line->fk_contract_line = $objp->fk_contract_line;
  381. // Ne plus utiliser
  382. $line->price = $objp->price;
  383. $line->remise = $objp->remise;
  384. $this->lines[$i] = $line;
  385. $i++;
  386. }
  387. $this->db->free($result);
  388. return 1;
  389. }
  390. else
  391. {
  392. $this->error=$this->db-lasterror();
  393. return -3;
  394. }
  395. }
  396. /**
  397. * Delete template invoice
  398. *
  399. * @param int $rowid Id of invoice to delete. If empty, we delete current instance of invoice
  400. * @param int $notrigger 1=Does not execute triggers, 0= execute triggers
  401. * @param int $idwarehouse Id warehouse to use for stock change.
  402. * @return int <0 if KO, >0 if OK
  403. */
  404. function delete($rowid=0, $notrigger=0, $idwarehouse=-1)
  405. {
  406. if (empty($rowid)) $rowid=$this->id;
  407. dol_syslog(get_class($this)."::delete rowid=".$rowid, LOG_DEBUG);
  408. $error=0;
  409. $this->db->begin();
  410. $sql = "DELETE FROM ".MAIN_DB_PREFIX."facturedet_rec WHERE fk_facture = ".$rowid;
  411. dol_syslog($sql);
  412. if ($this->db->query($sql))
  413. {
  414. $sql = "DELETE FROM ".MAIN_DB_PREFIX."facture_rec WHERE rowid = ".$rowid;
  415. dol_syslog($sql);
  416. if (! $this->db->query($sql))
  417. {
  418. $this->error=$this->db->lasterror();
  419. $error=-1;
  420. }
  421. }
  422. else
  423. {
  424. $this->error=$this->db->lasterror();
  425. $error=-2;
  426. }
  427. if (! $error)
  428. {
  429. $this->db->commit();
  430. return 1;
  431. }
  432. else
  433. {
  434. $this->db->rollback();
  435. return $error;
  436. }
  437. }
  438. /**
  439. * Add a line to invoice
  440. *
  441. * @param string $desc Description de la ligne
  442. * @param double $pu_ht Prix unitaire HT (> 0 even for credit note)
  443. * @param double $qty Quantite
  444. * @param double $txtva Taux de tva force, sinon -1
  445. * @param int $fk_product Id du produit/service predefini
  446. * @param double $remise_percent Pourcentage de remise de la ligne
  447. * @param string $price_base_type HT or TTC
  448. * @param int $info_bits Bits de type de lignes
  449. * @param int $fk_remise_except Id remise
  450. * @param double $pu_ttc Prix unitaire TTC (> 0 even for credit note)
  451. * @param int $type Type of line (0=product, 1=service)
  452. * @param int $rang Position of line
  453. * @param int $special_code Special code
  454. * @param string $label Label of the line
  455. * @param string $fk_unit Unit
  456. * @return int <0 if KO, Id of line if OK
  457. */
  458. function addline($desc, $pu_ht, $qty, $txtva, $fk_product=0, $remise_percent=0, $price_base_type='HT', $info_bits=0, $fk_remise_except='', $pu_ttc=0, $type=0, $rang=-1, $special_code=0, $label='', $fk_unit=null)
  459. {
  460. global $mysoc;
  461. $facid=$this->id;
  462. dol_syslog(get_class($this)."::addline facid=$facid,desc=$desc,pu_ht=$pu_ht,qty=$qty,txtva=$txtva,fk_product=$fk_product,remise_percent=$remise_percent,info_bits=$info_bits,fk_remise_except=$fk_remise_except,price_base_type=$price_base_type,pu_ttc=$pu_ttc,type=$type,fk_unit=$fk_unit", LOG_DEBUG);
  463. include_once DOL_DOCUMENT_ROOT.'/core/lib/price.lib.php';
  464. // Check parameters
  465. if ($type < 0) return -1;
  466. if ($this->brouillon)
  467. {
  468. // Clean parameters
  469. $remise_percent=price2num($remise_percent);
  470. $qty=price2num($qty);
  471. if (! $qty) $qty=1;
  472. if (! $info_bits) $info_bits=0;
  473. $pu_ht=price2num($pu_ht);
  474. $pu_ttc=price2num($pu_ttc);
  475. $txtva=price2num($txtva);
  476. if ($price_base_type=='HT')
  477. {
  478. $pu=$pu_ht;
  479. }
  480. else
  481. {
  482. $pu=$pu_ttc;
  483. }
  484. // Calcul du total TTC et de la TVA pour la ligne a partir de
  485. // qty, pu, remise_percent et txtva
  486. // TRES IMPORTANT: C'est au moment de l'insertion ligne qu'on doit stocker
  487. // la part ht, tva et ttc, et ce au niveau de la ligne qui a son propre taux tva.
  488. $tabprice=calcul_price_total($qty, $pu, $remise_percent, $txtva, 0, 0, 0, $price_base_type, $info_bits, $type, $mysoc);
  489. $total_ht = $tabprice[0];
  490. $total_tva = $tabprice[1];
  491. $total_ttc = $tabprice[2];
  492. $product_type=$type;
  493. if ($fk_product)
  494. {
  495. $product=new Product($this->db);
  496. $result=$product->fetch($fk_product);
  497. $product_type=$product->type;
  498. }
  499. $sql = "INSERT INTO ".MAIN_DB_PREFIX."facturedet_rec (";
  500. $sql.= "fk_facture";
  501. $sql.= ", label";
  502. $sql.= ", description";
  503. $sql.= ", price";
  504. $sql.= ", qty";
  505. $sql.= ", tva_tx";
  506. $sql.= ", fk_product";
  507. $sql.= ", product_type";
  508. $sql.= ", remise_percent";
  509. $sql.= ", subprice";
  510. $sql.= ", remise";
  511. $sql.= ", total_ht";
  512. $sql.= ", total_tva";
  513. $sql.= ", total_ttc";
  514. $sql.= ", rang";
  515. $sql.= ", special_code";
  516. $sql.= ", fk_unit";
  517. $sql.= ") VALUES (";
  518. $sql.= "'".$facid."'";
  519. $sql.= ", ".(! empty($label)?"'".$this->db->escape($label)."'":"null");
  520. $sql.= ", '".$this->db->escape($desc)."'";
  521. $sql.= ", ".price2num($pu_ht);
  522. $sql.= ", ".price2num($qty);
  523. $sql.= ", ".price2num($txtva);
  524. $sql.= ", ".(! empty($fk_product)?"'".$fk_product."'":"null");
  525. $sql.= ", ".$product_type;
  526. $sql.= ", '".price2num($remise_percent)."'";
  527. $sql.= ", '".price2num($pu_ht)."'";
  528. $sql.= ", null";
  529. $sql.= ", '".price2num($total_ht)."'";
  530. $sql.= ", '".price2num($total_tva)."'";
  531. $sql.= ", '".price2num($total_ttc)."'";
  532. $sql.= ", ".$rang;
  533. $sql.= ", ".$special_code;
  534. $sql.= ", ".($fk_unit?"'".$this->db->escape($fk_unit)."'":"null").")";
  535. dol_syslog(get_class($this)."::addline", LOG_DEBUG);
  536. if ($this->db->query($sql))
  537. {
  538. $this->id=$facid;
  539. $this->update_price();
  540. return 1;
  541. }
  542. else
  543. {
  544. $this->error=$this->db->lasterror();
  545. return -1;
  546. }
  547. }
  548. }
  549. /**
  550. * Update a line to invoice
  551. *
  552. * @param int $rowid Id of line to update
  553. * @param string $desc Description de la ligne
  554. * @param double $pu_ht Prix unitaire HT (> 0 even for credit note)
  555. * @param double $qty Quantite
  556. * @param double $txtva Taux de tva force, sinon -1
  557. * @param int $fk_product Id du produit/service predefini
  558. * @param double $remise_percent Pourcentage de remise de la ligne
  559. * @param string $price_base_type HT or TTC
  560. * @param int $info_bits Bits de type de lignes
  561. * @param int $fk_remise_except Id remise
  562. * @param double $pu_ttc Prix unitaire TTC (> 0 even for credit note)
  563. * @param int $type Type of line (0=product, 1=service)
  564. * @param int $rang Position of line
  565. * @param int $special_code Special code
  566. * @param string $label Label of the line
  567. * @param string $fk_unit Unit
  568. * @return int <0 if KO, Id of line if OK
  569. */
  570. function updateline($rowid, $desc, $pu_ht, $qty, $txtva, $fk_product=0, $remise_percent=0, $price_base_type='HT', $info_bits=0, $fk_remise_except='', $pu_ttc=0, $type=0, $rang=-1, $special_code=0, $label='', $fk_unit=null)
  571. {
  572. global $mysoc;
  573. $facid=$this->id;
  574. dol_syslog(get_class($this)."::updateline facid=".$facid." rowid=$rowid,desc=$desc,pu_ht=$pu_ht,qty=$qty,txtva=$txtva,fk_product=$fk_product,remise_percent=$remise_percent,info_bits=$info_bits,fk_remise_except=$fk_remise_except,price_base_type=$price_base_type,pu_ttc=$pu_ttc,type=$type,fk_unit=$fk_unit", LOG_DEBUG);
  575. include_once DOL_DOCUMENT_ROOT.'/core/lib/price.lib.php';
  576. // Check parameters
  577. if ($type < 0) return -1;
  578. if ($this->brouillon)
  579. {
  580. // Clean parameters
  581. $remise_percent=price2num($remise_percent);
  582. $qty=price2num($qty);
  583. if (! $qty) $qty=1;
  584. if (! $info_bits) $info_bits=0;
  585. $pu_ht=price2num($pu_ht);
  586. $pu_ttc=price2num($pu_ttc);
  587. $txtva=price2num($txtva);
  588. if ($price_base_type=='HT')
  589. {
  590. $pu=$pu_ht;
  591. }
  592. else
  593. {
  594. $pu=$pu_ttc;
  595. }
  596. // Calcul du total TTC et de la TVA pour la ligne a partir de
  597. // qty, pu, remise_percent et txtva
  598. // TRES IMPORTANT: C'est au moment de l'insertion ligne qu'on doit stocker
  599. // la part ht, tva et ttc, et ce au niveau de la ligne qui a son propre taux tva.
  600. $tabprice=calcul_price_total($qty, $pu, $remise_percent, $txtva, 0, 0, 0, $price_base_type, $info_bits, $type, $mysoc);
  601. $total_ht = $tabprice[0];
  602. $total_tva = $tabprice[1];
  603. $total_ttc = $tabprice[2];
  604. $product_type=$type;
  605. if ($fk_product)
  606. {
  607. $product=new Product($this->db);
  608. $result=$product->fetch($fk_product);
  609. $product_type=$product->type;
  610. }
  611. $sql = "UPDATE ".MAIN_DB_PREFIX."facturedet_rec SET ";
  612. $sql.= "fk_facture = '".$facid."'";
  613. $sql.= ", label=".(! empty($label)?"'".$this->db->escape($label)."'":"null");
  614. $sql.= ", description='".$this->db->escape($desc)."'";
  615. $sql.= ", price=".price2num($pu_ht);
  616. $sql.= ", qty=".price2num($qty);
  617. $sql.= ", tva_tx=".price2num($txtva);
  618. $sql.= ", fk_product=".(! empty($fk_product)?"'".$fk_product."'":"null");
  619. $sql.= ", product_type=".$product_type;
  620. $sql.= ", remise_percent='".price2num($remise_percent)."'";
  621. $sql.= ", subprice='".price2num($pu_ht)."'";
  622. $sql.= ", total_ht='".price2num($total_ht)."'";
  623. $sql.= ", total_tva='".price2num($total_tva)."'";
  624. $sql.= ", total_ttc='".price2num($total_ttc)."'";
  625. $sql.= ", rang=".$rang;
  626. $sql.= ", special_code=".$special_code;
  627. $sql.= ", fk_unit=".($fk_unit?"'".$this->db->escape($fk_unit)."'":"null");
  628. $sql.= " WHERE rowid = ".$rowid;
  629. dol_syslog(get_class($this)."::updateline", LOG_DEBUG);
  630. if ($this->db->query($sql))
  631. {
  632. $this->id=$facid;
  633. $this->update_price();
  634. return 1;
  635. }
  636. else
  637. {
  638. $this->error=$this->db->lasterror();
  639. return -1;
  640. }
  641. }
  642. }
  643. /**
  644. * Return the next date of
  645. *
  646. * @return timestamp false if KO, timestamp if OK
  647. */
  648. function getNextDate()
  649. {
  650. if (empty($this->date_when)) return false;
  651. return dol_time_plus_duree($this->date_when, $this->frequency, $this->unit_frequency);
  652. }
  653. /**
  654. * Create all recurrents invoices.
  655. * A result may also be provided into this->output
  656. *
  657. * @return int 0 if OK, < 0 if KO (this function is used also by cron so only 0 is OK)
  658. */
  659. function createRecurringInvoices()
  660. {
  661. global $langs, $db, $user;
  662. $langs->load("bills");
  663. $nb_create=0;
  664. $now = dol_now();
  665. $tmparray=dol_getdate($now);
  666. $today = dol_mktime(23,59,59,$tmparray['mon'],$tmparray['mday'],$tmparray['year']); // Today is last second of current day
  667. dol_syslog("createRecurringInvoices");
  668. $sql = 'SELECT rowid FROM '.MAIN_DB_PREFIX.'facture_rec';
  669. $sql.= ' WHERE frequency > 0'; // A recurring invoice is an invoice with a frequency
  670. $sql.= " AND (date_when IS NULL OR date_when <= '".$db->idate($today)."')";
  671. $sql.= ' AND (nb_gen_done < nb_gen_max OR nb_gen_max = 0)';
  672. //print $sql;exit;
  673. $resql = $db->query($sql);
  674. if ($resql)
  675. {
  676. $i=0;
  677. $num = $db->num_rows($resql);
  678. if ($num) $this->output.=$langs->trans("FoundXQualifiedRecurringInvoiceTemplate", $num)."\n";
  679. else $this->output.=$langs->trans("NoQualifiedRecurringInvoiceTemplateFound");
  680. while ($i < $num)
  681. {
  682. $line = $db->fetch_object($resql);
  683. $db->begin();
  684. $facturerec = new FactureRec($db);
  685. $facturerec->fetch($line->rowid);
  686. dol_syslog("createRecurringInvoices Process invoice template id=".$facturerec->id.", ref=".$facturerec->ref);
  687. $error=0;
  688. $facture = new Facture($db);
  689. $facture->fac_rec = $facturerec->id; // We will create $facture from this recurring invoice
  690. $facture->type = self::TYPE_STANDARD;
  691. $facture->brouillon = 1;
  692. $facture->date = $facturerec->date_when; // We could also use dol_now here but we prefer date_when so invoice has real date when we would like even if we generate later.
  693. $facture->socid = $facturerec->socid;
  694. $invoiceidgenerated = $facture->create($user); // This will also update fields of recurring invoice
  695. if ($invoiceidgenerated <= 0)
  696. {
  697. $this->errors = $facture->errors;
  698. $this->error = $facture->error;
  699. $error++;
  700. }
  701. if (! $error && $facturerec->auto_validate)
  702. {
  703. $result = $facture->validate($user);
  704. if ($result <= 0)
  705. {
  706. $this->errors = $facture->errors;
  707. $this->error = $facture->error;
  708. $error++;
  709. }
  710. }
  711. if (! $error && $invoiceidgenerated >= 0)
  712. {
  713. $db->commit();
  714. dol_syslog("createRecurringInvoices Process invoice template ".$facturerec->ref." is finished with a success generation");
  715. $nb_create++;
  716. $this->output.=$langs->trans("InvoiceGeneratedFromTemplate", $facture->ref, $facturerec->ref)."\n";
  717. }
  718. else
  719. {
  720. $db->rollback();
  721. }
  722. $i++;
  723. }
  724. }
  725. else dol_print_error($db);
  726. $this->output=trim($this->output);
  727. return $error?$error:0;
  728. }
  729. /**
  730. * Return clicable name (with picto eventually)
  731. *
  732. * @param int $withpicto Add picto into link
  733. * @param string $option Where point the link
  734. * @param int $max Maxlength of ref
  735. * @param int $short 1=Return just URL
  736. * @param string $moretitle Add more text to title tooltip
  737. * @return string String with URL
  738. */
  739. function getNomUrl($withpicto=0,$option='',$max=0,$short=0,$moretitle='')
  740. {
  741. global $langs;
  742. $result='';
  743. $label=$langs->trans("ShowInvoice").': '.$this->ref;
  744. $url = DOL_URL_ROOT.'/compta/facture/fiche-rec.php?facid='.$this->id;
  745. if ($short) return $url;
  746. $picto='bill';
  747. $link = '<a href="'.$url.'" title="'.dol_escape_htmltag($label, 1).'" class="classfortooltip">';
  748. $linkend='</a>';
  749. if ($withpicto) $result.=($link.img_object($label, $picto, 'class="classfortooltip"').$linkend);
  750. if ($withpicto && $withpicto != 2) $result.=' ';
  751. if ($withpicto != 2) $result.=$link.$this->ref.$linkend;
  752. return $result;
  753. }
  754. /**
  755. * Initialise an instance with random values.
  756. * Used to build previews or test instances.
  757. * id must be 0 if object instance is a specimen.
  758. *
  759. * @param string $option ''=Create a specimen invoice with lines, 'nolines'=No lines
  760. * @return void
  761. */
  762. function initAsSpecimen($option='')
  763. {
  764. global $user,$langs,$conf;
  765. $now=dol_now();
  766. $arraynow=dol_getdate($now);
  767. $nownotime=dol_mktime(0, 0, 0, $arraynow['mon'], $arraynow['mday'], $arraynow['year']);
  768. // Load array of products prodids
  769. $num_prods = 0;
  770. $prodids = array();
  771. $sql = "SELECT rowid";
  772. $sql.= " FROM ".MAIN_DB_PREFIX."product";
  773. $sql.= " WHERE entity IN (".getEntity('product', 1).")";
  774. $resql = $this->db->query($sql);
  775. if ($resql)
  776. {
  777. $num_prods = $this->db->num_rows($resql);
  778. $i = 0;
  779. while ($i < $num_prods)
  780. {
  781. $i++;
  782. $row = $this->db->fetch_row($resql);
  783. $prodids[$i] = $row[0];
  784. }
  785. }
  786. // Initialize parameters
  787. $this->id=0;
  788. $this->ref = 'SPECIMEN';
  789. $this->specimen=1;
  790. $this->socid = 1;
  791. $this->date = $nownotime;
  792. $this->date_lim_reglement = $nownotime + 3600 * 24 *30;
  793. $this->cond_reglement_id = 1;
  794. $this->cond_reglement_code = 'RECEP';
  795. $this->date_lim_reglement=$this->calculate_date_lim_reglement();
  796. $this->mode_reglement_id = 0; // Not forced to show payment mode CHQ + VIR
  797. $this->mode_reglement_code = ''; // Not forced to show payment mode CHQ + VIR
  798. $this->note_public='This is a comment (public)';
  799. $this->note_private='This is a comment (private)';
  800. $this->note='This is a comment (private)';
  801. $this->fk_incoterms=0;
  802. $this->location_incoterms='';
  803. if (empty($option) || $option != 'nolines')
  804. {
  805. // Lines
  806. $nbp = 5;
  807. $xnbp = 0;
  808. while ($xnbp < $nbp)
  809. {
  810. $line=new FactureLigne($this->db);
  811. $line->desc=$langs->trans("Description")." ".$xnbp;
  812. $line->qty=1;
  813. $line->subprice=100;
  814. $line->tva_tx=19.6;
  815. $line->localtax1_tx=0;
  816. $line->localtax2_tx=0;
  817. $line->remise_percent=0;
  818. if ($xnbp == 1) // Qty is negative (product line)
  819. {
  820. $prodid = mt_rand(1, $num_prods);
  821. $line->fk_product=$prodids[$prodid];
  822. $line->qty=-1;
  823. $line->total_ht=-100;
  824. $line->total_ttc=-119.6;
  825. $line->total_tva=-19.6;
  826. }
  827. else if ($xnbp == 2) // UP is negative (free line)
  828. {
  829. $line->subprice=-100;
  830. $line->total_ht=-100;
  831. $line->total_ttc=-119.6;
  832. $line->total_tva=-19.6;
  833. $line->remise_percent=0;
  834. }
  835. else if ($xnbp == 3) // Discount is 50% (product line)
  836. {
  837. $prodid = mt_rand(1, $num_prods);
  838. $line->fk_product=$prodids[$prodid];
  839. $line->total_ht=50;
  840. $line->total_ttc=59.8;
  841. $line->total_tva=9.8;
  842. $line->remise_percent=50;
  843. }
  844. else // (product line)
  845. {
  846. $prodid = mt_rand(1, $num_prods);
  847. $line->fk_product=$prodids[$prodid];
  848. $line->total_ht=100;
  849. $line->total_ttc=119.6;
  850. $line->total_tva=19.6;
  851. $line->remise_percent=00;
  852. }
  853. $this->lines[$xnbp]=$line;
  854. $xnbp++;
  855. $this->total_ht += $line->total_ht;
  856. $this->total_tva += $line->total_tva;
  857. $this->total_ttc += $line->total_ttc;
  858. }
  859. $this->revenuestamp = 0;
  860. // Add a line "offered"
  861. $line=new FactureLigne($this->db);
  862. $line->desc=$langs->trans("Description")." (offered line)";
  863. $line->qty=1;
  864. $line->subprice=100;
  865. $line->tva_tx=19.6;
  866. $line->localtax1_tx=0;
  867. $line->localtax2_tx=0;
  868. $line->remise_percent=100;
  869. $line->total_ht=0;
  870. $line->total_ttc=0; // 90 * 1.196
  871. $line->total_tva=0;
  872. $prodid = mt_rand(1, $num_prods);
  873. $line->fk_product=$prodids[$prodid];
  874. $this->lines[$xnbp]=$line;
  875. $xnbp++;
  876. }
  877. $this->usenewprice = 1;
  878. }
  879. /**
  880. * Function used to replace a thirdparty id with another one.
  881. *
  882. * @param DoliDB $db Database handler
  883. * @param int $origin_id Old thirdparty id
  884. * @param int $dest_id New thirdparty id
  885. * @return bool
  886. */
  887. public static function replaceThirdparty(DoliDB $db, $origin_id, $dest_id)
  888. {
  889. $tables = array(
  890. 'facture_rec'
  891. );
  892. return CommonObject::commonReplaceThirdparty($db, $origin_id, $dest_id, $tables);
  893. }
  894. /**
  895. * Update frequency and unit
  896. *
  897. * @param int $frequency value of frequency
  898. * @param string $unit unit of frequency (d, m, y)
  899. * @return int <0 if KO, >0 if OK
  900. */
  901. function setFrequencyAndUnit($frequency,$unit)
  902. {
  903. if (! $this->table_element)
  904. {
  905. dol_syslog(get_class($this)."::setFrequencyAndUnit was called on objet with property table_element not defined",LOG_ERR);
  906. return -1;
  907. }
  908. if (!empty($frequency) && empty($unit))
  909. {
  910. dol_syslog(get_class($this)."::setFrequencyAndUnit was called on objet with params frequency defined but unit not defined",LOG_ERR);
  911. return -2;
  912. }
  913. $sql = 'UPDATE '.MAIN_DB_PREFIX.$this->table_element;
  914. $sql.= ' SET frequency = '.($frequency?$this->db->escape($frequency):'null');
  915. if (!empty($unit))
  916. {
  917. $sql.= ', unit_frequency = "'.$this->db->escape($unit).'"';
  918. }
  919. $sql.= ' WHERE rowid = '.$this->id;
  920. dol_syslog(get_class($this)."::setFrequencyAndUnit", LOG_DEBUG);
  921. if ($this->db->query($sql))
  922. {
  923. $this->frequency = $frequency;
  924. if (!empty($unit)) $this->unit_frequency = $unit;
  925. return 1;
  926. }
  927. else
  928. {
  929. dol_print_error($this->db);
  930. return -1;
  931. }
  932. }
  933. /**
  934. * Update the next date of execution
  935. *
  936. * @param datetime $date date of execution
  937. * @param int $increment_nb_gen_done 0 do nothing more, >0 increment nb_gen_done
  938. * @return int <0 if KO, >0 if OK
  939. */
  940. function setNextDate($date, $increment_nb_gen_done=0)
  941. {
  942. if (! $this->table_element)
  943. {
  944. dol_syslog(get_class($this)."::setNextDate was called on objet with property table_element not defined",LOG_ERR);
  945. return -1;
  946. }
  947. $sql = 'UPDATE '.MAIN_DB_PREFIX.$this->table_element;
  948. $sql.= ' SET date_when = "'.$this->db->idate($date).'"';
  949. if ($increment_nb_gen_done>0) $sql.= ', nb_gen_done = nb_gen_done + 1';
  950. $sql.= ' WHERE rowid = '.$this->id;
  951. dol_syslog(get_class($this)."::setNextDate", LOG_DEBUG);
  952. if ($this->db->query($sql))
  953. {
  954. $this->date_when = $date;
  955. if ($increment_nb_gen_done>0) $this->nb_gen_done++;
  956. return 1;
  957. }
  958. else
  959. {
  960. dol_print_error($this->db);
  961. return -1;
  962. }
  963. }
  964. /**
  965. * Update the maximum period
  966. *
  967. * @param int $nb number of maximum period
  968. * @return int <0 if KO, >0 if OK
  969. */
  970. function setMaxPeriod($nb)
  971. {
  972. if (! $this->table_element)
  973. {
  974. dol_syslog(get_class($this)."::setMaxPeriod was called on objet with property table_element not defined",LOG_ERR);
  975. return -1;
  976. }
  977. if (empty($nb)) $nb=0;
  978. $sql = 'UPDATE '.MAIN_DB_PREFIX.$this->table_element;
  979. $sql.= ' SET nb_gen_max = '.$nb;
  980. $sql.= ' WHERE rowid = '.$this->id;
  981. dol_syslog(get_class($this)."::setMaxPeriod", LOG_DEBUG);
  982. if ($this->db->query($sql))
  983. {
  984. $this->nb_gen_max = $nb;
  985. return 1;
  986. }
  987. else
  988. {
  989. dol_print_error($this->db);
  990. return -1;
  991. }
  992. }
  993. /**
  994. * Update the auto validate invoice
  995. *
  996. * @param int $validate 0 to create in draft, 1 to create and validate invoice
  997. * @return int <0 if KO, >0 if OK
  998. */
  999. function setAutoValidate($validate)
  1000. {
  1001. if (! $this->table_element)
  1002. {
  1003. dol_syslog(get_class($this)."::setAutoValidate was called on objet with property table_element not defined",LOG_ERR);
  1004. return -1;
  1005. }
  1006. $sql = 'UPDATE '.MAIN_DB_PREFIX.$this->table_element;
  1007. $sql.= ' SET auto_validate = '.$validate;
  1008. $sql.= ' WHERE rowid = '.$this->id;
  1009. dol_syslog(get_class($this)."::setAutoValidate", LOG_DEBUG);
  1010. if ($this->db->query($sql))
  1011. {
  1012. $this->auto_validate = $validate;
  1013. return 1;
  1014. }
  1015. else
  1016. {
  1017. dol_print_error($this->db);
  1018. return -1;
  1019. }
  1020. }
  1021. }
  1022. /**
  1023. * Class to manage invoice lines of templates.
  1024. * Saved into database table llx_facturedet_rec
  1025. */
  1026. class FactureLigneRec extends CommonInvoiceLine
  1027. {
  1028. /**
  1029. * Delete line in database
  1030. *
  1031. * @return int <0 if KO, >0 if OK
  1032. */
  1033. function delete()
  1034. {
  1035. global $conf,$langs,$user;
  1036. $error=0;
  1037. $this->db->begin();
  1038. // Call trigger
  1039. /*$result=$this->call_trigger('LINEBILLREC_DELETE',$user);
  1040. if ($result < 0)
  1041. {
  1042. $this->db->rollback();
  1043. return -1;
  1044. }*/
  1045. // End call triggers
  1046. $sql = "DELETE FROM ".MAIN_DB_PREFIX."facturedet_rec WHERE rowid = ".($this->rowid > 0 ? $this->rowid : $this->id);
  1047. dol_syslog(get_class($this)."::delete", LOG_DEBUG);
  1048. if ($this->db->query($sql) )
  1049. {
  1050. $this->db->commit();
  1051. return 1;
  1052. }
  1053. else
  1054. {
  1055. $this->error=$this->db->error()." sql=".$sql;
  1056. $this->db->rollback();
  1057. return -1;
  1058. }
  1059. }
  1060. }