expensereport.class.php 91 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581158215831584158515861587158815891590159115921593159415951596159715981599160016011602160316041605160616071608160916101611161216131614161516161617161816191620162116221623162416251626162716281629163016311632163316341635163616371638163916401641164216431644164516461647164816491650165116521653165416551656165716581659166016611662166316641665166616671668166916701671167216731674167516761677167816791680168116821683168416851686168716881689169016911692169316941695169616971698169917001701170217031704170517061707170817091710171117121713171417151716171717181719172017211722172317241725172617271728172917301731173217331734173517361737173817391740174117421743174417451746174717481749175017511752175317541755175617571758175917601761176217631764176517661767176817691770177117721773177417751776177717781779178017811782178317841785178617871788178917901791179217931794179517961797179817991800180118021803180418051806180718081809181018111812181318141815181618171818181918201821182218231824182518261827182818291830183118321833183418351836183718381839184018411842184318441845184618471848184918501851185218531854185518561857185818591860186118621863186418651866186718681869187018711872187318741875187618771878187918801881188218831884188518861887188818891890189118921893189418951896189718981899190019011902190319041905190619071908190919101911191219131914191519161917191819191920192119221923192419251926192719281929193019311932193319341935193619371938193919401941194219431944194519461947194819491950195119521953195419551956195719581959196019611962196319641965196619671968196919701971197219731974197519761977197819791980198119821983198419851986198719881989199019911992199319941995199619971998199920002001200220032004200520062007200820092010201120122013201420152016201720182019202020212022202320242025202620272028202920302031203220332034203520362037203820392040204120422043204420452046204720482049205020512052205320542055205620572058205920602061206220632064206520662067206820692070207120722073207420752076207720782079208020812082208320842085208620872088208920902091209220932094209520962097209820992100210121022103210421052106210721082109211021112112211321142115211621172118211921202121212221232124212521262127212821292130213121322133213421352136213721382139214021412142214321442145214621472148214921502151215221532154215521562157215821592160216121622163216421652166216721682169217021712172217321742175217621772178217921802181218221832184218521862187218821892190219121922193219421952196219721982199220022012202220322042205220622072208220922102211221222132214221522162217221822192220222122222223222422252226222722282229223022312232223322342235223622372238223922402241224222432244224522462247224822492250225122522253225422552256225722582259226022612262226322642265226622672268226922702271227222732274227522762277227822792280228122822283228422852286228722882289229022912292229322942295229622972298229923002301230223032304230523062307230823092310231123122313231423152316231723182319232023212322232323242325232623272328232923302331233223332334233523362337233823392340234123422343234423452346234723482349235023512352235323542355235623572358235923602361236223632364236523662367236823692370237123722373237423752376237723782379238023812382238323842385238623872388238923902391239223932394239523962397239823992400240124022403240424052406240724082409241024112412241324142415241624172418241924202421242224232424242524262427242824292430243124322433243424352436243724382439244024412442244324442445244624472448244924502451245224532454245524562457245824592460246124622463246424652466246724682469247024712472247324742475247624772478247924802481248224832484248524862487248824892490249124922493249424952496249724982499250025012502250325042505250625072508250925102511251225132514251525162517251825192520252125222523252425252526252725282529253025312532253325342535253625372538253925402541254225432544254525462547254825492550255125522553255425552556255725582559256025612562256325642565256625672568256925702571257225732574257525762577257825792580258125822583258425852586258725882589259025912592259325942595259625972598259926002601260226032604260526062607260826092610261126122613261426152616261726182619262026212622262326242625262626272628262926302631263226332634263526362637263826392640264126422643264426452646264726482649265026512652265326542655265626572658265926602661266226632664266526662667266826692670267126722673267426752676267726782679268026812682268326842685268626872688268926902691269226932694269526962697269826992700270127022703270427052706270727082709271027112712271327142715271627172718271927202721272227232724272527262727272827292730273127322733273427352736273727382739274027412742274327442745274627472748274927502751275227532754275527562757275827592760276127622763276427652766276727682769277027712772277327742775277627772778277927802781278227832784278527862787278827892790279127922793
  1. <?php
  2. /* Copyright (C) 2011 Dimitri Mouillard <dmouillard@teclib.com>
  3. * Copyright (C) 2015 Laurent Destailleur <eldy@users.sourceforge.net>
  4. * Copyright (C) 2015 Alexandre Spangaro <aspangaro@zendsi.com>
  5. * Copyright (C) 2016 Ferran Marcet <fmarcet@2byte.es>
  6. * Copyright (C) 2018 Nicolas ZABOURI <info@inovea-conseil.com>
  7. *
  8. * This program is free software; you can redistribute it and/or modify
  9. * it under the terms of the GNU General Public License as published by
  10. * the Free Software Foundation; either version 3 of the License, or
  11. * (at your option) any later version.
  12. *
  13. * This program is distributed in the hope that it will be useful,
  14. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  15. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  16. * GNU General Public License for more details.
  17. *
  18. * You should have received a copy of the GNU General Public License
  19. * along with this program. If not, see <http://www.gnu.org/licenses/>.
  20. */
  21. /**
  22. * \file htdocs/expensereport/class/expensereport.class.php
  23. * \ingroup expensereport
  24. * \brief File to manage Expense Reports
  25. */
  26. require_once DOL_DOCUMENT_ROOT .'/core/class/commonobject.class.php';
  27. require_once DOL_DOCUMENT_ROOT .'/expensereport/class/expensereport_ik.class.php';
  28. require_once DOL_DOCUMENT_ROOT .'/expensereport/class/expensereport_rule.class.php';
  29. /**
  30. * Class to manage Trips and Expenses
  31. */
  32. class ExpenseReport extends CommonObject
  33. {
  34. /**
  35. * @var string ID to identify managed object
  36. */
  37. public $element='expensereport';
  38. /**
  39. * @var string Name of table without prefix where object is stored
  40. */
  41. public $table_element='expensereport';
  42. var $table_element_line = 'expensereport_det';
  43. var $fk_element = 'fk_expensereport';
  44. var $picto = 'trip';
  45. var $lines=array();
  46. public $date_debut;
  47. public $date_fin;
  48. var $status;
  49. var $fk_statut; // -- 0=draft, 2=validated (attente approb), 4=canceled, 5=approved, 6=payed, 99=denied
  50. var $fk_c_paiement;
  51. var $paid;
  52. var $user_author_infos;
  53. var $user_validator_infos;
  54. var $fk_typepayment;
  55. var $num_payment;
  56. var $code_paiement;
  57. var $code_statut;
  58. // ACTIONS
  59. // Create
  60. var $date_create;
  61. var $fk_user_author; // Note fk_user_author is not the 'author' but the guy the expense report is for.
  62. // Update
  63. var $date_modif;
  64. var $fk_user_modif;
  65. // Refus
  66. var $date_refuse;
  67. var $detail_refuse;
  68. var $fk_user_refuse;
  69. // Annulation
  70. var $date_cancel;
  71. var $detail_cancel;
  72. var $fk_user_cancel;
  73. var $fk_user_validator; // User that is defined to approve
  74. // Validation
  75. var $date_valid; // User making validation
  76. var $fk_user_valid;
  77. var $user_valid_infos;
  78. // Approve
  79. var $date_approve;
  80. var $fk_user_approve; // User that has approved
  81. // Paiement
  82. var $user_paid_infos;
  83. /*
  84. END ACTIONS
  85. */
  86. /**
  87. * Draft
  88. */
  89. const STATUS_DRAFT = 0;
  90. /**
  91. * Validated (need to be paid)
  92. */
  93. const STATUS_VALIDATED = 2;
  94. /**
  95. * Classified approved
  96. */
  97. const STATUS_APPROVED = 5;
  98. /**
  99. * Classified refused
  100. */
  101. const STATUS_REFUSED = 99;
  102. /**
  103. * Classified paid.
  104. */
  105. const STATUS_CLOSED = 6;
  106. /**
  107. * Constructor
  108. *
  109. * @param DoliDB $db Handler acces base de donnees
  110. */
  111. function __construct($db)
  112. {
  113. $this->db = $db;
  114. $this->total_ht = 0;
  115. $this->total_ttc = 0;
  116. $this->total_tva = 0;
  117. $this->modepaymentid = 0;
  118. // List of language codes for status
  119. $this->statuts_short = array(0 => 'Draft', 2 => 'Validated', 4 => 'Canceled', 5 => 'Approved', 6 => 'Paid', 99 => 'Refused');
  120. $this->statuts = array(0 => 'Draft', 2 => 'ValidatedWaitingApproval', 4 => 'Canceled', 5 => 'Approved', 6 => 'Paid', 99 => 'Refused');
  121. $this->statuts_logo = array(0 => 'statut0', 2 => 'statut1', 4 => 'statut5', 5 => 'statut3', 6 => 'statut6', 99 => 'statut5');
  122. }
  123. /**
  124. * Create object in database
  125. *
  126. * @param User $user User that create
  127. * @param int $notrigger Disable triggers
  128. * @return int <0 if KO, >0 if OK
  129. */
  130. function create($user, $notrigger=0)
  131. {
  132. global $conf;
  133. $now = dol_now();
  134. $error = 0;
  135. // Check parameters
  136. if (empty($this->date_debut) || empty($this->date_fin))
  137. {
  138. $this->error='ErrorFieldRequired';
  139. return -1;
  140. }
  141. $fuserid = $this->fk_user_author; // Note fk_user_author is not the 'author' but the guy the expense report is for.
  142. if (empty($fuserid)) $fuserid = $user->id;
  143. $this->db->begin();
  144. $sql = "INSERT INTO ".MAIN_DB_PREFIX.$this->table_element." (";
  145. $sql.= "ref";
  146. $sql.= ",total_ht";
  147. $sql.= ",total_ttc";
  148. $sql.= ",total_tva";
  149. $sql.= ",date_debut";
  150. $sql.= ",date_fin";
  151. $sql.= ",date_create";
  152. $sql.= ",fk_user_author";
  153. $sql.= ",fk_user_validator";
  154. $sql.= ",fk_user_approve";
  155. $sql.= ",fk_user_modif";
  156. $sql.= ",fk_statut";
  157. $sql.= ",fk_c_paiement";
  158. $sql.= ",paid";
  159. $sql.= ",note_public";
  160. $sql.= ",note_private";
  161. $sql.= ",entity";
  162. $sql.= ") VALUES(";
  163. $sql.= "'(PROV)'";
  164. $sql.= ", ".$this->total_ht;
  165. $sql.= ", ".$this->total_ttc;
  166. $sql.= ", ".$this->total_tva;
  167. $sql.= ", '".$this->db->idate($this->date_debut)."'";
  168. $sql.= ", '".$this->db->idate($this->date_fin)."'";
  169. $sql.= ", '".$this->db->idate($now)."'";
  170. $sql.= ", ".$fuserid;
  171. $sql.= ", ".($this->fk_user_validator > 0 ? $this->fk_user_validator:"null");
  172. $sql.= ", ".($this->fk_user_approve > 0 ? $this->fk_user_approve:"null");
  173. $sql.= ", ".($this->fk_user_modif > 0 ? $this->fk_user_modif:"null");
  174. $sql.= ", ".($this->fk_statut > 1 ? $this->fk_statut:0);
  175. $sql.= ", ".($this->modepaymentid?$this->modepaymentid:"null");
  176. $sql.= ", 0";
  177. $sql.= ", ".($this->note_public?"'".$this->db->escape($this->note_public)."'":"null");
  178. $sql.= ", ".($this->note_private?"'".$this->db->escape($this->note_private)."'":"null");
  179. $sql.= ", ".$conf->entity;
  180. $sql.= ")";
  181. $result = $this->db->query($sql);
  182. if ($result)
  183. {
  184. $this->id = $this->db->last_insert_id(MAIN_DB_PREFIX.$this->table_element);
  185. $this->ref='(PROV'.$this->id.')';
  186. $sql = 'UPDATE '.MAIN_DB_PREFIX.$this->table_element." SET ref='".$this->db->escape($this->ref)."' WHERE rowid=".$this->id;
  187. $resql=$this->db->query($sql);
  188. if (!$resql) $error++;
  189. if (is_array($this->lines) && count($this->lines)>0)
  190. {
  191. foreach ($this->lines as $i => $val)
  192. {
  193. $newndfline=new ExpenseReportLine($this->db);
  194. $newndfline=$this->lines[$i];
  195. $newndfline->fk_expensereport=$this->id;
  196. if ($result >= 0)
  197. {
  198. $result=$newndfline->insert();
  199. }
  200. if ($result < 0)
  201. {
  202. $error++;
  203. break;
  204. }
  205. }
  206. }
  207. if (! $error)
  208. {
  209. $result=$this->insertExtraFields();
  210. if ($result < 0) $error++;
  211. }
  212. if (! $error)
  213. {
  214. $result=$this->update_price();
  215. if ($result > 0)
  216. {
  217. if (!$notrigger)
  218. {
  219. // Call trigger
  220. $result=$this->call_trigger('EXPENSE_REPORT_CREATE',$user);
  221. if ($result < 0) {
  222. $error++;
  223. }
  224. // End call triggers
  225. }
  226. if (empty($error))
  227. {
  228. $this->db->commit();
  229. return $this->id;
  230. }
  231. else
  232. {
  233. $this->db->rollback();
  234. return -4;
  235. }
  236. }
  237. else
  238. {
  239. $this->db->rollback();
  240. return -3;
  241. }
  242. }
  243. else
  244. {
  245. dol_syslog(get_class($this)."::create error ".$this->error, LOG_ERR);
  246. $this->db->rollback();
  247. return -2;
  248. }
  249. }
  250. else
  251. {
  252. $this->error=$this->db->lasterror()." sql=".$sql;
  253. $this->db->rollback();
  254. return -1;
  255. }
  256. }
  257. /**
  258. * Load an object from its id and create a new one in database
  259. *
  260. * @param int $fk_user_author Id of new user
  261. * @return int New id of clone
  262. */
  263. function createFromClone($fk_user_author)
  264. {
  265. global $user,$hookmanager;
  266. $error=0;
  267. if (empty($fk_user_author)) $fk_user_author = $user->id;
  268. $this->context['createfromclone'] = 'createfromclone';
  269. $this->db->begin();
  270. // get extrafields so they will be clone
  271. //foreach($this->lines as $line)
  272. //$line->fetch_optionals($line->rowid);
  273. // Load source object
  274. $objFrom = clone $this;
  275. $this->id=0;
  276. $this->ref = '';
  277. $this->status=0;
  278. $this->fk_statut=0;
  279. // Clear fields
  280. $this->fk_user_author = $fk_user_author; // Note fk_user_author is not the 'author' but the guy the expense report is for.
  281. $this->fk_user_valid = '';
  282. $this->date_create = '';
  283. $this->date_creation = '';
  284. $this->date_validation = '';
  285. // Create clone
  286. $result=$this->create($user);
  287. if ($result < 0) $error++;
  288. if (! $error)
  289. {
  290. // Hook of thirdparty module
  291. if (is_object($hookmanager))
  292. {
  293. $parameters=array('objFrom'=>$objFrom);
  294. $action='';
  295. $reshook=$hookmanager->executeHooks('createFrom',$parameters,$this,$action); // Note that $action and $object may have been modified by some hooks
  296. if ($reshook < 0) $error++;
  297. }
  298. }
  299. unset($this->context['createfromclone']);
  300. // End
  301. if (! $error)
  302. {
  303. $this->db->commit();
  304. return $this->id;
  305. }
  306. else
  307. {
  308. $this->db->rollback();
  309. return -1;
  310. }
  311. }
  312. /**
  313. * update
  314. *
  315. * @param User $user User making change
  316. * @param int $notrigger Disable triggers
  317. * @param User $userofexpensereport New user we want to have the expense report on.
  318. * @return int <0 if KO, >0 if OK
  319. */
  320. function update($user, $notrigger = 0, $userofexpensereport=null)
  321. {
  322. global $langs;
  323. $error = 0;
  324. $this->db->begin();
  325. $sql = "UPDATE ".MAIN_DB_PREFIX.$this->table_element." SET";
  326. $sql.= " total_ht = ".$this->total_ht;
  327. $sql.= " , total_ttc = ".$this->total_ttc;
  328. $sql.= " , total_tva = ".$this->total_tva;
  329. $sql.= " , date_debut = '".$this->db->idate($this->date_debut)."'";
  330. $sql.= " , date_fin = '".$this->db->idate($this->date_fin)."'";
  331. if ($userofexpensereport && is_object($userofexpensereport))
  332. {
  333. $sql.= " , fk_user_author = ".($userofexpensereport->id > 0 ? "'".$userofexpensereport->id."'":"null"); // Note fk_user_author is not the 'author' but the guy the expense report is for.
  334. }
  335. $sql.= " , fk_user_validator = ".($this->fk_user_validator > 0 ? $this->fk_user_validator:"null");
  336. $sql.= " , fk_user_valid = ".($this->fk_user_valid > 0 ? $this->fk_user_valid:"null");
  337. $sql.= " , fk_user_approve = ".($this->fk_user_approve > 0 ? $this->fk_user_approve:"null");
  338. $sql.= " , fk_user_modif = ".$user->id;
  339. $sql.= " , fk_statut = ".($this->fk_statut >= 0 ? $this->fk_statut:'0');
  340. $sql.= " , fk_c_paiement = ".($this->fk_c_paiement > 0 ? $this->fk_c_paiement:"null");
  341. $sql.= " , note_public = ".(!empty($this->note_public)?"'".$this->db->escape($this->note_public)."'":"''");
  342. $sql.= " , note_private = ".(!empty($this->note_private)?"'".$this->db->escape($this->note_private)."'":"''");
  343. $sql.= " , detail_refuse = ".(!empty($this->detail_refuse)?"'".$this->db->escape($this->detail_refuse)."'":"''");
  344. $sql.= " WHERE rowid = ".$this->id;
  345. dol_syslog(get_class($this)."::update sql=".$sql, LOG_DEBUG);
  346. $result = $this->db->query($sql);
  347. if ($result)
  348. {
  349. if (!$notrigger)
  350. {
  351. // Call trigger
  352. $result=$this->call_trigger('EXPENSE_REPORT_UPDATE',$user);
  353. if ($result < 0) {
  354. $error++;
  355. }
  356. // End call triggers
  357. }
  358. if (empty($error))
  359. {
  360. $this->db->commit();
  361. return 1;
  362. }
  363. else
  364. {
  365. $this->db->rollback();
  366. $this->error=$this->db->error();
  367. return -2;
  368. }
  369. }
  370. else
  371. {
  372. $this->db->rollback();
  373. $this->error=$this->db->error();
  374. return -1;
  375. }
  376. }
  377. /**
  378. * Load an object from database
  379. *
  380. * @param int $id Id {@min 1}
  381. * @param string $ref Ref {@name ref}
  382. * @return int <0 if KO, >0 if OK
  383. */
  384. function fetch($id, $ref='')
  385. {
  386. global $conf;
  387. $sql = "SELECT d.rowid, d.ref, d.note_public, d.note_private,"; // DEFAULT
  388. $sql.= " d.detail_refuse, d.detail_cancel, d.fk_user_refuse, d.fk_user_cancel,"; // ACTIONS
  389. $sql.= " d.date_refuse, d.date_cancel,"; // ACTIONS
  390. $sql.= " d.total_ht, d.total_ttc, d.total_tva,"; // TOTAUX (int)
  391. $sql.= " d.date_debut, d.date_fin, d.date_create, d.tms as date_modif, d.date_valid, d.date_approve,"; // DATES (datetime)
  392. $sql.= " d.fk_user_author, d.fk_user_modif, d.fk_user_validator,";
  393. $sql.= " d.fk_user_valid, d.fk_user_approve,";
  394. $sql.= " d.fk_statut as status, d.fk_c_paiement,";
  395. $sql.= " dp.libelle as libelle_paiement, dp.code as code_paiement"; // INNER JOIN paiement
  396. $sql.= " FROM ".MAIN_DB_PREFIX.$this->table_element." as d";
  397. $sql.= " LEFT JOIN ".MAIN_DB_PREFIX."c_paiement as dp ON d.fk_c_paiement = dp.id";
  398. if ($ref) $sql.= " WHERE d.ref = '".$this->db->escape($ref)."'";
  399. else $sql.= " WHERE d.rowid = ".$id;
  400. //$sql.= $restrict;
  401. dol_syslog(get_class($this)."::fetch sql=".$sql, LOG_DEBUG);
  402. $resql = $this->db->query($sql) ;
  403. if ($resql)
  404. {
  405. $obj = $this->db->fetch_object($resql);
  406. if ($obj)
  407. {
  408. $this->id = $obj->rowid;
  409. $this->ref = $obj->ref;
  410. $this->total_ht = $obj->total_ht;
  411. $this->total_tva = $obj->total_tva;
  412. $this->total_ttc = $obj->total_ttc;
  413. $this->note_public = $obj->note_public;
  414. $this->note_private = $obj->note_private;
  415. $this->detail_refuse = $obj->detail_refuse;
  416. $this->detail_cancel = $obj->detail_cancel;
  417. $this->date_debut = $this->db->jdate($obj->date_debut);
  418. $this->date_fin = $this->db->jdate($obj->date_fin);
  419. $this->date_valid = $this->db->jdate($obj->date_valid);
  420. $this->date_approve = $this->db->jdate($obj->date_approve);
  421. $this->date_create = $this->db->jdate($obj->date_create);
  422. $this->date_modif = $this->db->jdate($obj->date_modif);
  423. $this->date_refuse = $this->db->jdate($obj->date_refuse);
  424. $this->date_cancel = $this->db->jdate($obj->date_cancel);
  425. $this->fk_user_author = $obj->fk_user_author; // Note fk_user_author is not the 'author' but the guy the expense report is for.
  426. $this->fk_user_modif = $obj->fk_user_modif;
  427. $this->fk_user_validator = $obj->fk_user_validator;
  428. $this->fk_user_valid = $obj->fk_user_valid;
  429. $this->fk_user_refuse = $obj->fk_user_refuse;
  430. $this->fk_user_cancel = $obj->fk_user_cancel;
  431. $this->fk_user_approve = $obj->fk_user_approve;
  432. $user_author = new User($this->db);
  433. if ($this->fk_user_author > 0) $user_author->fetch($this->fk_user_author);
  434. $this->user_author_infos = dolGetFirstLastname($user_author->firstname, $user_author->lastname);
  435. $user_approver = new User($this->db);
  436. if ($this->fk_user_approve > 0) $user_approver->fetch($this->fk_user_approve);
  437. elseif ($this->fk_user_validator > 0) $user_approver->fetch($this->fk_user_validator); // For backward compatibility
  438. $this->user_validator_infos = dolGetFirstLastname($user_approver->firstname, $user_approver->lastname);
  439. $this->fk_statut = $obj->status;
  440. $this->status = $obj->status;
  441. $this->fk_c_paiement = $obj->fk_c_paiement;
  442. $this->paid = $obj->paid;
  443. if ($this->fk_statut==5 || $this->fk_statut==6)
  444. {
  445. $user_valid = new User($this->db);
  446. if ($this->fk_user_valid > 0) $user_valid->fetch($this->fk_user_valid);
  447. $this->user_valid_infos = dolGetFirstLastname($user_valid->firstname, $user_valid->lastname);
  448. }
  449. $this->libelle_statut = $obj->libelle_statut;
  450. $this->libelle_paiement = $obj->libelle_paiement;
  451. $this->code_statut = $obj->code_statut;
  452. $this->code_paiement = $obj->code_paiement;
  453. $this->lines = array();
  454. $result=$this->fetch_lines();
  455. return $result;
  456. }
  457. else
  458. {
  459. return 0;
  460. }
  461. }
  462. else
  463. {
  464. $this->error=$this->db->lasterror();
  465. return -1;
  466. }
  467. }
  468. // phpcs:disable PEAR.NamingConventions.ValidFunctionName.NotCamelCaps
  469. /**
  470. * Classify the expense report as paid
  471. *
  472. * @param int $id Id of expense report
  473. * @param user $fuser User making change
  474. * @param int $notrigger Disable triggers
  475. * @return int <0 if KO, >0 if OK
  476. */
  477. function set_paid($id, $fuser, $notrigger = 0)
  478. {
  479. // phpcs:enable
  480. $error = 0;
  481. $this->db->begin();
  482. $sql = "UPDATE ".MAIN_DB_PREFIX."expensereport";
  483. $sql.= " SET fk_statut = 6, paid=1";
  484. $sql.= " WHERE rowid = ".$id." AND fk_statut = 5";
  485. dol_syslog(get_class($this)."::set_paid sql=".$sql, LOG_DEBUG);
  486. $resql=$this->db->query($sql);
  487. if ($resql)
  488. {
  489. if ($this->db->affected_rows($resql))
  490. {
  491. if (!$notrigger)
  492. {
  493. // Call trigger
  494. $result=$this->call_trigger('EXPENSE_REPORT_PAID',$fuser);
  495. if ($result < 0) {
  496. $error++;
  497. }
  498. // End call triggers
  499. }
  500. if (empty($error))
  501. {
  502. $this->db->commit();
  503. return 1;
  504. }
  505. else
  506. {
  507. $this->db->rollback();
  508. $this->error=$this->db->error();
  509. return -2;
  510. }
  511. }
  512. else
  513. {
  514. $this->db->commit();
  515. return 0;
  516. }
  517. }
  518. else
  519. {
  520. $this->db->rollback();
  521. dol_print_error($this->db);
  522. return -1;
  523. }
  524. }
  525. /**
  526. * Returns the label status
  527. *
  528. * @param int $mode 0=long label, 1=short label, 2=Picto + short label, 3=Picto, 4=Picto + long label, 5=Short label + Picto
  529. * @return string Label
  530. */
  531. function getLibStatut($mode=0)
  532. {
  533. return $this->LibStatut($this->status,$mode);
  534. }
  535. // phpcs:disable PEAR.NamingConventions.ValidFunctionName.NotCamelCaps
  536. /**
  537. * Returns the label of a statut
  538. *
  539. * @param int $status id statut
  540. * @param int $mode 0=long label, 1=short label, 2=Picto + short label, 3=Picto, 4=Picto + long label, 5=Short label + Picto, 6=Long label + Picto
  541. * @return string Label
  542. */
  543. function LibStatut($status,$mode=0)
  544. {
  545. // phpcs:enable
  546. global $langs;
  547. if ($mode == 0)
  548. return $langs->transnoentities($this->statuts[$status]);
  549. elseif ($mode == 1)
  550. return $langs->transnoentities($this->statuts_short[$status]);
  551. elseif ($mode == 2)
  552. return img_picto($langs->transnoentities($this->statuts_short[$status]), $this->statuts_logo[$status]).' '.$langs->transnoentities($this->statuts_short[$status]);
  553. elseif ($mode == 3)
  554. return img_picto($langs->transnoentities($this->statuts_short[$status]), $this->statuts_logo[$status]);
  555. elseif ($mode == 4)
  556. return img_picto($langs->transnoentities($this->statuts_short[$status]),$this->statuts_logo[$status]).' '.$langs->transnoentities($this->statuts[$status]);
  557. elseif ($mode == 5)
  558. return '<span class="hideonsmartphone">'.$langs->transnoentities($this->statuts_short[$status]).' </span>'.img_picto($langs->transnoentities($this->statuts_short[$status]),$this->statuts_logo[$status]);
  559. elseif ($mode == 6)
  560. return $langs->transnoentities($this->statuts[$status]).' '.img_picto($langs->transnoentities($this->statuts_short[$status]),$this->statuts_logo[$status]);
  561. }
  562. /**
  563. * Load information on object
  564. *
  565. * @param int $id Id of object
  566. * @return void
  567. */
  568. function info($id)
  569. {
  570. global $conf;
  571. $sql = "SELECT f.rowid,";
  572. $sql.= " f.date_create as datec,";
  573. $sql.= " f.tms as date_modification,";
  574. $sql.= " f.date_valid as datev,";
  575. $sql.= " f.date_approve as datea,";
  576. //$sql.= " f.fk_user_author as fk_user_creation,"; // This is not user of creation but user the expense is for.
  577. $sql.= " f.fk_user_modif as fk_user_modification,";
  578. $sql.= " f.fk_user_valid,";
  579. $sql.= " f.fk_user_approve";
  580. $sql.= " FROM ".MAIN_DB_PREFIX."expensereport as f";
  581. $sql.= " WHERE f.rowid = ".$id;
  582. $sql.= " AND f.entity = ".$conf->entity;
  583. $resql = $this->db->query($sql);
  584. if ($resql)
  585. {
  586. if ($this->db->num_rows($resql))
  587. {
  588. $obj = $this->db->fetch_object($resql);
  589. $this->id = $obj->rowid;
  590. $this->date_creation = $this->db->jdate($obj->datec);
  591. $this->date_modification = $this->db->jdate($obj->date_modification);
  592. $this->date_validation = $this->db->jdate($obj->datev);
  593. $this->date_approbation = $this->db->jdate($obj->datea);
  594. $cuser = new User($this->db);
  595. $cuser->fetch($obj->fk_user_author);
  596. $this->user_creation = $cuser;
  597. if ($obj->fk_user_creation)
  598. {
  599. $cuser = new User($this->db);
  600. $cuser->fetch($obj->fk_user_creation);
  601. $this->user_creation = $cuser;
  602. }
  603. if ($obj->fk_user_valid)
  604. {
  605. $vuser = new User($this->db);
  606. $vuser->fetch($obj->fk_user_valid);
  607. $this->user_validation = $vuser;
  608. }
  609. if ($obj->fk_user_modification)
  610. {
  611. $muser = new User($this->db);
  612. $muser->fetch($obj->fk_user_modification);
  613. $this->user_modification = $muser;
  614. }
  615. if ($obj->fk_user_approve)
  616. {
  617. $auser = new User($this->db);
  618. $auser->fetch($obj->fk_user_approve);
  619. $this->user_approve = $auser;
  620. }
  621. }
  622. $this->db->free($resql);
  623. }
  624. else
  625. {
  626. dol_print_error($this->db);
  627. }
  628. }
  629. /**
  630. * Initialise an instance with random values.
  631. * Used to build previews or test instances.
  632. * id must be 0 if object instance is a specimen.
  633. *
  634. * @return void
  635. */
  636. function initAsSpecimen()
  637. {
  638. global $user,$langs,$conf;
  639. $now=dol_now();
  640. // Initialise parametres
  641. $this->id=0;
  642. $this->ref = 'SPECIMEN';
  643. $this->specimen=1;
  644. $this->date_create = $now;
  645. $this->date_debut = $now;
  646. $this->date_fin = $now;
  647. $this->date_valid = $now;
  648. $this->date_approve = $now;
  649. $type_fees_id = 2; // TF_TRIP
  650. $this->status = 5;
  651. $this->fk_statut = 5;
  652. $this->fk_user_author = $user->id;
  653. $this->fk_user_validator = $user->id;
  654. $this->fk_user_valid = $user->id;
  655. $this->fk_user_approve = $user->id;
  656. $this->note_private='Private note';
  657. $this->note_public='SPECIMEN';
  658. $nbp = 5;
  659. $xnbp = 0;
  660. while ($xnbp < $nbp)
  661. {
  662. $line=new ExpenseReportLine($this->db);
  663. $line->comments=$langs->trans("Comment")." ".$xnbp;
  664. $line->date=($now-3600*(1+$xnbp));
  665. $line->total_ht=100;
  666. $line->total_tva=20;
  667. $line->total_ttc=120;
  668. $line->qty=1;
  669. $line->vatrate=20;
  670. $line->value_unit=120;
  671. $line->fk_expensereport=0;
  672. $line->type_fees_code='TRA';
  673. $line->fk_c_type_fees=$type_fees_id;
  674. $line->projet_ref = 'ABC';
  675. $this->lines[$xnbp]=$line;
  676. $xnbp++;
  677. $this->total_ht+=$line->total_ht;
  678. $this->total_tva+=$line->total_tva;
  679. $this->total_ttc+=$line->total_ttc;
  680. }
  681. }
  682. // phpcs:disable PEAR.NamingConventions.ValidFunctionName.NotCamelCaps
  683. /**
  684. * fetch_line_by_project
  685. *
  686. * @param int $projectid Project id
  687. * @param User $user User
  688. * @return int <0 if KO, >0 if OK
  689. */
  690. function fetch_line_by_project($projectid,$user='')
  691. {
  692. // phpcs:enable
  693. global $conf,$db,$langs;
  694. $langs->load('trips');
  695. if ($user->rights->expensereport->lire) {
  696. $sql = "SELECT de.fk_expensereport, de.date, de.comments, de.total_ht, de.total_ttc";
  697. $sql.= " FROM ".MAIN_DB_PREFIX."expensereport_det as de";
  698. $sql.= " WHERE de.fk_projet = ".$projectid;
  699. dol_syslog(get_class($this)."::fetch sql=".$sql, LOG_DEBUG);
  700. $result = $db->query($sql) ;
  701. if ($result)
  702. {
  703. $num = $db->num_rows($result);
  704. $i = 0;
  705. $total_HT = 0;
  706. $total_TTC = 0;
  707. while ($i < $num)
  708. {
  709. $objp = $db->fetch_object($result);
  710. $sql2 = "SELECT d.rowid, d.fk_user_author, d.ref, d.fk_statut";
  711. $sql2.= " FROM ".MAIN_DB_PREFIX."expensereport as d";
  712. $sql2.= " WHERE d.rowid = '".$objp->fk_expensereport."'";
  713. $result2 = $db->query($sql2);
  714. $obj = $db->fetch_object($result2);
  715. $objp->fk_user_author = $obj->fk_user_author;
  716. $objp->ref = $obj->ref;
  717. $objp->fk_c_expensereport_status = $obj->fk_statut;
  718. $objp->rowid = $obj->rowid;
  719. $total_HT = $total_HT + $objp->total_ht;
  720. $total_TTC = $total_TTC + $objp->total_ttc;
  721. $author = new User($db);
  722. $author->fetch($objp->fk_user_author);
  723. print '<tr>';
  724. print '<td><a href="'.DOL_URL_ROOT.'/expensereport/card.php?id='.$objp->rowid.'">'.$objp->ref_num.'</a></td>';
  725. print '<td align="center">'.dol_print_date($objp->date,'day').'</td>';
  726. print '<td>'.$author->getNomUrl(1).'</td>';
  727. print '<td>'.$objp->comments.'</td>';
  728. print '<td align="right">'.price($objp->total_ht).'</td>';
  729. print '<td align="right">'.price($objp->total_ttc).'</td>';
  730. print '<td align="right">';
  731. switch($objp->fk_c_expensereport_status) {
  732. case 4:
  733. print img_picto($langs->trans('StatusOrderCanceled'),'statut5');
  734. break;
  735. case 1:
  736. print $langs->trans('Draft').' '.img_picto($langs->trans('Draft'),'statut0');
  737. break;
  738. case 2:
  739. print $langs->trans('TripForValid').' '.img_picto($langs->trans('TripForValid'),'statut3');
  740. break;
  741. case 5:
  742. print $langs->trans('TripForPaid').' '.img_picto($langs->trans('TripForPaid'),'statut3');
  743. break;
  744. case 6:
  745. print $langs->trans('TripPaid').' '.img_picto($langs->trans('TripPaid'),'statut4');
  746. break;
  747. }
  748. /*
  749. if ($status==4) return img_picto($langs->trans('StatusOrderCanceled'),'statut5');
  750. if ($status==1) return img_picto($langs->trans('StatusOrderDraft'),'statut0');
  751. if ($status==2) return img_picto($langs->trans('StatusOrderValidated'),'statut1');
  752. if ($status==2) return img_picto($langs->trans('StatusOrderOnProcess'),'statut3');
  753. if ($status==5) return img_picto($langs->trans('StatusOrderToBill'),'statut4');
  754. if ($status==6) return img_picto($langs->trans('StatusOrderOnProcess'),'statut6');
  755. */
  756. print '</td>';
  757. print '</tr>';
  758. $i++;
  759. }
  760. print '<tr class="liste_total"><td colspan="4">'.$langs->trans("Number").': '.$i.'</td>';
  761. print '<td align="right" width="100">'.$langs->trans("TotalHT").' : '.price($total_HT).'</td>';
  762. print '<td align="right" width="100">'.$langs->trans("TotalTTC").' : '.price($total_TTC).'</td>';
  763. print '<td>&nbsp;</td>';
  764. print '</tr>';
  765. }
  766. else
  767. {
  768. $this->error=$db->lasterror();
  769. return -1;
  770. }
  771. }
  772. }
  773. /**
  774. * recalculer
  775. * TODO Replace this with call to update_price if not already done
  776. *
  777. * @param int $id Id of expense report
  778. * @return int <0 if KO, >0 if OK
  779. */
  780. function recalculer($id)
  781. {
  782. $sql = 'SELECT tt.total_ht, tt.total_ttc, tt.total_tva';
  783. $sql.= ' FROM '.MAIN_DB_PREFIX.$this->table_element_line.' as tt';
  784. $sql.= ' WHERE tt.'.$this->fk_element.' = '.$id;
  785. $total_ht = 0; $total_tva = 0; $total_ttc = 0;
  786. $result = $this->db->query($sql);
  787. if($result)
  788. {
  789. $num = $this->db->num_rows($result);
  790. $i = 0;
  791. while ($i < $num):
  792. $objp = $this->db->fetch_object($result);
  793. $total_ht+=$objp->total_ht;
  794. $total_tva+=$objp->total_tva;
  795. $i++;
  796. endwhile;
  797. $total_ttc = $total_ht + $total_tva;
  798. $sql = "UPDATE ".MAIN_DB_PREFIX.$this->table_element." SET";
  799. $sql.= " total_ht = ".$total_ht;
  800. $sql.= " , total_ttc = ".$total_ttc;
  801. $sql.= " , total_tva = ".$total_tva;
  802. $sql.= " WHERE rowid = ".$id;
  803. $result = $this->db->query($sql);
  804. if($result):
  805. $this->db->free($result);
  806. return 1;
  807. else:
  808. $this->error=$this->db->lasterror();
  809. dol_syslog(get_class($this)."::recalculer: Error ".$this->error,LOG_ERR);
  810. return -3;
  811. endif;
  812. }
  813. else
  814. {
  815. $this->error=$this->db->lasterror();
  816. dol_syslog(get_class($this)."::recalculer: Error ".$this->error,LOG_ERR);
  817. return -3;
  818. }
  819. }
  820. // phpcs:disable PEAR.NamingConventions.ValidFunctionName.NotCamelCaps
  821. /**
  822. * fetch_lines
  823. *
  824. * @return int <0 if OK, >0 if KO
  825. */
  826. function fetch_lines()
  827. {
  828. // phpcs:enable
  829. $this->lines=array();
  830. $sql = ' SELECT de.rowid, de.comments, de.qty, de.value_unit, de.date, de.rang,';
  831. $sql.= ' de.'.$this->fk_element.', de.fk_c_type_fees, de.fk_c_exp_tax_cat, de.fk_projet, de.tva_tx,';
  832. $sql.= ' de.total_ht, de.total_tva, de.total_ttc,';
  833. $sql.= ' ctf.code as code_type_fees, ctf.label as libelle_type_fees,';
  834. $sql.= ' p.ref as ref_projet, p.title as title_projet';
  835. $sql.= ' FROM '.MAIN_DB_PREFIX.$this->table_element_line.' as de';
  836. $sql.= ' LEFT JOIN '.MAIN_DB_PREFIX.'c_type_fees as ctf ON de.fk_c_type_fees = ctf.id';
  837. $sql.= ' LEFT JOIN '.MAIN_DB_PREFIX.'projet as p ON de.fk_projet = p.rowid';
  838. $sql.= ' WHERE de.'.$this->fk_element.' = '.$this->id;
  839. if (! empty($conf->global->EXPENSEREPORT_LINES_SORTED_BY_ROWID))
  840. {
  841. $sql.= ' ORDER BY de.rang ASC, de.rowid ASC';
  842. }
  843. else
  844. {
  845. $sql.= ' ORDER BY de.rang ASC, de.date ASC';
  846. }
  847. $resql = $this->db->query($sql);
  848. if ($resql)
  849. {
  850. $num = $this->db->num_rows($resql);
  851. $i = 0;
  852. while ($i < $num)
  853. {
  854. $objp = $this->db->fetch_object($resql);
  855. $deplig = new ExpenseReportLine($this->db);
  856. $deplig->rowid = $objp->rowid;
  857. $deplig->id = $objp->id;
  858. $deplig->comments = $objp->comments;
  859. $deplig->qty = $objp->qty;
  860. $deplig->value_unit = $objp->value_unit;
  861. $deplig->date = $objp->date;
  862. $deplig->dates = $this->db->jdate($objp->date);
  863. $deplig->fk_expensereport = $objp->fk_expensereport;
  864. $deplig->fk_c_type_fees = $objp->fk_c_type_fees;
  865. $deplig->fk_c_exp_tax_cat = $objp->fk_c_exp_tax_cat;
  866. $deplig->fk_projet = $objp->fk_projet;
  867. $deplig->total_ht = $objp->total_ht;
  868. $deplig->total_tva = $objp->total_tva;
  869. $deplig->total_ttc = $objp->total_ttc;
  870. $deplig->type_fees_code = empty($objp->code_type_fees)?'TF_OTHER':$objp->code_type_fees;
  871. $deplig->type_fees_libelle = $objp->libelle_type_fees;
  872. $deplig->tva_tx = $objp->tva_tx;
  873. $deplig->vatrate = $objp->tva_tx;
  874. $deplig->projet_ref = $objp->ref_projet;
  875. $deplig->projet_title = $objp->title_projet;
  876. $deplig->rang = $objp->rang;
  877. $this->lines[$i] = $deplig;
  878. $i++;
  879. }
  880. $this->db->free($resql);
  881. return 1;
  882. }
  883. else
  884. {
  885. $this->error=$this->db->lasterror();
  886. dol_syslog(get_class($this)."::fetch_lines: Error ".$this->error, LOG_ERR);
  887. return -3;
  888. }
  889. }
  890. /**
  891. * delete
  892. *
  893. * @param User $fuser User that delete
  894. * @return int <0 if KO, >0 if OK
  895. */
  896. function delete(User $fuser=null)
  897. {
  898. global $user,$langs,$conf;
  899. if (! $rowid) $rowid=$this->id;
  900. $sql = 'DELETE FROM '.MAIN_DB_PREFIX.$this->table_element_line.' WHERE '.$this->fk_element.' = '.$rowid;
  901. if ($this->db->query($sql))
  902. {
  903. $sql = 'DELETE FROM '.MAIN_DB_PREFIX.$this->table_element.' WHERE rowid = '.$rowid;
  904. $resql=$this->db->query($sql);
  905. if ($resql)
  906. {
  907. $this->db->commit();
  908. return 1;
  909. }
  910. else
  911. {
  912. $this->error=$this->db->error()." sql=".$sql;
  913. dol_syslog(get_class($this)."::delete ".$this->error, LOG_ERR);
  914. $this->db->rollback();
  915. return -6;
  916. }
  917. }
  918. else
  919. {
  920. $this->error=$this->db->error()." sql=".$sql;
  921. dol_syslog(get_class($this)."::delete ".$this->error, LOG_ERR);
  922. $this->db->rollback();
  923. return -4;
  924. }
  925. }
  926. /**
  927. * Set to status validate
  928. *
  929. * @param User $fuser User
  930. * @param int $notrigger Disable triggers
  931. * @return int <0 if KO, 0 if nothing done, >0 if OK
  932. */
  933. function setValidate($fuser, $notrigger=0)
  934. {
  935. global $conf,$langs,$user;
  936. $error = 0;
  937. $now = dol_now();
  938. // Protection
  939. if ($this->statut == self::STATUS_VALIDATED)
  940. {
  941. dol_syslog(get_class($this)."::valid action abandonned: already validated", LOG_WARNING);
  942. return 0;
  943. }
  944. $this->date_valid = $now; // Required for the getNextNum later.
  945. // Define new ref
  946. if (! $error && (preg_match('/^[\(]?PROV/i', $this->ref) || empty($this->ref))) // empty should not happened, but when it occurs, the test save life
  947. {
  948. $num = $this->getNextNumRef();
  949. }
  950. else
  951. {
  952. $num = $this->ref;
  953. }
  954. if (empty($num) || $num < 0) return -1;
  955. $this->newref = $num;
  956. $this->db->begin();
  957. // Validate
  958. $sql = "UPDATE ".MAIN_DB_PREFIX.$this->table_element;
  959. $sql.= " SET ref = '".$num."',";
  960. $sql.= " fk_statut = ".self::STATUS_VALIDATED.",";
  961. $sql.= " date_valid='".$this->db->idate($this->date_valid)."',";
  962. $sql.= " fk_user_valid = ".$user->id;
  963. $sql.= " WHERE rowid = ".$this->id;
  964. $resql=$this->db->query($sql);
  965. if ($resql)
  966. {
  967. if (!$notrigger)
  968. {
  969. // Call trigger
  970. $result=$this->call_trigger('EXPENSE_REPORT_VALIDATE',$fuser);
  971. if ($result < 0) {
  972. $error++;
  973. }
  974. // End call triggers
  975. }
  976. if (! $error)
  977. {
  978. $this->oldref = $this->ref;
  979. // Rename directory if dir was a temporary ref
  980. if (preg_match('/^[\(]?PROV/i', $this->ref))
  981. {
  982. require_once DOL_DOCUMENT_ROOT.'/core/lib/files.lib.php';
  983. // On renomme repertoire ($this->ref = ancienne ref, $num = nouvelle ref)
  984. // in order not to lose the attachments
  985. $oldref = dol_sanitizeFileName($this->ref);
  986. $newref = dol_sanitizeFileName($num);
  987. $dirsource = $conf->expensereport->dir_output.'/'.$oldref;
  988. $dirdest = $conf->expensereport->dir_output.'/'.$newref;
  989. if (file_exists($dirsource))
  990. {
  991. dol_syslog(get_class($this)."::valid() rename dir ".$dirsource." into ".$dirdest);
  992. if (@rename($dirsource, $dirdest))
  993. {
  994. dol_syslog("Rename ok");
  995. // Rename docs starting with $oldref with $newref
  996. $listoffiles=dol_dir_list($conf->expensereport->dir_output.'/'.$newref, 'files', 1, '^'.preg_quote($oldref,'/'));
  997. foreach($listoffiles as $fileentry)
  998. {
  999. $dirsource=$fileentry['name'];
  1000. $dirdest=preg_replace('/^'.preg_quote($oldref,'/').'/',$newref, $dirsource);
  1001. $dirsource=$fileentry['path'].'/'.$dirsource;
  1002. $dirdest=$fileentry['path'].'/'.$dirdest;
  1003. @rename($dirsource, $dirdest);
  1004. }
  1005. }
  1006. }
  1007. }
  1008. }
  1009. // Set new ref and current status
  1010. if (! $error)
  1011. {
  1012. $this->ref = $num;
  1013. $this->statut = self::STATUS_VALIDATED;
  1014. }
  1015. if (empty($error))
  1016. {
  1017. $this->db->commit();
  1018. return 1;
  1019. }
  1020. else
  1021. {
  1022. $this->db->rollback();
  1023. $this->error=$this->db->error();
  1024. return -2;
  1025. }
  1026. }
  1027. else
  1028. {
  1029. $this->db->rollback();
  1030. $this->error=$this->db->lasterror();
  1031. return -1;
  1032. }
  1033. }
  1034. // phpcs:disable PEAR.NamingConventions.ValidFunctionName.NotCamelCaps
  1035. /**
  1036. * set_save_from_refuse
  1037. *
  1038. * @param User $fuser User
  1039. * @return int <0 if KO, >0 if OK
  1040. */
  1041. function set_save_from_refuse($fuser)
  1042. {
  1043. // phpcs:enable
  1044. global $conf,$langs;
  1045. // Sélection de la date de début de la NDF
  1046. $sql = 'SELECT date_debut';
  1047. $sql.= ' FROM '.MAIN_DB_PREFIX.$this->table_element;
  1048. $sql.= ' WHERE rowid = '.$this->id;
  1049. $result = $this->db->query($sql);
  1050. $objp = $this->db->fetch_object($result);
  1051. $this->date_debut = $this->db->jdate($objp->date_debut);
  1052. if ($this->fk_statut != 2)
  1053. {
  1054. $sql = 'UPDATE '.MAIN_DB_PREFIX.$this->table_element;
  1055. $sql.= " SET fk_statut = 2";
  1056. $sql.= ' WHERE rowid = '.$this->id;
  1057. dol_syslog(get_class($this)."::set_save_from_refuse sql=".$sql, LOG_DEBUG);
  1058. if ($this->db->query($sql))
  1059. {
  1060. return 1;
  1061. }
  1062. else
  1063. {
  1064. $this->error=$this->db->lasterror();
  1065. return -1;
  1066. }
  1067. }
  1068. else
  1069. {
  1070. dol_syslog(get_class($this)."::set_save_from_refuse expensereport already with save status", LOG_WARNING);
  1071. }
  1072. }
  1073. /**
  1074. * Set status to approved
  1075. *
  1076. * @param User $fuser User
  1077. * @param int $notrigger Disable triggers
  1078. * @return int <0 if KO, 0 if nothing done, >0 if OK
  1079. */
  1080. function setApproved($fuser, $notrigger=0)
  1081. {
  1082. $now=dol_now();
  1083. $error = 0;
  1084. // date approval
  1085. $this->date_approve = $now;
  1086. if ($this->fk_statut != 5)
  1087. {
  1088. $this->db->begin();
  1089. $sql = 'UPDATE '.MAIN_DB_PREFIX.$this->table_element;
  1090. $sql.= " SET ref = '".$this->db->escape($this->ref)."', fk_statut = 5, fk_user_approve = ".$fuser->id.",";
  1091. $sql.= " date_approve='".$this->db->idate($this->date_approve)."'";
  1092. $sql.= ' WHERE rowid = '.$this->id;
  1093. if ($this->db->query($sql))
  1094. {
  1095. if (!$notrigger)
  1096. {
  1097. // Call trigger
  1098. $result=$this->call_trigger('EXPENSE_REPORT_APPROVE',$fuser);
  1099. if ($result < 0) {
  1100. $error++;
  1101. }
  1102. // End call triggers
  1103. }
  1104. if (empty($error))
  1105. {
  1106. $this->db->commit();
  1107. return 1;
  1108. }
  1109. else
  1110. {
  1111. $this->db->rollback();
  1112. $this->error=$this->db->error();
  1113. return -2;
  1114. }
  1115. }
  1116. else
  1117. {
  1118. $this->db->rollback();
  1119. $this->error=$this->db->lasterror();
  1120. return -1;
  1121. }
  1122. }
  1123. else
  1124. {
  1125. dol_syslog(get_class($this)."::setApproved expensereport already with approve status", LOG_WARNING);
  1126. }
  1127. return 0;
  1128. }
  1129. /**
  1130. * setDeny
  1131. *
  1132. * @param User $fuser User
  1133. * @param Details $details Details
  1134. * @param int $notrigger Disable triggers
  1135. * @return int
  1136. */
  1137. function setDeny($fuser,$details,$notrigger=0)
  1138. {
  1139. $now = dol_now();
  1140. $error = 0;
  1141. // date de refus
  1142. if ($this->fk_statut != 99)
  1143. {
  1144. $sql = 'UPDATE '.MAIN_DB_PREFIX.$this->table_element;
  1145. $sql.= " SET ref = '".$this->db->escape($this->ref)."', fk_statut = 99, fk_user_refuse = ".$fuser->id.",";
  1146. $sql.= " date_refuse='".$this->db->idate($now)."',";
  1147. $sql.= " detail_refuse='".$this->db->escape($details)."',";
  1148. $sql.= " fk_user_approve = NULL";
  1149. $sql.= ' WHERE rowid = '.$this->id;
  1150. if ($this->db->query($sql))
  1151. {
  1152. $this->fk_statut = 99;
  1153. $this->fk_user_refuse = $fuser->id;
  1154. $this->detail_refuse = $details;
  1155. $this->date_refuse = $now;
  1156. if (!$notrigger)
  1157. {
  1158. // Call trigger
  1159. $result=$this->call_trigger('EXPENSE_REPORT_DENY',$fuser);
  1160. if ($result < 0) {
  1161. $error++;
  1162. }
  1163. // End call triggers
  1164. }
  1165. if (empty($error))
  1166. {
  1167. $this->db->commit();
  1168. return 1;
  1169. }
  1170. else
  1171. {
  1172. $this->db->rollback();
  1173. $this->error=$this->db->error();
  1174. return -2;
  1175. }
  1176. }
  1177. else
  1178. {
  1179. $this->db->rollback();
  1180. $this->error=$this->db->lasterror();
  1181. return -1;
  1182. }
  1183. }
  1184. else
  1185. {
  1186. dol_syslog(get_class($this)."::setDeny expensereport already with refuse status", LOG_WARNING);
  1187. }
  1188. }
  1189. // phpcs:disable PEAR.NamingConventions.ValidFunctionName.NotCamelCaps
  1190. /**
  1191. * set_unpaid
  1192. *
  1193. * @param User $fuser User
  1194. * @param int $notrigger Disable triggers
  1195. * @return int <0 if KO, >0 if OK
  1196. */
  1197. function set_unpaid($fuser, $notrigger = 0)
  1198. {
  1199. // phpcs:enable
  1200. $error = 0;
  1201. if ($this->fk_c_deplacement_statuts != 5)
  1202. {
  1203. $this->db->begin();
  1204. $sql = 'UPDATE '.MAIN_DB_PREFIX.$this->table_element;
  1205. $sql.= " SET fk_statut = 5";
  1206. $sql.= ' WHERE rowid = '.$this->id;
  1207. dol_syslog(get_class($this)."::set_unpaid sql=".$sql, LOG_DEBUG);
  1208. if ($this->db->query($sql))
  1209. {
  1210. if (!$notrigger)
  1211. {
  1212. // Call trigger
  1213. $result=$this->call_trigger('EXPENSE_REPORT_UNPAID',$fuser);
  1214. if ($result < 0) {
  1215. $error++;
  1216. }
  1217. // End call triggers
  1218. }
  1219. if (empty($error))
  1220. {
  1221. $this->db->commit();
  1222. return 1;
  1223. }
  1224. else
  1225. {
  1226. $this->db->rollback();
  1227. $this->error=$this->db->error();
  1228. return -2;
  1229. }
  1230. }
  1231. else
  1232. {
  1233. $this->db->rollback();
  1234. $this->error=$this->db->error();
  1235. return -1;
  1236. }
  1237. }
  1238. else
  1239. {
  1240. dol_syslog(get_class($this)."::set_unpaid expensereport already with unpaid status", LOG_WARNING);
  1241. }
  1242. }
  1243. // phpcs:disable PEAR.NamingConventions.ValidFunctionName.NotCamelCaps
  1244. /**
  1245. * set_cancel
  1246. *
  1247. * @param User $fuser User
  1248. * @param string $detail Detail
  1249. * @param int $notrigger Disable triggers
  1250. * @return int <0 if KO, >0 if OK
  1251. */
  1252. function set_cancel($fuser,$detail, $notrigger=0)
  1253. {
  1254. // phpcs:enable
  1255. $error = 0;
  1256. $this->date_cancel = $this->db->idate(gmmktime());
  1257. if ($this->fk_statut != 4)
  1258. {
  1259. $this->db->begin();
  1260. $sql = 'UPDATE '.MAIN_DB_PREFIX.$this->table_element;
  1261. $sql.= " SET fk_statut = 4, fk_user_cancel = ".$fuser->id;
  1262. $sql.= ", date_cancel='".$this->db->idate($this->date_cancel)."'";
  1263. $sql.= " ,detail_cancel='".$this->db->escape($detail)."'";
  1264. $sql.= ' WHERE rowid = '.$this->id;
  1265. dol_syslog(get_class($this)."::set_cancel sql=".$sql, LOG_DEBUG);
  1266. if ($this->db->query($sql))
  1267. {
  1268. if (!$notrigger)
  1269. {
  1270. // Call trigger
  1271. $result=$this->call_trigger('EXPENSE_REPORT_CANCEL',$fuser);
  1272. if ($result < 0) {
  1273. $error++;
  1274. }
  1275. // End call triggers
  1276. }
  1277. if (empty($error))
  1278. {
  1279. $this->db->commit();
  1280. return 1;
  1281. }
  1282. else
  1283. {
  1284. $this->db->rollback();
  1285. $this->error=$this->db->error();
  1286. return -2;
  1287. }
  1288. }
  1289. else
  1290. {
  1291. $this->db->rollback();
  1292. $this->error=$this->db->error();
  1293. return -1;
  1294. }
  1295. }
  1296. else
  1297. {
  1298. dol_syslog(get_class($this)."::set_cancel expensereport already with cancel status", LOG_WARNING);
  1299. }
  1300. }
  1301. /**
  1302. * Return next reference of expense report not already used
  1303. *
  1304. * @return string free ref
  1305. */
  1306. function getNextNumRef()
  1307. {
  1308. global $langs, $conf;
  1309. $langs->load("trips");
  1310. if (! empty($conf->global->EXPENSEREPORT_ADDON))
  1311. {
  1312. $mybool=false;
  1313. $file = $conf->global->EXPENSEREPORT_ADDON.".php";
  1314. $classname = $conf->global->EXPENSEREPORT_ADDON;
  1315. // Include file with class
  1316. $dirmodels=array_merge(array('/'),(array) $conf->modules_parts['models']);
  1317. foreach ($dirmodels as $reldir)
  1318. {
  1319. $dir = dol_buildpath($reldir."core/modules/expensereport/");
  1320. // Load file with numbering class (if found)
  1321. $mybool|=@include_once $dir.$file;
  1322. }
  1323. if (! $mybool)
  1324. {
  1325. dol_print_error('',"Failed to include file ".$file);
  1326. return '';
  1327. }
  1328. $obj = new $classname();
  1329. $numref = $obj->getNextValue($this);
  1330. if ($numref != "")
  1331. {
  1332. return $numref;
  1333. }
  1334. else
  1335. {
  1336. $this->error=$obj->error;
  1337. $this->errors=$obj->errors;
  1338. //dol_print_error($this->db,get_class($this)."::getNextNumRef ".$obj->error);
  1339. return -1;
  1340. }
  1341. }
  1342. else
  1343. {
  1344. $this->error = "Error_EXPENSEREPORT_ADDON_NotDefined";
  1345. return -2;
  1346. }
  1347. }
  1348. /**
  1349. * Return clicable name (with picto eventually)
  1350. *
  1351. * @param int $withpicto 0=No picto, 1=Include picto into link, 2=Only picto
  1352. * @param int $max Max length of shown ref
  1353. * @param int $short 1=Return just URL
  1354. * @param string $moretitle Add more text to title tooltip
  1355. * @param int $notooltip 1=Disable tooltip
  1356. * @param int $save_lastsearch_value -1=Auto, 0=No save of lastsearch_values when clicking, 1=Save lastsearch_values whenclicking
  1357. * @return string String with URL
  1358. */
  1359. function getNomUrl($withpicto=0, $max=0, $short=0, $moretitle='', $notooltip=0, $save_lastsearch_value=-1)
  1360. {
  1361. global $langs, $conf;
  1362. $result='';
  1363. $url = DOL_URL_ROOT.'/expensereport/card.php?id='.$this->id;
  1364. if ($short) return $url;
  1365. $label = '<u>' . $langs->trans("ShowExpenseReport") . '</u>';
  1366. if (! empty($this->ref))
  1367. $label .= '<br><b>' . $langs->trans('Ref') . ':</b> ' . $this->ref;
  1368. if (! empty($this->total_ht))
  1369. $label.= '<br><b>' . $langs->trans('AmountHT') . ':</b> ' . price($this->total_ht, 0, $langs, 0, -1, -1, $conf->currency);
  1370. if (! empty($this->total_tva))
  1371. $label.= '<br><b>' . $langs->trans('VAT') . ':</b> ' . price($this->total_tva, 0, $langs, 0, -1, -1, $conf->currency);
  1372. if (! empty($this->total_ttc))
  1373. $label.= '<br><b>' . $langs->trans('AmountTTC') . ':</b> ' . price($this->total_ttc, 0, $langs, 0, -1, -1, $conf->currency);
  1374. if ($moretitle) $label.=' - '.$moretitle;
  1375. //if ($option != 'nolink')
  1376. //{
  1377. // Add param to save lastsearch_values or not
  1378. $add_save_lastsearch_values=($save_lastsearch_value == 1 ? 1 : 0);
  1379. if ($save_lastsearch_value == -1 && preg_match('/list\.php/',$_SERVER["PHP_SELF"])) $add_save_lastsearch_values=1;
  1380. if ($add_save_lastsearch_values) $url.='&save_lastsearch_values=1';
  1381. //}
  1382. $ref=$this->ref;
  1383. if (empty($ref)) $ref=$this->id;
  1384. $linkclose='';
  1385. if (empty($notooltip))
  1386. {
  1387. if (! empty($conf->global->MAIN_OPTIMIZEFORTEXTBROWSER))
  1388. {
  1389. $label=$langs->trans("ShowExpenseReport");
  1390. $linkclose.=' alt="'.dol_escape_htmltag($label, 1).'"';
  1391. }
  1392. $linkclose.= ' title="'.dol_escape_htmltag($label, 1).'"';
  1393. $linkclose.=' class="classfortooltip"';
  1394. }
  1395. $linkstart = '<a href="'.$url.'"';
  1396. $linkstart.=$linkclose.'>';
  1397. $linkend='</a>';
  1398. $result .= $linkstart;
  1399. if ($withpicto) $result.=img_object(($notooltip?'':$label), $this->picto, ($notooltip?(($withpicto != 2) ? 'class="paddingright"' : ''):'class="'.(($withpicto != 2) ? 'paddingright ' : '').'classfortooltip"'), 0, 0, $notooltip?0:1);
  1400. if ($withpicto != 2) $result.=($max?dol_trunc($ref,$max):$ref);
  1401. $result .= $linkend;
  1402. return $result;
  1403. }
  1404. // phpcs:disable PEAR.NamingConventions.ValidFunctionName.NotCamelCaps
  1405. /**
  1406. * Update total of an expense report when you add a line.
  1407. *
  1408. * @param string $ligne_total_ht Amount without taxes
  1409. * @param string $ligne_total_tva Amount of all taxes
  1410. * @return void
  1411. */
  1412. function update_totaux_add($ligne_total_ht,$ligne_total_tva)
  1413. {
  1414. // phpcs:enable
  1415. $this->total_ht = $this->total_ht + $ligne_total_ht;
  1416. $this->total_tva = $this->total_tva + $ligne_total_tva;
  1417. $this->total_ttc = $this->total_ht + $this->total_tva;
  1418. $sql = "UPDATE ".MAIN_DB_PREFIX.$this->table_element." SET";
  1419. $sql.= " total_ht = ".$this->total_ht;
  1420. $sql.= " , total_ttc = ".$this->total_ttc;
  1421. $sql.= " , total_tva = ".$this->total_tva;
  1422. $sql.= " WHERE rowid = ".$this->id;
  1423. $result = $this->db->query($sql);
  1424. if ($result):
  1425. return 1;
  1426. else:
  1427. $this->error=$this->db->error();
  1428. return -1;
  1429. endif;
  1430. }
  1431. // phpcs:disable PEAR.NamingConventions.ValidFunctionName.NotCamelCaps
  1432. /**
  1433. * Update total of an expense report when you delete a line.
  1434. *
  1435. * @param string $ligne_total_ht Amount without taxes
  1436. * @param string $ligne_total_tva Amount of all taxes
  1437. * @return void
  1438. */
  1439. function update_totaux_del($ligne_total_ht,$ligne_total_tva)
  1440. {
  1441. // phpcs:enable
  1442. $this->total_ht = $this->total_ht - $ligne_total_ht;
  1443. $this->total_tva = $this->total_tva - $ligne_total_tva;
  1444. $this->total_ttc = $this->total_ht + $this->total_tva;
  1445. $sql = "UPDATE ".MAIN_DB_PREFIX.$this->table_element." SET";
  1446. $sql.= " total_ht = ".$this->total_ht;
  1447. $sql.= " , total_ttc = ".$this->total_ttc;
  1448. $sql.= " , total_tva = ".$this->total_tva;
  1449. $sql.= " WHERE rowid = ".$this->id;
  1450. $result = $this->db->query($sql);
  1451. if ($result):
  1452. return 1;
  1453. else:
  1454. $this->error=$this->db->error();
  1455. return -1;
  1456. endif;
  1457. }
  1458. /**
  1459. * addline
  1460. *
  1461. * @param real $qty Qty
  1462. * @param double $up Value init
  1463. * @param int $fk_c_type_fees Type payment
  1464. * @param double $vatrate Vat rate
  1465. * @param string $date Date
  1466. * @param string $comments Description
  1467. * @param int $fk_project Project id
  1468. * @param int $fk_c_exp_tax_cat Car category id
  1469. * @param int $type Type line
  1470. * @return int <0 if KO, >0 if OK
  1471. */
  1472. function addline($qty=0, $up=0, $fk_c_type_fees=0, $vatrate=0, $date='', $comments='', $fk_project=0, $fk_c_exp_tax_cat=0, $type=0)
  1473. {
  1474. global $conf,$langs;
  1475. dol_syslog(get_class($this)."::addline qty=$qty, up=$up, fk_c_type_fees=$fk_c_type_fees, vatrate=$vatrate, date=$date, fk_project=$fk_project, type=$type, comments=$comments", LOG_DEBUG);
  1476. if (empty($qty)) $qty = 0;
  1477. if (empty($fk_c_type_fees) || $fk_c_type_fees < 0) $fk_c_type_fees = 0;
  1478. if (empty($fk_c_exp_tax_cat) || $fk_c_exp_tax_cat < 0) $fk_c_exp_tax_cat = 0;
  1479. if (empty($vatrate) || $vatrate < 0) $vatrate = 0;
  1480. if (empty($date)) $date = '';
  1481. if (empty($fk_project)) $fk_project = 0;
  1482. $qty = price2num($qty);
  1483. $vatrate = price2num($vatrate);
  1484. $up = price2num($up);
  1485. if ($this->fk_statut == self::STATUS_DRAFT)
  1486. {
  1487. $this->db->begin();
  1488. $this->line = new ExpenseReportLine($this->db);
  1489. if (preg_match('/\((.*)\)/', $vatrate, $reg))
  1490. {
  1491. $vat_src_code = $reg[1];
  1492. $vatrate = preg_replace('/\s*\(.*\)/', '', $vatrate); // Remove code into vatrate.
  1493. }
  1494. $vatrate = preg_replace('/\*/','',$vatrate);
  1495. $seller = ''; // seller is unknown
  1496. $tmp = calcul_price_total($qty, $up, 0, $vatrate, 0, 0, 0, 'TTC', 0, $type, $seller);
  1497. $this->line->value_unit = $up;
  1498. $this->line->vatrate = price2num($vatrate);
  1499. $this->line->total_ttc = $tmp[2];
  1500. $this->line->total_ht = $tmp[0];
  1501. $this->line->total_tva = $tmp[1];
  1502. $this->line->fk_expensereport = $this->id;
  1503. $this->line->qty = $qty;
  1504. $this->line->date = $date;
  1505. $this->line->fk_c_type_fees = $fk_c_type_fees;
  1506. $this->line->fk_c_exp_tax_cat = $fk_c_exp_tax_cat;
  1507. $this->line->comments = $comments;
  1508. $this->line->fk_projet = $fk_project;
  1509. $this->applyOffset();
  1510. $this->checkRules($type, $seller);
  1511. $result=$this->line->insert(0, true);
  1512. if ($result > 0)
  1513. {
  1514. $result=$this->update_price(); // This method is designed to add line from user input so total calculation must be done using 'auto' mode.
  1515. if ($result > 0)
  1516. {
  1517. $this->db->commit();
  1518. return $this->line->rowid;
  1519. }
  1520. else
  1521. {
  1522. $this->db->rollback();
  1523. return -1;
  1524. }
  1525. }
  1526. else
  1527. {
  1528. $this->error=$this->line->error;
  1529. dol_syslog(get_class($this)."::addline error=".$this->error, LOG_ERR);
  1530. $this->db->rollback();
  1531. return -2;
  1532. }
  1533. }
  1534. else
  1535. {
  1536. dol_syslog(get_class($this)."::addline status of expense report must be Draft to allow use of ->addline()", LOG_ERR);
  1537. $this->error = 'ErrorExpenseNotDraft';
  1538. return -3;
  1539. }
  1540. }
  1541. /**
  1542. * Check constraint of rules and update price if needed
  1543. *
  1544. * @param int $type type of line
  1545. * @param string $seller seller, but actually he is unknown
  1546. * @return true or false
  1547. */
  1548. function checkRules($type=0, $seller='')
  1549. {
  1550. global $user,$conf,$db,$langs;
  1551. $langs->load('trips');
  1552. if (empty($conf->global->MAIN_USE_EXPENSE_RULE)) return true; // if don't use rules
  1553. $rulestocheck = ExpenseReportRule::getAllRule($this->line->fk_c_type_fees, $this->line->date, $this->fk_user_author);
  1554. $violation = 0;
  1555. $rule_warning_message_tab = array();
  1556. $current_total_ttc = $this->line->total_ttc;
  1557. $new_current_total_ttc = $this->line->total_ttc;
  1558. // check if one is violated
  1559. foreach ($rulestocheck as $rule)
  1560. {
  1561. if (in_array($rule->code_expense_rules_type, array('EX_DAY', 'EX_MON', 'EX_YEA'))) $amount_to_test = $this->line->getExpAmount($rule, $this->fk_user_author, $rule->code_expense_rules_type);
  1562. else $amount_to_test = $current_total_ttc; // EX_EXP
  1563. $amount_to_test = $amount_to_test - $current_total_ttc + $new_current_total_ttc; // if amount as been modified by a previous rule
  1564. if ($amount_to_test > $rule->amount)
  1565. {
  1566. $violation++;
  1567. if ($rule->restrictive)
  1568. {
  1569. $this->error = 'ExpenseReportConstraintViolationError';
  1570. $this->errors[] = $this->error;
  1571. $new_current_total_ttc -= $amount_to_test - $rule->amount; // ex, entered 16€, limit 12€, subtracts 4€;
  1572. $rule_warning_message_tab[] = $langs->trans('ExpenseReportConstraintViolationError', $rule->id, price($amount_to_test,0,$langs,1,-1,-1,$conf->currency), price($rule->amount,0,$langs,1,-1,-1,$conf->currency), $langs->trans('by'.$rule->code_expense_rules_type, price($new_current_total_ttc,0,$langs,1,-1,-1,$conf->currency)));
  1573. }
  1574. else
  1575. {
  1576. $this->error = 'ExpenseReportConstraintViolationWarning';
  1577. $this->errors[] = $this->error;
  1578. $rule_warning_message_tab[] = $langs->trans('ExpenseReportConstraintViolationWarning', $rule->id, price($amount_to_test,0,$langs,1,-1,-1,$conf->currency), price($rule->amount,0,$langs,1,-1,-1,$conf->currency), $langs->trans('nolimitby'.$rule->code_expense_rules_type));
  1579. }
  1580. // No break, we sould test if another rule is violated
  1581. }
  1582. }
  1583. $this->line->rule_warning_message = implode('\n', $rule_warning_message_tab);
  1584. if ($violation > 0)
  1585. {
  1586. $tmp = calcul_price_total($this->line->qty, $new_current_total_ttc/$this->line->qty, 0, $this->line->vatrate, 0, 0, 0, 'TTC', 0, $type, $seller);
  1587. $this->line->value_unit = $tmp[5];
  1588. $this->line->total_ttc = $tmp[2];
  1589. $this->line->total_ht = $tmp[0];
  1590. $this->line->total_tva = $tmp[1];
  1591. return false;
  1592. }
  1593. else return true;
  1594. }
  1595. /**
  1596. * Method to apply the offset if needed
  1597. *
  1598. * @return boolean true=applied, false=not applied
  1599. */
  1600. function applyOffset()
  1601. {
  1602. global $conf;
  1603. if (empty($conf->global->MAIN_USE_EXPENSE_IK)) return false;
  1604. $userauthor = new User($this->db);
  1605. if ($userauthor->fetch($this->fk_user_author) <= 0)
  1606. {
  1607. $this->error = 'ErrorCantFetchUser';
  1608. $this->errors[] = 'ErrorCantFetchUser';
  1609. return false;
  1610. }
  1611. $range = ExpenseReportIk::getRangeByUser($userauthor, $this->line->fk_c_exp_tax_cat);
  1612. if (empty($range))
  1613. {
  1614. $this->error = 'ErrorNoRangeAvailable';
  1615. $this->errors[] = 'ErrorNoRangeAvailable';
  1616. return false;
  1617. }
  1618. if (!empty($conf->global->MAIN_EXPENSE_APPLY_ENTIRE_OFFSET)) $ikoffset = $range->ikoffset;
  1619. else $ikoffset = $range->ikoffset / 12; // The amount of offset is a global value for the year
  1620. // Test if ikoffset has been applied for the current month
  1621. if (!$this->offsetAlreadyGiven())
  1622. {
  1623. $new_up = $range->coef + ($ikoffset / $this->line->qty);
  1624. $tmp = calcul_price_total($this->line->qty, $new_up, 0, $this->line->vatrate, 0, 0, 0, 'TTC', 0, $type, $seller);
  1625. $this->line->value_unit = $tmp[5];
  1626. $this->line->total_ttc = $tmp[2];
  1627. $this->line->total_ht = $tmp[0];
  1628. $this->line->total_tva = $tmp[1];
  1629. return true;
  1630. }
  1631. return false;
  1632. }
  1633. /**
  1634. * If the sql find any rows then the ikoffset is already given (ikoffset is applied at the first expense report line)
  1635. *
  1636. * @return bool
  1637. */
  1638. function offsetAlreadyGiven()
  1639. {
  1640. $sql = 'SELECT e.rowid FROM '.MAIN_DB_PREFIX.'expensereport e';
  1641. $sql.= ' INNER JOIN '.MAIN_DB_PREFIX.'expensereport_det d ON (e.rowid = d.fk_expensereport)';
  1642. $sql.= ' INNER JOIN '.MAIN_DB_PREFIX.'c_type_fees f ON (d.fk_c_type_fees = f.id AND f.code = "EX_KME")';
  1643. $sql.= ' WHERE e.fk_user_author = '.(int) $this->fk_user_author;
  1644. $sql.= ' AND YEAR(d.date) = "'.dol_print_date($this->line->date, '%Y').'" AND MONTH(d.date) = "'.dol_print_date($this->line->date, '%m').'"';
  1645. if (!empty($this->line->id)) $sql.= ' AND d.rowid <> '.$this->line->id;
  1646. dol_syslog(get_class($this)."::offsetAlreadyGiven sql=".$sql);
  1647. $resql = $this->db->query($sql);
  1648. if ($resql)
  1649. {
  1650. $num = $this->db->num_rows($resql);
  1651. if ($num > 0) return true;
  1652. }
  1653. else
  1654. {
  1655. dol_print_error($this->db);
  1656. }
  1657. return false;
  1658. }
  1659. /**
  1660. * updateline
  1661. *
  1662. * @param int $rowid Line to edit
  1663. * @param int $type_fees_id Type payment
  1664. * @param int $projet_id Project id
  1665. * @param double $vatrate Vat rate. Can be '8.5* (8.5NPROM...)'
  1666. * @param string $comments Description
  1667. * @param real $qty Qty
  1668. * @param double $value_unit Value init
  1669. * @param int $date Date
  1670. * @param int $expensereport_id Expense report id
  1671. * @param int $fk_c_exp_tax_cat id of category of car
  1672. * @return int <0 if KO, >0 if OK
  1673. */
  1674. function updateline($rowid, $type_fees_id, $projet_id, $vatrate, $comments, $qty, $value_unit, $date, $expensereport_id, $fk_c_exp_tax_cat=0)
  1675. {
  1676. global $user, $mysoc;
  1677. if ($this->fk_statut==0 || $this->fk_statut==99)
  1678. {
  1679. $this->db->begin();
  1680. $type = 0; // TODO What if type is service ?
  1681. // We don't know seller and buyer for expense reports
  1682. $seller = $mysoc;
  1683. $buyer = new Societe($this->db);
  1684. $localtaxes_type=getLocalTaxesFromRate($vatrate,0,$buyer,$seller);
  1685. // Clean vat code
  1686. $vat_src_code='';
  1687. if (preg_match('/\((.*)\)/', $vatrate, $reg))
  1688. {
  1689. $vat_src_code = $reg[1];
  1690. $vatrate = preg_replace('/\s*\(.*\)/', '', $vatrate); // Remove code into vatrate.
  1691. }
  1692. $vatrate = preg_replace('/\*/','',$vatrate);
  1693. $tmp = calcul_price_total($qty, $value_unit, 0, $vatrate, 0, 0, 0, 'TTC', 0, $type, $seller, $localtaxes_type);
  1694. // calcul total of line
  1695. //$total_ttc = price2num($qty*$value_unit, 'MT');
  1696. $tx_tva = $vatrate / 100;
  1697. $tx_tva = $tx_tva + 1;
  1698. $total_ht = price2num($total_ttc/$tx_tva, 'MT');
  1699. $total_tva = price2num($total_ttc - $total_ht, 'MT');
  1700. // fin calculs
  1701. $this->line = new ExpenseReportLine($this->db);
  1702. $this->line->comments = $comments;
  1703. $this->line->qty = $qty;
  1704. $this->line->value_unit = $value_unit;
  1705. $this->line->date = $date;
  1706. $this->line->fk_expensereport= $expensereport_id;
  1707. $this->line->fk_c_type_fees = $type_fees_id;
  1708. $this->line->fk_c_exp_tax_cat = $fk_c_exp_tax_cat;
  1709. $this->line->fk_projet = $projet_id;
  1710. $this->line->vat_src_code = $vat_src_code;
  1711. $this->line->vatrate = price2num($vatrate);
  1712. $this->line->total_ttc = $tmp[2];
  1713. $this->line->total_ht = $tmp[0];
  1714. $this->line->total_tva = $tmp[1];
  1715. $this->line->localtax1_tx = $localtaxes_type[1];
  1716. $this->line->localtax2_tx = $localtaxes_type[3];
  1717. $this->line->localtax1_type = $localtaxes_type[0];
  1718. $this->line->localtax2_type = $localtaxes_type[2];
  1719. $this->line->rowid = $rowid;
  1720. $this->line->id = $rowid;
  1721. // Select des infos sur le type fees
  1722. $sql = "SELECT c.code as code_type_fees, c.label as libelle_type_fees";
  1723. $sql.= " FROM ".MAIN_DB_PREFIX."c_type_fees as c";
  1724. $sql.= " WHERE c.id = ".$type_fees_id;
  1725. $result = $this->db->query($sql);
  1726. $objp_fees = $this->db->fetch_object($result);
  1727. $this->line->type_fees_code = $objp_fees->code_type_fees;
  1728. $this->line->type_fees_libelle = $objp_fees->libelle_type_fees;
  1729. // Select des informations du projet
  1730. $sql = "SELECT p.ref as ref_projet, p.title as title_projet";
  1731. $sql.= " FROM ".MAIN_DB_PREFIX."projet as p";
  1732. $sql.= " WHERE p.rowid = ".$projet_id;
  1733. $result = $this->db->query($sql);
  1734. if ($result) {
  1735. $objp_projet = $this->db->fetch_object($result);
  1736. }
  1737. $this->line->projet_ref = $objp_projet->ref_projet;
  1738. $this->line->projet_title = $objp_projet->title_projet;
  1739. $this->applyOffset();
  1740. $this->checkRules();
  1741. $result = $this->line->update($user);
  1742. if ($result > 0)
  1743. {
  1744. $this->db->commit();
  1745. return 1;
  1746. }
  1747. else
  1748. {
  1749. $this->error=$this->line->error;
  1750. $this->errors=$this->line->errors;
  1751. $this->db->rollback();
  1752. return -2;
  1753. }
  1754. }
  1755. }
  1756. /**
  1757. * deleteline
  1758. *
  1759. * @param int $rowid Row id
  1760. * @param User $fuser User
  1761. * @return int <0 if KO, >0 if OK
  1762. */
  1763. function deleteline($rowid, $fuser='')
  1764. {
  1765. $this->db->begin();
  1766. $sql = 'DELETE FROM '.MAIN_DB_PREFIX.$this->table_element_line;
  1767. $sql.= ' WHERE rowid = '.$rowid;
  1768. dol_syslog(get_class($this)."::deleteline sql=".$sql);
  1769. $result = $this->db->query($sql);
  1770. if (!$result)
  1771. {
  1772. $this->error=$this->db->error();
  1773. dol_syslog(get_class($this)."::deleteline Error ".$this->error, LOG_ERR);
  1774. $this->db->rollback();
  1775. return -1;
  1776. }
  1777. $this->db->commit();
  1778. return 1;
  1779. }
  1780. // phpcs:disable PEAR.NamingConventions.ValidFunctionName.NotCamelCaps
  1781. /**
  1782. * periode_existe
  1783. *
  1784. * @param User $fuser User
  1785. * @param Date $date_debut Start date
  1786. * @param Date $date_fin End date
  1787. * @return int <0 if KO, >0 if OK
  1788. */
  1789. function periode_existe($fuser, $date_debut, $date_fin)
  1790. {
  1791. // phpcs:enable
  1792. $sql = "SELECT rowid, date_debut, date_fin";
  1793. $sql.= " FROM ".MAIN_DB_PREFIX.$this->table_element;
  1794. $sql.= " WHERE fk_user_author = '{$fuser->id}'";
  1795. dol_syslog(get_class($this)."::periode_existe sql=".$sql);
  1796. $result = $this->db->query($sql);
  1797. if($result)
  1798. {
  1799. $num_lignes = $this->db->num_rows($result); $i = 0;
  1800. if ($num_lignes>0)
  1801. {
  1802. $date_d_form = $date_debut;
  1803. $date_f_form = $date_fin;
  1804. $existe = false;
  1805. while ($i < $num_lignes)
  1806. {
  1807. $objp = $this->db->fetch_object($result);
  1808. $date_d_req = $this->db->jdate($objp->date_debut); // 3
  1809. $date_f_req = $this->db->jdate($objp->date_fin); // 4
  1810. if (!($date_f_form < $date_d_req || $date_d_form > $date_f_req)) $existe = true;
  1811. $i++;
  1812. }
  1813. if($existe) return 1;
  1814. else return 0;
  1815. }
  1816. else
  1817. {
  1818. return 0;
  1819. }
  1820. }
  1821. else
  1822. {
  1823. $this->error=$this->db->lasterror();
  1824. dol_syslog(get_class($this)."::periode_existe Error ".$this->error, LOG_ERR);
  1825. return -1;
  1826. }
  1827. }
  1828. // phpcs:disable PEAR.NamingConventions.ValidFunctionName.NotCamelCaps
  1829. /**
  1830. * Return list of people with permission to validate expense reports.
  1831. * Search for permission "approve expense report"
  1832. *
  1833. * @return array Array of user ids
  1834. */
  1835. function fetch_users_approver_expensereport()
  1836. {
  1837. // phpcs:enable
  1838. $users_validator=array();
  1839. $sql = "SELECT DISTINCT ur.fk_user";
  1840. $sql.= " FROM ".MAIN_DB_PREFIX."user_rights as ur, ".MAIN_DB_PREFIX."rights_def as rd";
  1841. $sql.= " WHERE ur.fk_id = rd.id and rd.module = 'expensereport' AND rd.perms = 'approve'"; // Permission 'Approve';
  1842. $sql.= "UNION";
  1843. $sql.= " SELECT DISTINCT ugu.fk_user";
  1844. $sql.= " FROM ".MAIN_DB_PREFIX."usergroup_user as ugu, ".MAIN_DB_PREFIX."usergroup_rights as ur, ".MAIN_DB_PREFIX."rights_def as rd";
  1845. $sql.= " WHERE ugu.fk_usergroup = ur.fk_usergroup AND ur.fk_id = rd.id and rd.module = 'expensereport' AND rd.perms = 'approve'"; // Permission 'Approve';
  1846. //print $sql;
  1847. dol_syslog(get_class($this)."::fetch_users_approver_expensereport sql=".$sql);
  1848. $result = $this->db->query($sql);
  1849. if($result)
  1850. {
  1851. $num_lignes = $this->db->num_rows($result); $i = 0;
  1852. while ($i < $num_lignes)
  1853. {
  1854. $objp = $this->db->fetch_object($result);
  1855. array_push($users_validator,$objp->fk_user);
  1856. $i++;
  1857. }
  1858. return $users_validator;
  1859. }
  1860. else
  1861. {
  1862. $this->error=$this->db->lasterror();
  1863. dol_syslog(get_class($this)."::fetch_users_approver_expensereport Error ".$this->error, LOG_ERR);
  1864. return -1;
  1865. }
  1866. }
  1867. /**
  1868. * Create a document onto disk accordign to template module.
  1869. *
  1870. * @param string $modele Force le mnodele a utiliser ('' to not force)
  1871. * @param Translate $outputlangs objet lang a utiliser pour traduction
  1872. * @param int $hidedetails Hide details of lines
  1873. * @param int $hidedesc Hide description
  1874. * @param int $hideref Hide ref
  1875. * @param null|array $moreparams Array to provide more information
  1876. * @return int 0 if KO, 1 if OK
  1877. */
  1878. public function generateDocument($modele, $outputlangs, $hidedetails=0, $hidedesc=0, $hideref=0, $moreparams=null)
  1879. {
  1880. global $conf,$langs;
  1881. $langs->load("trips");
  1882. if (! dol_strlen($modele)) {
  1883. $modele = 'standard';
  1884. if ($this->modelpdf) {
  1885. $modele = $this->modelpdf;
  1886. } elseif (! empty($conf->global->EXPENSEREPORT_ADDON_PDF)) {
  1887. $modele = $conf->global->EXPENSEREPORT_ADDON_PDF;
  1888. }
  1889. }
  1890. $modelpath = "core/modules/expensereport/doc/";
  1891. return $this->commonGenerateDocument($modelpath, $modele, $outputlangs, $hidedetails, $hidedesc, $hideref,$moreparams);
  1892. }
  1893. /**
  1894. * List of types
  1895. *
  1896. * @param int $active Active or not
  1897. * @return array
  1898. */
  1899. function listOfTypes($active=1)
  1900. {
  1901. global $langs;
  1902. $ret=array();
  1903. $sql = "SELECT id, code, label";
  1904. $sql.= " FROM ".MAIN_DB_PREFIX."c_type_fees";
  1905. $sql.= " WHERE active = ".$active;
  1906. dol_syslog(get_class($this)."::listOfTypes", LOG_DEBUG);
  1907. $result = $this->db->query($sql);
  1908. if ( $result )
  1909. {
  1910. $num = $this->db->num_rows($result);
  1911. $i=0;
  1912. while ($i < $num)
  1913. {
  1914. $obj = $this->db->fetch_object($result);
  1915. $ret[$obj->code]=(($langs->trans($obj->code)!=$obj->code)?$langs->trans($obj->code):$obj->label);
  1916. $i++;
  1917. }
  1918. }
  1919. else
  1920. {
  1921. dol_print_error($this->db);
  1922. }
  1923. return $ret;
  1924. }
  1925. // phpcs:disable PEAR.NamingConventions.ValidFunctionName.NotCamelCaps
  1926. /**
  1927. * Charge indicateurs this->nb pour le tableau de bord
  1928. *
  1929. * @return int <0 if KO, >0 if OK
  1930. */
  1931. function load_state_board()
  1932. {
  1933. // phpcs:enable
  1934. global $conf;
  1935. $this->nb=array();
  1936. $sql = "SELECT count(ex.rowid) as nb";
  1937. $sql.= " FROM ".MAIN_DB_PREFIX."expensereport as ex";
  1938. $sql.= " WHERE ex.fk_statut > 0";
  1939. $sql.= " AND ex.entity IN (".getEntity('expensereport').")";
  1940. $resql=$this->db->query($sql);
  1941. if ($resql)
  1942. {
  1943. while ($obj=$this->db->fetch_object($resql))
  1944. {
  1945. $this->nb["expensereports"]=$obj->nb;
  1946. }
  1947. $this->db->free($resql);
  1948. return 1;
  1949. }
  1950. else
  1951. {
  1952. dol_print_error($this->db);
  1953. $this->error=$this->db->error();
  1954. return -1;
  1955. }
  1956. }
  1957. // phpcs:disable PEAR.NamingConventions.ValidFunctionName.NotCamelCaps
  1958. /**
  1959. * Load indicators for dashboard (this->nbtodo and this->nbtodolate)
  1960. *
  1961. * @param User $user Objet user
  1962. * @param string $option 'topay' or 'toapprove'
  1963. * @return WorkboardResponse|int <0 if KO, WorkboardResponse if OK
  1964. */
  1965. function load_board($user, $option='topay')
  1966. {
  1967. // phpcs:enable
  1968. global $conf, $langs;
  1969. if ($user->societe_id) return -1; // protection pour eviter appel par utilisateur externe
  1970. $now=dol_now();
  1971. $userchildids = $user->getAllChildIds(1);
  1972. $sql = "SELECT ex.rowid, ex.date_valid";
  1973. $sql.= " FROM ".MAIN_DB_PREFIX."expensereport as ex";
  1974. if ($option == 'toapprove') $sql.= " WHERE ex.fk_statut = 2";
  1975. else $sql.= " WHERE ex.fk_statut = 5";
  1976. $sql.= " AND ex.entity IN (".getEntity('expensereport').")";
  1977. $sql.= " AND (ex.fk_user_author IN (".join(',',$userchildids).")";
  1978. $sql.= " OR ex.fk_user_validator IN (".join(',',$userchildids)."))";
  1979. $resql=$this->db->query($sql);
  1980. if ($resql)
  1981. {
  1982. $langs->load("members");
  1983. $response = new WorkboardResponse();
  1984. if ($option == 'toapprove')
  1985. {
  1986. $response->warning_delay=$conf->expensereport->approve->warning_delay/60/60/24;
  1987. $response->label=$langs->trans("ExpenseReportsToApprove");
  1988. $response->url=DOL_URL_ROOT.'/expensereport/list.php?mainmenu=hrm&amp;statut=2';
  1989. }
  1990. else
  1991. {
  1992. $response->warning_delay=$conf->expensereport->payment->warning_delay/60/60/24;
  1993. $response->label=$langs->trans("ExpenseReportsToPay");
  1994. $response->url=DOL_URL_ROOT.'/expensereport/list.php?mainmenu=hrm&amp;statut=5';
  1995. }
  1996. $response->img=img_object('',"trip");
  1997. while ($obj=$this->db->fetch_object($resql))
  1998. {
  1999. $response->nbtodo++;
  2000. if ($option == 'toapprove')
  2001. {
  2002. if ($this->db->jdate($obj->date_valid) < ($now - $conf->expensereport->approve->warning_delay)) {
  2003. $response->nbtodolate++;
  2004. }
  2005. }
  2006. else
  2007. {
  2008. if ($this->db->jdate($obj->date_valid) < ($now - $conf->expensereport->payment->warning_delay)) {
  2009. $response->nbtodolate++;
  2010. }
  2011. }
  2012. }
  2013. return $response;
  2014. }
  2015. else
  2016. {
  2017. dol_print_error($this->db);
  2018. $this->error=$this->db->error();
  2019. return -1;
  2020. }
  2021. }
  2022. /**
  2023. * Return if an expense report is late or not
  2024. *
  2025. * @param string $option 'topay' or 'toapprove'
  2026. * @return boolean True if late, False if not late
  2027. */
  2028. public function hasDelay($option)
  2029. {
  2030. global $conf;
  2031. //Only valid members
  2032. if ($option == 'toapprove' && $this->status != 2) return false;
  2033. if ($option == 'topay' && $this->status != 5) return false;
  2034. $now = dol_now();
  2035. if ($option == 'toapprove')
  2036. {
  2037. return ($this->datevalid?$this->datevalid:$this->date_valid) < ($now - $conf->expensereport->approve->warning_delay);
  2038. }
  2039. else
  2040. return ($this->datevalid?$this->datevalid:$this->date_valid) < ($now - $conf->expensereport->payment->warning_delay);
  2041. }
  2042. /**
  2043. * Return if an expensereport was dispatched into bookkeeping
  2044. *
  2045. * @return int <0 if KO, 0=no, 1=yes
  2046. */
  2047. public function getVentilExportCompta()
  2048. {
  2049. $alreadydispatched = 0;
  2050. $type = 'expense_report';
  2051. $sql = " SELECT COUNT(ab.rowid) as nb FROM ".MAIN_DB_PREFIX."accounting_bookkeeping as ab WHERE ab.doc_type='".$type."' AND ab.fk_doc = ".$this->id;
  2052. $resql = $this->db->query($sql);
  2053. if ($resql)
  2054. {
  2055. $obj = $this->db->fetch_object($resql);
  2056. if ($obj)
  2057. {
  2058. $alreadydispatched = $obj->nb;
  2059. }
  2060. }
  2061. else
  2062. {
  2063. $this->error = $this->db->lasterror();
  2064. return -1;
  2065. }
  2066. if ($alreadydispatched)
  2067. {
  2068. return 1;
  2069. }
  2070. return 0;
  2071. }
  2072. }
  2073. /**
  2074. * Class of expense report details lines
  2075. */
  2076. class ExpenseReportLine
  2077. {
  2078. /**
  2079. * @var DoliDB Database handler.
  2080. */
  2081. public $db;
  2082. /**
  2083. * @var string Error code (or message)
  2084. */
  2085. public $error='';
  2086. var $rowid;
  2087. var $comments;
  2088. var $qty;
  2089. var $value_unit;
  2090. var $date;
  2091. var $fk_c_type_fees;
  2092. var $fk_c_exp_tax_cat;
  2093. var $fk_projet;
  2094. var $fk_expensereport;
  2095. var $type_fees_code;
  2096. var $type_fees_libelle;
  2097. var $projet_ref;
  2098. var $projet_title;
  2099. var $vatrate;
  2100. var $total_ht;
  2101. var $total_tva;
  2102. var $total_ttc;
  2103. /**
  2104. * Constructor
  2105. *
  2106. * @param DoliDB $db Handlet database
  2107. */
  2108. function __construct($db)
  2109. {
  2110. $this->db= $db;
  2111. }
  2112. /**
  2113. * Fetch record for expense report detailed line
  2114. *
  2115. * @param int $rowid Id of object to load
  2116. * @return int <0 if KO, >0 if OK
  2117. */
  2118. function fetch($rowid)
  2119. {
  2120. $sql = 'SELECT fde.rowid, fde.fk_expensereport, fde.fk_c_type_fees, fde.fk_c_exp_tax_cat, fde.fk_projet, fde.date,';
  2121. $sql.= ' fde.tva_tx as vatrate, fde.vat_src_code, fde.comments, fde.qty, fde.value_unit, fde.total_ht, fde.total_tva, fde.total_ttc,';
  2122. $sql.= ' ctf.code as type_fees_code, ctf.label as type_fees_libelle,';
  2123. $sql.= ' pjt.rowid as projet_id, pjt.title as projet_title, pjt.ref as projet_ref';
  2124. $sql.= ' FROM '.MAIN_DB_PREFIX.'expensereport_det as fde';
  2125. $sql.= ' LEFT JOIN '.MAIN_DB_PREFIX.'c_type_fees as ctf ON fde.fk_c_type_fees=ctf.id'; // Sometimes type of expense report has been removed, so we use a left join here.
  2126. $sql.= ' LEFT JOIN '.MAIN_DB_PREFIX.'projet as pjt ON fde.fk_projet=pjt.rowid';
  2127. $sql.= ' WHERE fde.rowid = '.$rowid;
  2128. $result = $this->db->query($sql);
  2129. if($result)
  2130. {
  2131. $objp = $this->db->fetch_object($result);
  2132. $this->rowid = $objp->rowid;
  2133. $this->id = $obj->rowid;
  2134. $this->ref = $obj->ref;
  2135. $this->fk_expensereport = $objp->fk_expensereport;
  2136. $this->comments = $objp->comments;
  2137. $this->qty = $objp->qty;
  2138. $this->date = $objp->date;
  2139. $this->dates = $this->db->jdate($objp->date);
  2140. $this->value_unit = $objp->value_unit;
  2141. $this->fk_c_type_fees = $objp->fk_c_type_fees;
  2142. $this->fk_c_exp_tax_cat = $objp->fk_c_exp_tax_cat;
  2143. $this->fk_projet = $objp->fk_projet;
  2144. $this->type_fees_code = $objp->type_fees_code;
  2145. $this->type_fees_libelle = $objp->type_fees_libelle;
  2146. $this->projet_ref = $objp->projet_ref;
  2147. $this->projet_title = $objp->projet_title;
  2148. $this->vatrate = $objp->vatrate;
  2149. $this->vat_src_code = $objp->vat_src_code;
  2150. $this->total_ht = $objp->total_ht;
  2151. $this->total_tva = $objp->total_tva;
  2152. $this->total_ttc = $objp->total_ttc;
  2153. $this->db->free($result);
  2154. } else {
  2155. dol_print_error($this->db);
  2156. }
  2157. }
  2158. /**
  2159. * insert
  2160. *
  2161. * @param int $notrigger 1=No trigger
  2162. * @param bool $fromaddline false=keep default behavior, true=exclude the update_price() of parent object
  2163. * @return int <0 if KO, >0 if OK
  2164. */
  2165. function insert($notrigger=0,$fromaddline=false)
  2166. {
  2167. global $langs,$user,$conf;
  2168. $error=0;
  2169. dol_syslog("ExpenseReportLine::Insert rang=".$this->rang, LOG_DEBUG);
  2170. // Clean parameters
  2171. $this->comments=trim($this->comments);
  2172. if (!$this->value_unit_HT) $this->value_unit_HT=0;
  2173. $this->qty = price2num($this->qty);
  2174. $this->vatrate = price2num($this->vatrate);
  2175. if (empty($this->fk_c_exp_tax_cat)) $this->fk_c_exp_tax_cat = 0;
  2176. $this->db->begin();
  2177. $sql = 'INSERT INTO '.MAIN_DB_PREFIX.'expensereport_det';
  2178. $sql.= ' (fk_expensereport, fk_c_type_fees, fk_projet,';
  2179. $sql.= ' tva_tx, vat_src_code, comments, qty, value_unit, total_ht, total_tva, total_ttc, date, rule_warning_message, fk_c_exp_tax_cat)';
  2180. $sql.= " VALUES (".$this->db->escape($this->fk_expensereport).",";
  2181. $sql.= " ".$this->db->escape($this->fk_c_type_fees).",";
  2182. $sql.= " ".$this->db->escape($this->fk_projet>0?$this->fk_projet:'null').",";
  2183. $sql.= " ".$this->db->escape($this->vatrate).",";
  2184. $sql.= " '".$this->db->escape($this->vat_src_code)."',";
  2185. $sql.= " '".$this->db->escape($this->comments)."',";
  2186. $sql.= " ".$this->db->escape($this->qty).",";
  2187. $sql.= " ".$this->db->escape($this->value_unit).",";
  2188. $sql.= " ".$this->db->escape($this->total_ht).",";
  2189. $sql.= " ".$this->db->escape($this->total_tva).",";
  2190. $sql.= " ".$this->db->escape($this->total_ttc).",";
  2191. $sql.= "'".$this->db->idate($this->date)."',";
  2192. $sql.= " '".$this->db->escape($this->rule_warning_message)."',";
  2193. $sql.= " ".$this->db->escape($this->fk_c_exp_tax_cat);
  2194. $sql.= ")";
  2195. $resql=$this->db->query($sql);
  2196. if ($resql)
  2197. {
  2198. $this->rowid=$this->db->last_insert_id(MAIN_DB_PREFIX.'expensereport_det');
  2199. if (! $fromaddline)
  2200. {
  2201. $tmpparent=new ExpenseReport($this->db);
  2202. $tmpparent->fetch($this->fk_expensereport);
  2203. $result = $tmpparent->update_price();
  2204. if ($result < 0)
  2205. {
  2206. $error++;
  2207. $this->error = $tmpparent->error;
  2208. $this->errors = $tmpparent->errors;
  2209. }
  2210. }
  2211. }
  2212. else
  2213. {
  2214. $error++;
  2215. }
  2216. if (! $error)
  2217. {
  2218. $this->db->commit();
  2219. return $this->rowid;
  2220. }
  2221. else
  2222. {
  2223. $this->error=$this->db->lasterror();
  2224. dol_syslog("ExpenseReportLine::insert Error ".$this->error, LOG_ERR);
  2225. $this->db->rollback();
  2226. return -2;
  2227. }
  2228. }
  2229. /**
  2230. * Function to get total amount in expense reports for a same rule
  2231. *
  2232. * @param ExpenseReportRule $rule object rule to check
  2233. * @param int $fk_user user author id
  2234. * @param string $mode day|EX_DAY / month|EX_MON / year|EX_YEA to get amount
  2235. * @return amount
  2236. */
  2237. public function getExpAmount(ExpenseReportRule $rule, $fk_user, $mode='day')
  2238. {
  2239. $amount = 0;
  2240. $sql = 'SELECT SUM(d.total_ttc) as total_amount';
  2241. $sql .= ' FROM '.MAIN_DB_PREFIX.'expensereport_det d';
  2242. $sql .= ' INNER JOIN '.MAIN_DB_PREFIX.'expensereport e ON (d.fk_expensereport = e.rowid)';
  2243. $sql .= ' WHERE e.fk_user_author = '.$fk_user;
  2244. if (!empty($this->id)) $sql.= ' AND d.rowid <> '.$this->id;
  2245. $sql .= ' AND d.fk_c_type_fees = '.$rule->fk_c_type_fees;
  2246. if ($mode == 'day' || $mode == 'EX_DAY') $sql .= ' AND d.date = \''.dol_print_date($this->date, '%Y-%m-%d').'\'';
  2247. elseif ($mode == 'mon' || $mode == 'EX_MON') $sql .= ' AND DATE_FORMAT(d.date, \'%Y-%m\') = \''.dol_print_date($this->date, '%Y-%m').'\'';
  2248. elseif ($mode == 'year' || $mode == 'EX_YEA') $sql .= ' AND DATE_FORMAT(d.date, \'%Y\') = \''.dol_print_date($this->date, '%Y').'\'';
  2249. dol_syslog('ExpenseReportLine::getExpAmountByDay sql='.$sql);
  2250. $resql = $this->db->query($sql);
  2251. if ($resql)
  2252. {
  2253. $num = $this->db->num_rows($resql);
  2254. if ($num > 0)
  2255. {
  2256. $obj = $this->db->fetch_object($resql);
  2257. $amount = (double) $obj->total_amount;
  2258. }
  2259. }
  2260. else
  2261. {
  2262. dol_print_error($this->db);
  2263. }
  2264. return $amount + $this->total_ttc;
  2265. }
  2266. /**
  2267. * update
  2268. *
  2269. * @param User $fuser User
  2270. * @return int <0 if KO, >0 if OK
  2271. */
  2272. function update($fuser)
  2273. {
  2274. global $fuser,$langs,$conf;
  2275. $error=0;
  2276. // Clean parameters
  2277. $this->comments=trim($this->comments);
  2278. $this->vatrate = price2num($this->vatrate);
  2279. $this->value_unit = price2num($this->value_unit);
  2280. if (empty($this->fk_c_exp_tax_cat)) $this->fk_c_exp_tax_cat = 0;
  2281. $this->db->begin();
  2282. // Update line in database
  2283. $sql = "UPDATE ".MAIN_DB_PREFIX."expensereport_det SET";
  2284. $sql.= " comments='".$this->db->escape($this->comments)."'";
  2285. $sql.= ",value_unit=".$this->db->escape($this->value_unit);
  2286. $sql.= ",qty=".$this->db->escape($this->qty);
  2287. $sql.= ",date='".$this->db->idate($this->date)."'";
  2288. $sql.= ",total_ht=".$this->db->escape($this->total_ht)."";
  2289. $sql.= ",total_tva=".$this->db->escape($this->total_tva)."";
  2290. $sql.= ",total_ttc=".$this->db->escape($this->total_ttc)."";
  2291. $sql.= ",tva_tx=".$this->db->escape($this->vatrate);
  2292. $sql.= ",vat_src_code='".$this->db->escape($this->vat_src_code)."'";
  2293. $sql.= ",rule_warning_message='".$this->db->escape($this->rule_warning_message)."'";
  2294. $sql.= ",fk_c_exp_tax_cat=".$this->db->escape($this->fk_c_exp_tax_cat);
  2295. if ($this->fk_c_type_fees) $sql.= ",fk_c_type_fees=".$this->db->escape($this->fk_c_type_fees);
  2296. else $sql.= ",fk_c_type_fees=null";
  2297. if ($this->fk_projet) $sql.= ",fk_projet=".$this->db->escape($this->fk_projet);
  2298. else $sql.= ",fk_projet=null";
  2299. $sql.= " WHERE rowid = ".$this->db->escape($this->rowid);
  2300. dol_syslog("ExpenseReportLine::update sql=".$sql);
  2301. $resql=$this->db->query($sql);
  2302. if ($resql)
  2303. {
  2304. $tmpparent=new ExpenseReport($this->db);
  2305. $result = $tmpparent->fetch($this->fk_expensereport);
  2306. if ($result > 0)
  2307. {
  2308. $result = $tmpparent->update_price();
  2309. if ($result < 0)
  2310. {
  2311. $error++;
  2312. $this->error = $tmpparent->error;
  2313. $this->errors = $tmpparent->errors;
  2314. }
  2315. }
  2316. else
  2317. {
  2318. $error++;
  2319. $this->error = $tmpparent->error;
  2320. $this->errors = $tmpparent->errors;
  2321. }
  2322. }
  2323. else
  2324. {
  2325. $error++;
  2326. dol_print_error($this->db);
  2327. }
  2328. if (! $error)
  2329. {
  2330. $this->db->commit();
  2331. return 1;
  2332. }
  2333. else
  2334. {
  2335. $this->error=$this->db->lasterror();
  2336. dol_syslog("ExpenseReportLine::update Error ".$this->error, LOG_ERR);
  2337. $this->db->rollback();
  2338. return -2;
  2339. }
  2340. }
  2341. }
  2342. // phpcs:disable PEAR.NamingConventions.ValidFunctionName.NotCamelCaps
  2343. /**
  2344. * Retourne la liste deroulante des differents etats d'une note de frais.
  2345. * Les valeurs de la liste sont les id de la table c_expensereport_statuts
  2346. *
  2347. * @param int $selected preselect status
  2348. * @param string $htmlname Name of HTML select
  2349. * @param int $useempty 1=Add empty line
  2350. * @param int $useshortlabel Use short labels
  2351. * @return string HTML select with status
  2352. */
  2353. function select_expensereport_statut($selected='',$htmlname='fk_statut',$useempty=1, $useshortlabel=0)
  2354. {
  2355. // phpcs:enable
  2356. global $db, $langs;
  2357. $tmpep=new ExpenseReport($db);
  2358. print '<select class="flat" name="'.$htmlname.'">';
  2359. if ($useempty) print '<option value="-1">&nbsp;</option>';
  2360. $arrayoflabels=$tmpep->statuts;
  2361. if ($useshortlabel) $arrayoflabels=$tmpep->statuts_short;
  2362. foreach ($arrayoflabels as $key => $val)
  2363. {
  2364. if ($selected != '' && $selected == $key)
  2365. {
  2366. print '<option value="'.$key.'" selected>';
  2367. }
  2368. else
  2369. {
  2370. print '<option value="'.$key.'">';
  2371. }
  2372. print $langs->trans($val);
  2373. print '</option>';
  2374. }
  2375. print '</select>';
  2376. }
  2377. // phpcs:disable PEAR.NamingConventions.ValidFunctionName.NotCamelCaps
  2378. /**
  2379. * Return list of types of notes with select value = id
  2380. *
  2381. * @param int $selected Preselected type
  2382. * @param string $htmlname Name of field in form
  2383. * @param int $showempty Add an empty field
  2384. * @param int $active 1=Active only, 0=Unactive only, -1=All
  2385. * @return string Select html
  2386. */
  2387. function select_type_fees_id($selected='',$htmlname='type',$showempty=0, $active=1)
  2388. {
  2389. // phpcs:enable
  2390. global $db,$langs,$user;
  2391. $langs->load("trips");
  2392. print '<select class="flat" name="'.$htmlname.'">';
  2393. if ($showempty)
  2394. {
  2395. print '<option value="-1"';
  2396. if ($selected == -1) print ' selected';
  2397. print '>&nbsp;</option>';
  2398. }
  2399. $sql = "SELECT c.id, c.code, c.label as type FROM ".MAIN_DB_PREFIX."c_type_fees as c";
  2400. if ($active >= 0) $sql.= " WHERE c.active = ".$active;
  2401. $sql.= " ORDER BY c.label ASC";
  2402. $resql=$db->query($sql);
  2403. if ($resql)
  2404. {
  2405. $num = $db->num_rows($resql);
  2406. $i = 0;
  2407. while ($i < $num)
  2408. {
  2409. $obj = $db->fetch_object($resql);
  2410. print '<option value="'.$obj->id.'"';
  2411. if ($obj->code == $selected || $obj->id == $selected) print ' selected';
  2412. print '>';
  2413. if ($obj->code != $langs->trans($obj->code)) print $langs->trans($obj->code);
  2414. else print $langs->trans($obj->type);
  2415. $i++;
  2416. }
  2417. }
  2418. print '</select>';
  2419. }