commonobject.class.php 158 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412141314141415141614171418141914201421142214231424142514261427142814291430143114321433143414351436143714381439144014411442144314441445144614471448144914501451145214531454145514561457145814591460146114621463146414651466146714681469147014711472147314741475147614771478147914801481148214831484148514861487148814891490149114921493149414951496149714981499150015011502150315041505150615071508150915101511151215131514151515161517151815191520152115221523152415251526152715281529153015311532153315341535153615371538153915401541154215431544154515461547154815491550155115521553155415551556155715581559156015611562156315641565156615671568156915701571157215731574157515761577157815791580158115821583158415851586158715881589159015911592159315941595159615971598159916001601160216031604160516061607160816091610161116121613161416151616161716181619162016211622162316241625162616271628162916301631163216331634163516361637163816391640164116421643164416451646164716481649165016511652165316541655165616571658165916601661166216631664166516661667166816691670167116721673167416751676167716781679168016811682168316841685168616871688168916901691169216931694169516961697169816991700170117021703170417051706170717081709171017111712171317141715171617171718171917201721172217231724172517261727172817291730173117321733173417351736173717381739174017411742174317441745174617471748174917501751175217531754175517561757175817591760176117621763176417651766176717681769177017711772177317741775177617771778177917801781178217831784178517861787178817891790179117921793179417951796179717981799180018011802180318041805180618071808180918101811181218131814181518161817181818191820182118221823182418251826182718281829183018311832183318341835183618371838183918401841184218431844184518461847184818491850185118521853185418551856185718581859186018611862186318641865186618671868186918701871187218731874187518761877187818791880188118821883188418851886188718881889189018911892189318941895189618971898189919001901190219031904190519061907190819091910191119121913191419151916191719181919192019211922192319241925192619271928192919301931193219331934193519361937193819391940194119421943194419451946194719481949195019511952195319541955195619571958195919601961196219631964196519661967196819691970197119721973197419751976197719781979198019811982198319841985198619871988198919901991199219931994199519961997199819992000200120022003200420052006200720082009201020112012201320142015201620172018201920202021202220232024202520262027202820292030203120322033203420352036203720382039204020412042204320442045204620472048204920502051205220532054205520562057205820592060206120622063206420652066206720682069207020712072207320742075207620772078207920802081208220832084208520862087208820892090209120922093209420952096209720982099210021012102210321042105210621072108210921102111211221132114211521162117211821192120212121222123212421252126212721282129213021312132213321342135213621372138213921402141214221432144214521462147214821492150215121522153215421552156215721582159216021612162216321642165216621672168216921702171217221732174217521762177217821792180218121822183218421852186218721882189219021912192219321942195219621972198219922002201220222032204220522062207220822092210221122122213221422152216221722182219222022212222222322242225222622272228222922302231223222332234223522362237223822392240224122422243224422452246224722482249225022512252225322542255225622572258225922602261226222632264226522662267226822692270227122722273227422752276227722782279228022812282228322842285228622872288228922902291229222932294229522962297229822992300230123022303230423052306230723082309231023112312231323142315231623172318231923202321232223232324232523262327232823292330233123322333233423352336233723382339234023412342234323442345234623472348234923502351235223532354235523562357235823592360236123622363236423652366236723682369237023712372237323742375237623772378237923802381238223832384238523862387238823892390239123922393239423952396239723982399240024012402240324042405240624072408240924102411241224132414241524162417241824192420242124222423242424252426242724282429243024312432243324342435243624372438243924402441244224432444244524462447244824492450245124522453245424552456245724582459246024612462246324642465246624672468246924702471247224732474247524762477247824792480248124822483248424852486248724882489249024912492249324942495249624972498249925002501250225032504250525062507250825092510251125122513251425152516251725182519252025212522252325242525252625272528252925302531253225332534253525362537253825392540254125422543254425452546254725482549255025512552255325542555255625572558255925602561256225632564256525662567256825692570257125722573257425752576257725782579258025812582258325842585258625872588258925902591259225932594259525962597259825992600260126022603260426052606260726082609261026112612261326142615261626172618261926202621262226232624262526262627262826292630263126322633263426352636263726382639264026412642264326442645264626472648264926502651265226532654265526562657265826592660266126622663266426652666266726682669267026712672267326742675267626772678267926802681268226832684268526862687268826892690269126922693269426952696269726982699270027012702270327042705270627072708270927102711271227132714271527162717271827192720272127222723272427252726272727282729273027312732273327342735273627372738273927402741274227432744274527462747274827492750275127522753275427552756275727582759276027612762276327642765276627672768276927702771277227732774277527762777277827792780278127822783278427852786278727882789279027912792279327942795279627972798279928002801280228032804280528062807280828092810281128122813281428152816281728182819282028212822282328242825282628272828282928302831283228332834283528362837283828392840284128422843284428452846284728482849285028512852285328542855285628572858285928602861286228632864286528662867286828692870287128722873287428752876287728782879288028812882288328842885288628872888288928902891289228932894289528962897289828992900290129022903290429052906290729082909291029112912291329142915291629172918291929202921292229232924292529262927292829292930293129322933293429352936293729382939294029412942294329442945294629472948294929502951295229532954295529562957295829592960296129622963296429652966296729682969297029712972297329742975297629772978297929802981298229832984298529862987298829892990299129922993299429952996299729982999300030013002300330043005300630073008300930103011301230133014301530163017301830193020302130223023302430253026302730283029303030313032303330343035303630373038303930403041304230433044304530463047304830493050305130523053305430553056305730583059306030613062306330643065306630673068306930703071307230733074307530763077307830793080308130823083308430853086308730883089309030913092309330943095309630973098309931003101310231033104310531063107310831093110311131123113311431153116311731183119312031213122312331243125312631273128312931303131313231333134313531363137313831393140314131423143314431453146314731483149315031513152315331543155315631573158315931603161316231633164316531663167316831693170317131723173317431753176317731783179318031813182318331843185318631873188318931903191319231933194319531963197319831993200320132023203320432053206320732083209321032113212321332143215321632173218321932203221322232233224322532263227322832293230323132323233323432353236323732383239324032413242324332443245324632473248324932503251325232533254325532563257325832593260326132623263326432653266326732683269327032713272327332743275327632773278327932803281328232833284328532863287328832893290329132923293329432953296329732983299330033013302330333043305330633073308330933103311331233133314331533163317331833193320332133223323332433253326332733283329333033313332333333343335333633373338333933403341334233433344334533463347334833493350335133523353335433553356335733583359336033613362336333643365336633673368336933703371337233733374337533763377337833793380338133823383338433853386338733883389339033913392339333943395339633973398339934003401340234033404340534063407340834093410341134123413341434153416341734183419342034213422342334243425342634273428342934303431343234333434343534363437343834393440344134423443344434453446344734483449345034513452345334543455345634573458345934603461346234633464346534663467346834693470347134723473347434753476347734783479348034813482348334843485348634873488348934903491349234933494349534963497349834993500350135023503350435053506350735083509351035113512351335143515351635173518351935203521352235233524352535263527352835293530353135323533353435353536353735383539354035413542354335443545354635473548354935503551355235533554355535563557355835593560356135623563356435653566356735683569357035713572357335743575357635773578357935803581358235833584358535863587358835893590359135923593359435953596359735983599360036013602360336043605360636073608360936103611361236133614361536163617361836193620362136223623362436253626362736283629363036313632363336343635363636373638363936403641364236433644364536463647364836493650365136523653365436553656365736583659366036613662366336643665366636673668366936703671367236733674367536763677367836793680368136823683368436853686368736883689369036913692369336943695369636973698369937003701370237033704370537063707370837093710371137123713371437153716371737183719372037213722372337243725372637273728372937303731373237333734373537363737373837393740374137423743374437453746374737483749375037513752375337543755375637573758375937603761376237633764376537663767376837693770377137723773377437753776377737783779378037813782378337843785378637873788378937903791379237933794379537963797379837993800380138023803380438053806380738083809381038113812381338143815381638173818381938203821382238233824382538263827382838293830383138323833383438353836383738383839384038413842384338443845384638473848384938503851385238533854385538563857385838593860386138623863386438653866386738683869387038713872387338743875387638773878387938803881388238833884388538863887388838893890389138923893389438953896389738983899390039013902390339043905390639073908390939103911391239133914391539163917391839193920392139223923392439253926392739283929393039313932393339343935393639373938393939403941394239433944394539463947394839493950395139523953395439553956395739583959396039613962396339643965396639673968396939703971397239733974397539763977397839793980398139823983398439853986398739883989399039913992399339943995399639973998399940004001400240034004400540064007400840094010401140124013401440154016401740184019402040214022402340244025402640274028402940304031403240334034403540364037403840394040404140424043404440454046404740484049405040514052405340544055405640574058405940604061406240634064406540664067406840694070407140724073407440754076407740784079408040814082408340844085408640874088408940904091409240934094409540964097409840994100410141024103410441054106410741084109411041114112411341144115411641174118411941204121412241234124412541264127412841294130413141324133413441354136413741384139414041414142414341444145414641474148414941504151415241534154415541564157415841594160416141624163416441654166416741684169417041714172417341744175417641774178417941804181418241834184418541864187418841894190419141924193419441954196419741984199420042014202420342044205420642074208420942104211421242134214421542164217421842194220422142224223422442254226422742284229423042314232423342344235423642374238423942404241424242434244424542464247424842494250425142524253425442554256425742584259426042614262426342644265426642674268426942704271427242734274427542764277427842794280428142824283428442854286428742884289429042914292429342944295429642974298429943004301430243034304430543064307430843094310431143124313431443154316431743184319432043214322432343244325432643274328432943304331433243334334433543364337433843394340434143424343434443454346434743484349435043514352435343544355435643574358435943604361436243634364436543664367436843694370437143724373437443754376437743784379438043814382438343844385438643874388438943904391439243934394439543964397439843994400440144024403440444054406440744084409441044114412441344144415441644174418441944204421442244234424442544264427442844294430443144324433443444354436443744384439444044414442444344444445444644474448444944504451445244534454445544564457445844594460446144624463446444654466446744684469447044714472447344744475447644774478447944804481448244834484448544864487448844894490449144924493449444954496449744984499450045014502450345044505450645074508450945104511451245134514451545164517451845194520452145224523452445254526452745284529453045314532453345344535453645374538453945404541454245434544454545464547454845494550455145524553455445554556455745584559456045614562456345644565456645674568456945704571457245734574457545764577457845794580458145824583458445854586
  1. <?php
  2. /* Copyright (C) 2006-2015 Laurent Destailleur <eldy@users.sourceforge.net>
  3. * Copyright (C) 2005-2013 Regis Houssin <regis.houssin@capnetworks.com>
  4. * Copyright (C) 2010-2013 Juanjo Menent <jmenent@2byte.es>
  5. * Copyright (C) 2012 Christophe Battarel <christophe.battarel@altairis.fr>
  6. * Copyright (C) 2010-2015 Juanjo Menent <jmenent@2byte.es>
  7. * Copyright (C) 2012-2013 Christophe Battarel <christophe.battarel@altairis.fr>
  8. * Copyright (C) 2011-2014 Philippe Grand <philippe.grand@atoo-net.com>
  9. * Copyright (C) 2012-2015 Marcos García <marcosgdf@gmail.com>
  10. * Copyright (C) 2012-2015 Raphaël Doursenaud <rdoursenaud@gpcsolutions.fr>
  11. * Copyright (C) 2012 Cedric Salvador <csalvador@gpcsolutions.fr>
  12. * Copyright (C) 2015 Alexandre Spangaro <aspangaro.dolibarr@gmail.com>
  13. * Copyright (C) 2016 Bahfir abbes <dolipar@dolipar.org>
  14. *
  15. * This program is free software; you can redistribute it and/or modify
  16. * it under the terms of the GNU General Public License as published by
  17. * the Free Software Foundation; either version 3 of the License, or
  18. * (at your option) any later version.
  19. *
  20. * This program is distributed in the hope that it will be useful,
  21. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  22. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  23. * GNU General Public License for more details.
  24. *
  25. * You should have received a copy of the GNU General Public License
  26. * along with this program. If not, see <http://www.gnu.org/licenses/>.
  27. */
  28. /**
  29. * \file htdocs/core/class/commonobject.class.php
  30. * \ingroup core
  31. * \brief File of parent class of all other business classes (invoices, contracts, proposals, orders, ...)
  32. */
  33. /**
  34. * Parent class of all other business classes (invoices, contracts, proposals, orders, ...)
  35. */
  36. abstract class CommonObject
  37. {
  38. /**
  39. * @var DoliDb Database handler (result of a new DoliDB)
  40. */
  41. public $db;
  42. /**
  43. * @var int The object identifier
  44. */
  45. public $id;
  46. /**
  47. * @var string Error string
  48. * @deprecated Use instead the array of error strings
  49. * @see errors
  50. */
  51. public $error;
  52. /**
  53. * @var string[] Array of error strings
  54. */
  55. public $errors=array();
  56. /**
  57. * @var string Key value used to track if data is coming from import wizard
  58. */
  59. public $import_key;
  60. /**
  61. * @var mixed Contains data to manage extrafields
  62. */
  63. public $array_options=array();
  64. /**
  65. * @var int[] Array of linked objects ids. Loaded by ->fetchObjectLinked
  66. */
  67. public $linkedObjectsIds;
  68. /**
  69. * @var mixed Array of linked objects. Loaded by ->fetchObjectLinked
  70. */
  71. public $linkedObjects;
  72. /**
  73. * @var string Column name of the ref field.
  74. */
  75. protected $table_ref_field = '';
  76. // Following vars are used by some objects only. We keep this property here in CommonObject to be able to provide common method using them.
  77. /**
  78. * @var string[] Can be used to pass information when only object is provided to method
  79. */
  80. public $context=array();
  81. /**
  82. * @var string Contains canvas name if record is an alternative canvas record
  83. */
  84. public $canvas;
  85. /**
  86. * @var Project The related project
  87. * @see fetch_projet()
  88. */
  89. public $project;
  90. /**
  91. * @var int The related project ID
  92. * @see setProject(), project
  93. */
  94. public $fk_project;
  95. /**
  96. * @deprecated
  97. * @see project
  98. */
  99. public $projet;
  100. /**
  101. * @var Contact a related contact
  102. * @see fetch_contact()
  103. */
  104. public $contact;
  105. /**
  106. * @var int The related contact ID
  107. * @see fetch_contact()
  108. */
  109. public $contact_id;
  110. /**
  111. * @var Societe A related thirdparty
  112. * @see fetch_thirdparty()
  113. */
  114. public $thirdparty;
  115. /**
  116. * @var User A related user
  117. * @see fetch_user()
  118. */
  119. public $user;
  120. /**
  121. * @var CommonObject An originating object?
  122. * @see fetch_origin()
  123. */
  124. public $origin;
  125. /**
  126. * @var int The originating object?
  127. * @see fetch_origin(), origin
  128. */
  129. public $origin_id;
  130. /**
  131. * @var string The object's reference
  132. */
  133. public $ref;
  134. /**
  135. * @var string The object's previous reference
  136. */
  137. public $ref_previous;
  138. /**
  139. * @var string The object's next reference
  140. */
  141. public $ref_next;
  142. /**
  143. * @var string An external reference for the object
  144. */
  145. public $ref_ext;
  146. /**
  147. * @var string
  148. */
  149. public $element;
  150. /**
  151. * @var string
  152. */
  153. public $table_element;
  154. /**
  155. * @var
  156. */
  157. public $table_element_line;
  158. /**
  159. * @var int The object's status
  160. * @see setStatut()
  161. */
  162. public $statut;
  163. /**
  164. * @var string
  165. * @see getFullAddress()
  166. */
  167. public $country;
  168. /**
  169. * @var int
  170. * @see getFullAddress(), country
  171. */
  172. public $country_id;
  173. /**
  174. * @var string
  175. * @see getFullAddress(), isInEEC(), country
  176. */
  177. public $country_code;
  178. /**
  179. * @var int
  180. * @see fetch_barcode()
  181. */
  182. public $barcode_type;
  183. /**
  184. * @var string
  185. * @see fetch_barcode(), barcode_type
  186. */
  187. public $barcode_type_code;
  188. /**
  189. * @var string
  190. * @see fetch_barcode(), barcode_type
  191. */
  192. public $barcode_type_label;
  193. /**
  194. * @var string
  195. * @see fetch_barcode(), barcode_type
  196. */
  197. public $barcode_type_coder;
  198. /**
  199. * @var int Payment method ID (cheque, cash, ...)
  200. * @see setPaymentMethods()
  201. */
  202. public $mode_reglement_id;
  203. /**
  204. * @var int Payment terms ID
  205. * @see setPaymentTerms()
  206. */
  207. public $cond_reglement_id;
  208. /**
  209. * @var int Payment terms ID
  210. * @deprecated Kept for compatibility
  211. * @see cond_reglement_id;
  212. */
  213. public $cond_reglement;
  214. /**
  215. * @var int Delivery address ID
  216. * @see setDeliveryAddress()
  217. */
  218. public $fk_delivery_address;
  219. /**
  220. * @var int Shipping method ID
  221. * @see setShippingMethod()
  222. */
  223. public $shipping_method_id;
  224. /**
  225. * @var string
  226. * @see SetDocModel()
  227. */
  228. public $modelpdf;
  229. /**
  230. * @var int Bank account ID
  231. * @see SetBankAccount()
  232. */
  233. public $fk_account;
  234. /**
  235. * @var string Public note
  236. * @see update_note()
  237. */
  238. public $note_public;
  239. /**
  240. * @var string Private note
  241. * @see update_note()
  242. */
  243. public $note_private;
  244. /**
  245. * @deprecated
  246. * @see note_public
  247. */
  248. public $note;
  249. /**
  250. * @var float Total amount before taxes
  251. * @see update_price()
  252. */
  253. public $total_ht;
  254. /**
  255. * @var float Total VAT amount
  256. * @see update_price()
  257. */
  258. public $total_tva;
  259. /**
  260. * @var float Total local tax 1 amount
  261. * @see update_price()
  262. */
  263. public $total_localtax1;
  264. /**
  265. * @var float Total local tax 2 amount
  266. * @see update_price()
  267. */
  268. public $total_localtax2;
  269. /**
  270. * @var float Total amount with taxes
  271. * @see update_price()
  272. */
  273. public $total_ttc;
  274. /**
  275. * @var CommonObjectLine[]
  276. */
  277. public $lines;
  278. /**
  279. * @var int
  280. * @see setIncoterms()
  281. */
  282. public $fk_incoterms;
  283. /**
  284. * @var string
  285. * @see SetIncoterms()
  286. */
  287. public $libelle_incoterms;
  288. /**
  289. * @var string
  290. * @see display_incoterms()
  291. */
  292. public $location_incoterms;
  293. public $name;
  294. public $lastname;
  295. public $firstname;
  296. public $civility_id;
  297. // No constructor as it is an abstract class
  298. /**
  299. * Check an object id/ref exists
  300. * If you don't need/want to instantiate object and just need to know if object exists, use this method instead of fetch
  301. *
  302. * @param string $element String of element ('product', 'facture', ...)
  303. * @param int $id Id of object
  304. * @param string $ref Ref of object to check
  305. * @param string $ref_ext Ref ext of object to check
  306. * @return int <0 if KO, 0 if OK but not found, >0 if OK and exists
  307. */
  308. static function isExistingObject($element, $id, $ref='', $ref_ext='')
  309. {
  310. global $db,$conf;
  311. $sql = "SELECT rowid, ref, ref_ext";
  312. $sql.= " FROM ".MAIN_DB_PREFIX.$element;
  313. $sql.= " WHERE entity IN (".getEntity($element, true).")" ;
  314. if ($id > 0) $sql.= " AND rowid = ".$db->escape($id);
  315. else if ($ref) $sql.= " AND ref = '".$db->escape($ref)."'";
  316. else if ($ref_ext) $sql.= " AND ref_ext = '".$db->escape($ref_ext)."'";
  317. else {
  318. $error='ErrorWrongParameters';
  319. dol_print_error(get_class()."::isExistingObject ".$error, LOG_ERR);
  320. return -1;
  321. }
  322. if ($ref || $ref_ext) $sql.= " AND entity = ".$conf->entity;
  323. dol_syslog(get_class()."::isExistingObject", LOG_DEBUG);
  324. $resql = $db->query($sql);
  325. if ($resql)
  326. {
  327. $num=$db->num_rows($resql);
  328. if ($num > 0) return 1;
  329. else return 0;
  330. }
  331. return -1;
  332. }
  333. /**
  334. * Method to output saved errors
  335. *
  336. * @return string String with errors
  337. */
  338. function errorsToString()
  339. {
  340. return $this->error.(is_array($this->errors)?(($this->error!=''?', ':'').join(', ',$this->errors)):'');
  341. }
  342. /**
  343. * Return full name (civility+' '+name+' '+lastname)
  344. *
  345. * @param Translate $langs Language object for translation of civility
  346. * @param int $option 0=No option, 1=Add civility
  347. * @param int $nameorder -1=Auto, 0=Lastname+Firstname, 1=Firstname+Lastname, 2=Firstname
  348. * @param int $maxlen Maximum length
  349. * @return string String with full name
  350. */
  351. function getFullName($langs,$option=0,$nameorder=-1,$maxlen=0)
  352. {
  353. //print "lastname=".$this->lastname." name=".$this->name." nom=".$this->nom."<br>\n";
  354. $lastname=$this->lastname;
  355. $firstname=$this->firstname;
  356. if (empty($lastname)) $lastname=(isset($this->lastname)?$this->lastname:(isset($this->name)?$this->name:(isset($this->nom)?$this->nom:'')));
  357. $ret='';
  358. if ($option && $this->civility_id)
  359. {
  360. if ($langs->transnoentitiesnoconv("Civility".$this->civility_id)!="Civility".$this->civility_id) $ret.=$langs->transnoentitiesnoconv("Civility".$this->civility_id).' ';
  361. else $ret.=$this->civility_id.' ';
  362. }
  363. $ret.=dolGetFirstLastname($firstname, $lastname, $nameorder);
  364. return dol_trunc($ret,$maxlen);
  365. }
  366. /**
  367. * Return full address of contact
  368. *
  369. * @param int $withcountry 1=Add country into address string
  370. * @param string $sep Separator to use to build string
  371. * @return string Full address string
  372. */
  373. function getFullAddress($withcountry=0,$sep="\n")
  374. {
  375. if ($withcountry && $this->country_id && (empty($this->country_code) || empty($this->country)))
  376. {
  377. require_once DOL_DOCUMENT_ROOT .'/core/lib/company.lib.php';
  378. $tmparray=getCountry($this->country_id,'all');
  379. $this->country_code=$tmparray['code'];
  380. $this->country =$tmparray['label'];
  381. }
  382. return dol_format_address($this, $withcountry, $sep);
  383. }
  384. /**
  385. * Return full address of contact
  386. *
  387. * @param string $htmlkey HTML id to make banner content unique
  388. * @param Object $object Object (thirdparty, thirdparty of contact for contact, null for a member)
  389. * @return string Full address string
  390. */
  391. function getBannerAddress($htmlkey, $object)
  392. {
  393. global $conf, $langs;
  394. $countriesusingstate=array('AU','US','IN','GB','ES','UK','TR'); // See also option MAIN_FORCE_STATE_INTO_ADDRESS
  395. $contactid=0;
  396. $thirdpartyid=0;
  397. if ($this->element == 'societe')
  398. {
  399. $thirdpartyid=$this->id;
  400. }
  401. if ($this->element == 'contact')
  402. {
  403. $contactid=$this->id;
  404. $thirdpartyid=$object->fk_soc;
  405. }
  406. if ($this->element == 'user')
  407. {
  408. $contactid=$this->contact_id;
  409. $thirdpartyid=$object->fk_soc;
  410. }
  411. $out='<!-- BEGIN part to show address block -->';
  412. $outdone=0;
  413. $coords = $this->getFullAddress(1,', ');
  414. if ($coords)
  415. {
  416. if (! empty($conf->use_javascript_ajax))
  417. {
  418. $namecoords = $this->getFullName($langs,1).'<br>'.$coords;
  419. // hideonsmatphone because copyToClipboard call jquery dialog that does not work with jmobile
  420. $out.='<a href="#" class="hideonsmartphone" onclick="return copyToClipboard(\''.dol_escape_js($namecoords).'\',\''.dol_escape_js($langs->trans("HelpCopyToClipboard")).'\');">';
  421. $out.=img_picto($langs->trans("Address"), 'object_address.png');
  422. $out.='</a> ';
  423. }
  424. $out.=dol_print_address($coords, 'address_'.$htmlkey.'_'.$this->id, $this->element, $this->id, 1); $outdone++;
  425. $outdone++;
  426. }
  427. if (! in_array($this->country_code,$countriesusingstate) && empty($conf->global->MAIN_FORCE_STATE_INTO_ADDRESS) // If MAIN_FORCE_STATE_INTO_ADDRESS is on, state is already returned previously with getFullAddress
  428. && empty($conf->global->SOCIETE_DISABLE_STATE) && $this->state)
  429. {
  430. $out.=($outdone?' - ':'').$this->state;
  431. $outdone++;
  432. }
  433. if (! empty($this->phone) || ! empty($this->phone_pro) || ! empty($this->phone_mobile) || ! empty($this->phone_perso) || ! empty($this->fax) || ! empty($this->office_phone) || ! empty($this->user_mobile) || ! empty($this->office_fax)) $out.=($outdone?'<br>':'');
  434. if (! empty($this->phone) && empty($this->phone_pro)) { // For objects that store pro phone into ->phone
  435. $out.=dol_print_phone($this->phone,$this->country_code,$contactid,$thirdpartyid,'AC_TEL','&nbsp;','phone',$langs->trans("PhonePro")); $outdone++;
  436. }
  437. if (! empty($this->phone_pro)) {
  438. $out.=dol_print_phone($this->phone_pro,$this->country_code,$contactid,$thirdpartyid,'AC_TEL','&nbsp;','phone',$langs->trans("PhonePro")); $outdone++;
  439. }
  440. if (! empty($this->phone_mobile)) {
  441. $out.=dol_print_phone($this->phone_mobile,$this->country_code,$contactid,$thirdpartyid,'AC_TEL','&nbsp;','phone',$langs->trans("PhoneMobile")); $outdone++;
  442. }
  443. if (! empty($this->phone_perso)) {
  444. $out.=dol_print_phone($this->phone_perso,$this->country_code,$contactid,$thirdpartyid,'AC_TEL','&nbsp;','phone',$langs->trans("PhonePerso")); $outdone++;
  445. }
  446. if (! empty($this->fax)) {
  447. $out.=dol_print_phone($this->fax,$this->country_code,$contactid,$thirdpartyid,'AC_FAX','&nbsp;','fax',$langs->trans("Fax")); $outdone++;
  448. }
  449. if (! empty($this->office_phone)) {
  450. $out.=dol_print_phone($this->office_phone,$this->country_code,$contactid,$thirdpartyid,'AC_TEL','&nbsp;','phone',$langs->trans("PhonePro")); $outdone++;
  451. }
  452. if (! empty($this->user_mobile)) {
  453. $out.=dol_print_phone($this->user_mobile,$this->country_code,$contactid,$thirdpartyid,'AC_TEL','&nbsp;','phone',$langs->trans("PhoneMobile")); $outdone++;
  454. }
  455. if (! empty($this->office_fax)) {
  456. $out.=dol_print_phone($this->fax,$this->country_code,$contactid,$thirdpartyid,'AC_FAX','&nbsp;','fax',$langs->trans("Fax")); $outdone++;
  457. }
  458. $out.='<div style="clear: both;"></div>';
  459. $outdone=0;
  460. if (! empty($this->email))
  461. {
  462. $out.=dol_print_email($this->email,$this->id,$object->id,'AC_EMAIL',0,0,1);
  463. $outdone++;
  464. }
  465. if (! empty($this->url))
  466. {
  467. $out.=dol_print_url($this->url,'',0,1);
  468. $outdone++;
  469. }
  470. if (! empty($conf->skype->enabled))
  471. {
  472. $out.='<div style="clear: both;"></div>';
  473. if ($this->skype) $out.=dol_print_skype($this->skype,$this->id,$object->id,'AC_SKYPE');
  474. $outdone++;
  475. }
  476. $out.='<!-- END Part to show address block -->';
  477. return $out;
  478. }
  479. /**
  480. * Add a link between element $this->element and a contact
  481. *
  482. * @param int $fk_socpeople Id of thirdparty contact (if source = 'external') or id of user (if souce = 'internal') to link
  483. * @param int $type_contact Type of contact (code or id). Must be if or code found into table llx_c_type_contact. For example: SALESREPFOLL
  484. * @param string $source external=Contact extern (llx_socpeople), internal=Contact intern (llx_user)
  485. * @param int $notrigger Disable all triggers
  486. * @return int <0 if KO, >0 if OK
  487. */
  488. function add_contact($fk_socpeople, $type_contact, $source='external',$notrigger=0)
  489. {
  490. global $user,$langs;
  491. dol_syslog(get_class($this)."::add_contact $fk_socpeople, $type_contact, $source");
  492. // Check parameters
  493. if ($fk_socpeople <= 0)
  494. {
  495. $langs->load("errors");
  496. $this->error=$langs->trans("ErrorWrongValueForParameterX","1");
  497. dol_syslog(get_class($this)."::add_contact ".$this->error,LOG_ERR);
  498. return -1;
  499. }
  500. if (! $type_contact)
  501. {
  502. $langs->load("errors");
  503. $this->error=$langs->trans("ErrorWrongValueForParameterX","2");
  504. dol_syslog(get_class($this)."::add_contact ".$this->error,LOG_ERR);
  505. return -2;
  506. }
  507. $id_type_contact=0;
  508. if (is_numeric($type_contact))
  509. {
  510. $id_type_contact=$type_contact;
  511. }
  512. else
  513. {
  514. // On recherche id type_contact
  515. $sql = "SELECT tc.rowid";
  516. $sql.= " FROM ".MAIN_DB_PREFIX."c_type_contact as tc";
  517. $sql.= " WHERE tc.element='".$this->element."'";
  518. $sql.= " AND tc.source='".$source."'";
  519. $sql.= " AND tc.code='".$type_contact."' AND tc.active=1";
  520. //print $sql;
  521. $resql=$this->db->query($sql);
  522. if ($resql)
  523. {
  524. $obj = $this->db->fetch_object($resql);
  525. $id_type_contact=$obj->rowid;
  526. }
  527. }
  528. if(! empty($id_type_contact)) {
  529. $datecreate = dol_now();
  530. $this->db->begin();
  531. // Insertion dans la base
  532. $sql = "INSERT INTO ".MAIN_DB_PREFIX."element_contact";
  533. $sql.= " (element_id, fk_socpeople, datecreate, statut, fk_c_type_contact) ";
  534. $sql.= " VALUES (".$this->id.", ".$fk_socpeople." , " ;
  535. $sql.= "'".$this->db->idate($datecreate)."'";
  536. $sql.= ", 4, ". $id_type_contact . " ";
  537. $sql.= ")";
  538. dol_syslog(get_class($this)."::add_contact", LOG_DEBUG);
  539. $resql=$this->db->query($sql);
  540. if ($resql)
  541. {
  542. if (! $notrigger)
  543. {
  544. $result=$this->call_trigger(strtoupper($this->element).'_ADD_CONTACT', $user);
  545. if ($result < 0)
  546. {
  547. $this->db->rollback();
  548. return -1;
  549. }
  550. }
  551. $this->db->commit();
  552. return 1;
  553. }
  554. else
  555. {
  556. if ($this->db->errno() == 'DB_ERROR_RECORD_ALREADY_EXISTS')
  557. {
  558. $this->error=$this->db->errno();
  559. $this->db->rollback();
  560. return -2;
  561. }
  562. else
  563. {
  564. $this->error=$this->db->error();
  565. $this->db->rollback();
  566. return -1;
  567. }
  568. }
  569. }
  570. return 1;
  571. }
  572. /**
  573. * Copy contact from one element to current
  574. *
  575. * @param CommonObject $objFrom Source element
  576. * @param string $source Nature of contact ('internal' or 'external')
  577. * @return int >0 if OK, <0 if KO
  578. */
  579. function copy_linked_contact($objFrom, $source='internal')
  580. {
  581. $contacts = $objFrom->liste_contact(-1, $source);
  582. foreach($contacts as $contact)
  583. {
  584. if ($this->add_contact($contact['id'], $contact['fk_c_type_contact'], $contact['source']) < 0)
  585. {
  586. $this->error=$this->db->lasterror();
  587. return -1;
  588. }
  589. }
  590. return 1;
  591. }
  592. /**
  593. * Update a link to contact line
  594. *
  595. * @param int $rowid Id of line contact-element
  596. * @param int $statut New status of link
  597. * @param int $type_contact_id Id of contact type (not modified if 0)
  598. * @param int $fk_socpeople Id of soc_people to update (not modified if 0)
  599. * @return int <0 if KO, >= 0 if OK
  600. */
  601. function update_contact($rowid, $statut, $type_contact_id=0, $fk_socpeople=0)
  602. {
  603. // Insertion dans la base
  604. $sql = "UPDATE ".MAIN_DB_PREFIX."element_contact set";
  605. $sql.= " statut = ".$statut;
  606. if ($type_contact_id) $sql.= ", fk_c_type_contact = '".$type_contact_id ."'";
  607. if ($fk_socpeople) $sql.= ", fk_socpeople = '".$fk_socpeople ."'";
  608. $sql.= " where rowid = ".$rowid;
  609. $resql=$this->db->query($sql);
  610. if ($resql)
  611. {
  612. return 0;
  613. }
  614. else
  615. {
  616. $this->error=$this->db->lasterror();
  617. return -1;
  618. }
  619. }
  620. /**
  621. * Delete a link to contact line
  622. *
  623. * @param int $rowid Id of contact link line to delete
  624. * @param int $notrigger Disable all triggers
  625. * @return int >0 if OK, <0 if KO
  626. */
  627. function delete_contact($rowid, $notrigger=0)
  628. {
  629. global $user;
  630. $this->db->begin();
  631. $sql = "DELETE FROM ".MAIN_DB_PREFIX."element_contact";
  632. $sql.= " WHERE rowid =".$rowid;
  633. dol_syslog(get_class($this)."::delete_contact", LOG_DEBUG);
  634. if ($this->db->query($sql))
  635. {
  636. if (! $notrigger)
  637. {
  638. $result=$this->call_trigger(strtoupper($this->element).'_DELETE_CONTACT', $user);
  639. if ($result < 0) { $this->db->rollback(); return -1; }
  640. }
  641. $this->db->commit();
  642. return 1;
  643. }
  644. else
  645. {
  646. $this->error=$this->db->lasterror();
  647. $this->db->rollback();
  648. return -1;
  649. }
  650. }
  651. /**
  652. * Delete all links between an object $this and all its contacts
  653. *
  654. * @param string $source '' or 'internal' or 'external'
  655. * @param string $code Type of contact (code or id)
  656. * @return int >0 if OK, <0 if KO
  657. */
  658. function delete_linked_contact($source='',$code='')
  659. {
  660. $temp = array();
  661. $typeContact = $this->liste_type_contact($source,'',0,0,$code);
  662. foreach($typeContact as $key => $value)
  663. {
  664. array_push($temp,$key);
  665. }
  666. $listId = implode(",", $temp);
  667. $sql = "DELETE FROM ".MAIN_DB_PREFIX."element_contact";
  668. $sql.= " WHERE element_id = ".$this->id;
  669. if ($listId)
  670. $sql.= " AND fk_c_type_contact IN (".$listId.")";
  671. dol_syslog(get_class($this)."::delete_linked_contact", LOG_DEBUG);
  672. if ($this->db->query($sql))
  673. {
  674. return 1;
  675. }
  676. else
  677. {
  678. $this->error=$this->db->lasterror();
  679. return -1;
  680. }
  681. }
  682. /**
  683. * Get array of all contacts for an object
  684. *
  685. * @param int $statut Status of links to get (-1=all)
  686. * @param string $source Source of contact: external or thirdparty (llx_socpeople) or internal (llx_user)
  687. * @param int $list 0:Return array contains all properties, 1:Return array contains just id
  688. * @param string $code Filter on this code of contact type ('SHIPPING', 'BILLING', ...)
  689. * @return array Array of contacts
  690. */
  691. function liste_contact($statut=-1,$source='external',$list=0,$code='')
  692. {
  693. global $langs;
  694. $tab=array();
  695. $sql = "SELECT ec.rowid, ec.statut as statuslink, ec.fk_socpeople as id, ec.fk_c_type_contact"; // This field contains id of llx_socpeople or id of llx_user
  696. if ($source == 'internal') $sql.=", '-1' as socid, t.statut as statuscontact";
  697. if ($source == 'external' || $source == 'thirdparty') $sql.=", t.fk_soc as socid, t.statut as statuscontact";
  698. $sql.= ", t.civility as civility, t.lastname as lastname, t.firstname, t.email";
  699. $sql.= ", tc.source, tc.element, tc.code, tc.libelle";
  700. $sql.= " FROM ".MAIN_DB_PREFIX."c_type_contact tc";
  701. $sql.= ", ".MAIN_DB_PREFIX."element_contact ec";
  702. if ($source == 'internal') $sql.=" LEFT JOIN ".MAIN_DB_PREFIX."user t on ec.fk_socpeople = t.rowid";
  703. if ($source == 'external'|| $source == 'thirdparty') $sql.=" LEFT JOIN ".MAIN_DB_PREFIX."socpeople t on ec.fk_socpeople = t.rowid";
  704. $sql.= " WHERE ec.element_id =".$this->id;
  705. $sql.= " AND ec.fk_c_type_contact=tc.rowid";
  706. $sql.= " AND tc.element='".$this->element."'";
  707. if ($code) $sql.= " AND tc.code = '".$this->db->escape($code)."'";
  708. if ($source == 'internal') $sql.= " AND tc.source = 'internal'";
  709. if ($source == 'external' || $source == 'thirdparty') $sql.= " AND tc.source = 'external'";
  710. $sql.= " AND tc.active=1";
  711. if ($statut >= 0) $sql.= " AND ec.statut = '".$statut."'";
  712. $sql.=" ORDER BY t.lastname ASC";
  713. dol_syslog(get_class($this)."::liste_contact", LOG_DEBUG);
  714. $resql=$this->db->query($sql);
  715. if ($resql)
  716. {
  717. $num=$this->db->num_rows($resql);
  718. $i=0;
  719. while ($i < $num)
  720. {
  721. $obj = $this->db->fetch_object($resql);
  722. if (! $list)
  723. {
  724. $transkey="TypeContact_".$obj->element."_".$obj->source."_".$obj->code;
  725. $libelle_type=($langs->trans($transkey)!=$transkey ? $langs->trans($transkey) : $obj->libelle);
  726. $tab[$i]=array('source'=>$obj->source,'socid'=>$obj->socid,'id'=>$obj->id,
  727. 'nom'=>$obj->lastname, // For backward compatibility
  728. 'civility'=>$obj->civility, 'lastname'=>$obj->lastname, 'firstname'=>$obj->firstname, 'email'=>$obj->email, 'statuscontact'=>$obj->statuscontact,
  729. 'rowid'=>$obj->rowid, 'code'=>$obj->code, 'libelle'=>$libelle_type, 'status'=>$obj->statuslink, 'fk_c_type_contact'=>$obj->fk_c_type_contact);
  730. }
  731. else
  732. {
  733. $tab[$i]=$obj->id;
  734. }
  735. $i++;
  736. }
  737. return $tab;
  738. }
  739. else
  740. {
  741. $this->error=$this->db->lasterror();
  742. dol_print_error($this->db);
  743. return -1;
  744. }
  745. }
  746. /**
  747. * Update status of a contact linked to object
  748. *
  749. * @param int $rowid Id of link between object and contact
  750. * @return int <0 if KO, >=0 if OK
  751. */
  752. function swapContactStatus($rowid)
  753. {
  754. $sql = "SELECT ec.datecreate, ec.statut, ec.fk_socpeople, ec.fk_c_type_contact,";
  755. $sql.= " tc.code, tc.libelle";
  756. //$sql.= ", s.fk_soc";
  757. $sql.= " FROM (".MAIN_DB_PREFIX."element_contact as ec, ".MAIN_DB_PREFIX."c_type_contact as tc)";
  758. //$sql.= " LEFT JOIN ".MAIN_DB_PREFIX."socpeople as s ON ec.fk_socpeople=s.rowid"; // Si contact de type external, alors il est lie a une societe
  759. $sql.= " WHERE ec.rowid =".$rowid;
  760. $sql.= " AND ec.fk_c_type_contact=tc.rowid";
  761. $sql.= " AND tc.element = '".$this->element."'";
  762. dol_syslog(get_class($this)."::swapContactStatus", LOG_DEBUG);
  763. $resql=$this->db->query($sql);
  764. if ($resql)
  765. {
  766. $obj = $this->db->fetch_object($resql);
  767. $newstatut = ($obj->statut == 4) ? 5 : 4;
  768. $result = $this->update_contact($rowid, $newstatut);
  769. $this->db->free($resql);
  770. return $result;
  771. }
  772. else
  773. {
  774. $this->error=$this->db->error();
  775. dol_print_error($this->db);
  776. return -1;
  777. }
  778. }
  779. /**
  780. * Return array with list of possible values for type of contacts
  781. *
  782. * @param string $source 'internal', 'external' or 'all'
  783. * @param string $order Sort order by : 'position', 'code', 'rowid'...
  784. * @param int $option 0=Return array id->label, 1=Return array code->label
  785. * @param int $activeonly 0=all status of contact, 1=only the active
  786. * @param string $code Type of contact (Example: 'CUSTOMER', 'SERVICE')
  787. * @return array Array list of type of contacts (id->label if option=0, code->label if option=1)
  788. */
  789. function liste_type_contact($source='internal', $order='position', $option=0, $activeonly=0, $code='')
  790. {
  791. global $langs;
  792. if (empty($order)) $order='position';
  793. if ($order == 'position') $order.=',code';
  794. $tab = array();
  795. $sql = "SELECT DISTINCT tc.rowid, tc.code, tc.libelle, tc.position";
  796. $sql.= " FROM ".MAIN_DB_PREFIX."c_type_contact as tc";
  797. $sql.= " WHERE tc.element='".$this->element."'";
  798. if ($activeonly == 1) $sql.= " AND tc.active=1"; // only the active types
  799. if (! empty($source) && $source != 'all') $sql.= " AND tc.source='".$source."'";
  800. if (! empty($code)) $sql.= " AND tc.code='".$code."'";
  801. $sql.= $this->db->order($order,'ASC');
  802. //print "sql=".$sql;
  803. $resql=$this->db->query($sql);
  804. if ($resql)
  805. {
  806. $num=$this->db->num_rows($resql);
  807. $i=0;
  808. while ($i < $num)
  809. {
  810. $obj = $this->db->fetch_object($resql);
  811. $transkey="TypeContact_".$this->element."_".$source."_".$obj->code;
  812. $libelle_type=($langs->trans($transkey)!=$transkey ? $langs->trans($transkey) : $obj->libelle);
  813. if (empty($option)) $tab[$obj->rowid]=$libelle_type;
  814. else $tab[$obj->code]=$libelle_type;
  815. $i++;
  816. }
  817. return $tab;
  818. }
  819. else
  820. {
  821. $this->error=$this->db->lasterror();
  822. //dol_print_error($this->db);
  823. return null;
  824. }
  825. }
  826. /**
  827. * Return id of contacts for a source and a contact code.
  828. * Example: contact client de facturation ('external', 'BILLING')
  829. * Example: contact client de livraison ('external', 'SHIPPING')
  830. * Example: contact interne suivi paiement ('internal', 'SALESREPFOLL')
  831. *
  832. * @param string $source 'external' or 'internal'
  833. * @param string $code 'BILLING', 'SHIPPING', 'SALESREPFOLL', ...
  834. * @param int $status limited to a certain status
  835. * @return array List of id for such contacts
  836. */
  837. function getIdContact($source,$code,$status=0)
  838. {
  839. global $conf;
  840. $result=array();
  841. $i=0;
  842. $sql = "SELECT ec.fk_socpeople";
  843. $sql.= " FROM ".MAIN_DB_PREFIX."element_contact as ec,";
  844. if ($source == 'internal') $sql.= " ".MAIN_DB_PREFIX."user as c,";
  845. if ($source == 'external') $sql.= " ".MAIN_DB_PREFIX."socpeople as c,";
  846. $sql.= " ".MAIN_DB_PREFIX."c_type_contact as tc";
  847. $sql.= " WHERE ec.element_id = ".$this->id;
  848. $sql.= " AND ec.fk_socpeople = c.rowid";
  849. if ($source == 'internal') $sql.= " AND c.entity IN (0,".$conf->entity.")";
  850. if ($source == 'external') $sql.= " AND c.entity IN (".getEntity('societe', 1).")";
  851. $sql.= " AND ec.fk_c_type_contact = tc.rowid";
  852. $sql.= " AND tc.element = '".$this->element."'";
  853. $sql.= " AND tc.source = '".$source."'";
  854. $sql.= " AND tc.code = '".$code."'";
  855. $sql.= " AND tc.active = 1";
  856. if ($status) $sql.= " AND ec.statut = ".$status;
  857. dol_syslog(get_class($this)."::getIdContact", LOG_DEBUG);
  858. $resql=$this->db->query($sql);
  859. if ($resql)
  860. {
  861. while ($obj = $this->db->fetch_object($resql))
  862. {
  863. $result[$i]=$obj->fk_socpeople;
  864. $i++;
  865. }
  866. }
  867. else
  868. {
  869. $this->error=$this->db->error();
  870. return null;
  871. }
  872. return $result;
  873. }
  874. /**
  875. * Load object contact with id=$this->contactid into $this->contact
  876. *
  877. * @param int $contactid Id du contact. Use this->contactid if empty.
  878. * @return int <0 if KO, >0 if OK
  879. */
  880. function fetch_contact($contactid=null)
  881. {
  882. if (empty($contactid)) $contactid=$this->contactid;
  883. if (empty($contactid)) return 0;
  884. require_once DOL_DOCUMENT_ROOT.'/contact/class/contact.class.php';
  885. $contact = new Contact($this->db);
  886. $result=$contact->fetch($contactid);
  887. $this->contact = $contact;
  888. return $result;
  889. }
  890. /**
  891. * Load the third party of object, from id $this->socid or $this->fk_soc, into this->thirdparty
  892. *
  893. * @param int $force_thirdparty_id Force thirdparty id
  894. * @return int <0 if KO, >0 if OK
  895. */
  896. function fetch_thirdparty($force_thirdparty_id=0)
  897. {
  898. global $conf;
  899. if (empty($this->socid) && empty($this->fk_soc) && empty($this->fk_thirdparty) && empty($force_thirdparty_id))
  900. return 0;
  901. require_once DOL_DOCUMENT_ROOT . '/societe/class/societe.class.php';
  902. $idtofetch = isset($this->socid) ? $this->socid : (isset($this->fk_soc) ? $this->fk_soc : $this->fk_thirdparty);
  903. if ($force_thirdparty_id)
  904. $idtofetch = $force_thirdparty_id;
  905. if ($idtofetch) {
  906. $thirdparty = new Societe($this->db);
  907. $result = $thirdparty->fetch($idtofetch);
  908. $this->thirdparty = $thirdparty;
  909. // Use first price level if level not defined for third party
  910. if (!empty($conf->global->PRODUIT_MULTIPRICES) && empty($this->thirdparty->price_level)) {
  911. $this->thirdparty->price_level = 1;
  912. }
  913. return $result;
  914. } else
  915. return -1;
  916. }
  917. /**
  918. * Looks for an object with ref matching the wildcard provided
  919. * It does only work when $this->table_ref_field is set
  920. *
  921. * @param string $ref Wildcard
  922. * @return int >1 = OK, 0 = Not found or table_ref_field not defined, <0 = KO
  923. */
  924. public function fetchOneLike($ref)
  925. {
  926. if (!$this->table_ref_field) {
  927. return 0;
  928. }
  929. $sql = 'SELECT rowid FROM '.MAIN_DB_PREFIX.$this->table_element.' WHERE '.$this->table_ref_field.' LIKE "'.$this->db->escape($ref).'" LIMIT 1';
  930. $query = $this->db->query($sql);
  931. if (!$this->db->num_rows($query)) {
  932. return 0;
  933. }
  934. $result = $this->db->fetch_object($query);
  935. return $this->fetch($result->rowid);
  936. }
  937. /**
  938. * Load data for barcode into properties ->barcode_type*
  939. * Properties ->barcode_type that is id of barcode. Type is used to find other properties, but
  940. * if it is not defined, ->element must be defined to know default barcode type.
  941. *
  942. * @return int <0 if KO, 0 if can't guess type of barcode (ISBN, EAN13...), >0 if OK (all barcode properties loaded)
  943. */
  944. function fetch_barcode()
  945. {
  946. global $conf;
  947. dol_syslog(get_class($this).'::fetch_barcode this->element='.$this->element.' this->barcode_type='.$this->barcode_type);
  948. $idtype=$this->barcode_type;
  949. if (empty($idtype) && $idtype != '0') // If type of barcode no set, we try to guess. If set to '0' it means we forced to have type remain not defined
  950. {
  951. if ($this->element == 'product') $idtype = $conf->global->PRODUIT_DEFAULT_BARCODE_TYPE;
  952. else if ($this->element == 'societe') $idtype = $conf->global->GENBARCODE_BARCODETYPE_THIRDPARTY;
  953. else dol_syslog('Call fetch_barcode with barcode_type not defined and cant be guessed', LOG_WARNING);
  954. }
  955. if ($idtype > 0)
  956. {
  957. if (empty($this->barcode_type) || empty($this->barcode_type_code) || empty($this->barcode_type_label) || empty($this->barcode_type_coder)) // If data not already loaded
  958. {
  959. $sql = "SELECT rowid, code, libelle as label, coder";
  960. $sql.= " FROM ".MAIN_DB_PREFIX."c_barcode_type";
  961. $sql.= " WHERE rowid = ".$idtype;
  962. dol_syslog(get_class($this).'::fetch_barcode', LOG_DEBUG);
  963. $resql = $this->db->query($sql);
  964. if ($resql)
  965. {
  966. $obj = $this->db->fetch_object($resql);
  967. $this->barcode_type = $obj->rowid;
  968. $this->barcode_type_code = $obj->code;
  969. $this->barcode_type_label = $obj->label;
  970. $this->barcode_type_coder = $obj->coder;
  971. return 1;
  972. }
  973. else
  974. {
  975. dol_print_error($this->db);
  976. return -1;
  977. }
  978. }
  979. }
  980. return 0;
  981. }
  982. /**
  983. * Charge le projet d'id $this->fk_project dans this->projet
  984. *
  985. * @return int <0 if KO, >=0 if OK
  986. */
  987. function fetch_projet()
  988. {
  989. include_once DOL_DOCUMENT_ROOT.'/projet/class/project.class.php';
  990. if (empty($this->fk_project) && ! empty($this->fk_projet)) $this->fk_project = $this->fk_projet; // For backward compatibility
  991. if (empty($this->fk_project)) return 0;
  992. $project = new Project($this->db);
  993. $result = $project->fetch($this->fk_project);
  994. $this->projet = $project; // deprecated
  995. $this->project = $project;
  996. return $result;
  997. }
  998. /**
  999. * Charge le user d'id userid dans this->user
  1000. *
  1001. * @param int $userid Id du contact
  1002. * @return int <0 if KO, >0 if OK
  1003. */
  1004. function fetch_user($userid)
  1005. {
  1006. $user = new User($this->db);
  1007. $result=$user->fetch($userid);
  1008. $this->user = $user;
  1009. return $result;
  1010. }
  1011. /**
  1012. * Read linked origin object
  1013. *
  1014. * @return void
  1015. */
  1016. function fetch_origin()
  1017. {
  1018. // TODO uniformise code
  1019. if ($this->origin == 'shipping') $this->origin = 'expedition';
  1020. if ($this->origin == 'delivery') $this->origin = 'livraison';
  1021. $origin = $this->origin;
  1022. $classname = ucfirst($origin);
  1023. $this->$origin = new $classname($this->db);
  1024. $this->$origin->fetch($this->origin_id);
  1025. }
  1026. /**
  1027. * Load object from specific field
  1028. *
  1029. * @param string $table Table element or element line
  1030. * @param string $field Field selected
  1031. * @param string $key Import key
  1032. * @return int <0 if KO, >0 if OK
  1033. */
  1034. function fetchObjectFrom($table,$field,$key)
  1035. {
  1036. global $conf;
  1037. $result=false;
  1038. $sql = "SELECT rowid FROM ".MAIN_DB_PREFIX.$table;
  1039. $sql.= " WHERE ".$field." = '".$key."'";
  1040. $sql.= " AND entity = ".$conf->entity;
  1041. dol_syslog(get_class($this).'::fetchObjectFrom', LOG_DEBUG);
  1042. $resql = $this->db->query($sql);
  1043. if ($resql)
  1044. {
  1045. $row = $this->db->fetch_row($resql);
  1046. $result = $this->fetch($row[0]);
  1047. }
  1048. return $result;
  1049. }
  1050. /**
  1051. * Getter generic. Load value from a specific field
  1052. *
  1053. * @param string $table Table of element or element line
  1054. * @param int $id Element id
  1055. * @param string $field Field selected
  1056. * @return int <0 if KO, >0 if OK
  1057. */
  1058. function getValueFrom($table, $id, $field)
  1059. {
  1060. $result=false;
  1061. if (!empty($id) && !empty($field) && !empty($table)) {
  1062. $sql = "SELECT ".$field." FROM ".MAIN_DB_PREFIX.$table;
  1063. $sql.= " WHERE rowid = ".$id;
  1064. dol_syslog(get_class($this).'::getValueFrom', LOG_DEBUG);
  1065. $resql = $this->db->query($sql);
  1066. if ($resql)
  1067. {
  1068. $row = $this->db->fetch_row($resql);
  1069. $result = $row[0];
  1070. }
  1071. }
  1072. return $result;
  1073. }
  1074. /**
  1075. * Setter generic. Update a specific field into database
  1076. *
  1077. * @param string $field Field to update
  1078. * @param mixed $value New value
  1079. * @param string $table To force other table element or element line (should not be used)
  1080. * @param int $id To force other object id (should not be used)
  1081. * @param string $format Data format ('text', 'date'). 'text' is used if not defined
  1082. * @param string $id_field To force rowid field name. 'rowid' is used it not defined
  1083. * @param User|string $user Update last update fields also if user object provided
  1084. * @return int <0 if KO, >0 if OK
  1085. */
  1086. function setValueFrom($field, $value, $table='', $id=null, $format='', $id_field='', $user='')
  1087. {
  1088. if (empty($table)) $table=$this->table_element;
  1089. if (empty($id)) $id=$this->id;
  1090. if (empty($format)) $format='text';
  1091. if (empty($id_field)) $id_field='rowid';
  1092. $this->db->begin();
  1093. $sql = "UPDATE ".MAIN_DB_PREFIX.$table." SET ";
  1094. if ($format == 'text') $sql.= $field." = '".$this->db->escape($value)."'";
  1095. else if ($format == 'date') $sql.= $field." = '".$this->db->idate($value)."'";
  1096. if (is_object($user)) $sql.=", fk_user_modif = ".$user->id;
  1097. $sql.= " WHERE ".$id_field." = ".$id;
  1098. dol_syslog(get_class($this)."::".__FUNCTION__."", LOG_DEBUG);
  1099. $resql = $this->db->query($sql);
  1100. if ($resql)
  1101. {
  1102. $this->db->commit();
  1103. return 1;
  1104. }
  1105. else
  1106. {
  1107. $this->error=$this->db->lasterror();
  1108. $this->db->rollback();
  1109. return -1;
  1110. }
  1111. }
  1112. /**
  1113. * Load properties id_previous and id_next
  1114. *
  1115. * @param string $filter Optional filter
  1116. * @param int $fieldid Name of field to use for the select MAX and MIN
  1117. * @param int $nodbprefix Do not include DB prefix to forge table name
  1118. * @return int <0 if KO, >0 if OK
  1119. */
  1120. function load_previous_next_ref($filter,$fieldid,$nodbprefix=0)
  1121. {
  1122. global $user;
  1123. if (! $this->table_element)
  1124. {
  1125. dol_print_error('',get_class($this)."::load_previous_next_ref was called on objet with property table_element not defined");
  1126. return -1;
  1127. }
  1128. // this->ismultientitymanaged contains
  1129. // 0=No test on entity, 1=Test with field entity, 2=Test with link by societe
  1130. $alias = 's';
  1131. if ($this->element == 'societe') $alias = 'te';
  1132. $sql = "SELECT MAX(te.".$fieldid.")";
  1133. $sql.= " FROM ".(empty($nodbprefix)?MAIN_DB_PREFIX:'').$this->table_element." as te";
  1134. if (isset($this->ismultientitymanaged) && $this->ismultientitymanaged == 2 || ($this->element != 'societe' && empty($this->isnolinkedbythird) && empty($user->rights->societe->client->voir))) $sql.= ", ".MAIN_DB_PREFIX."societe as s"; // If we need to link to societe to limit select to entity
  1135. if (empty($this->isnolinkedbythird) && !$user->rights->societe->client->voir) $sql.= " LEFT JOIN ".MAIN_DB_PREFIX."societe_commerciaux as sc ON ".$alias.".rowid = sc.fk_soc";
  1136. $sql.= " WHERE te.".$fieldid." < '".$this->db->escape($this->ref)."'";
  1137. if (empty($this->isnolinkedbythird) && !$user->rights->societe->client->voir) $sql.= " AND sc.fk_user = " .$user->id;
  1138. if (! empty($filter)) $sql.=" AND ".$filter;
  1139. if (isset($this->ismultientitymanaged) && $this->ismultientitymanaged == 2 || ($this->element != 'societe' && empty($this->isnolinkedbythird) && !$user->rights->societe->client->voir)) $sql.= ' AND te.fk_soc = s.rowid'; // If we need to link to societe to limit select to entity
  1140. if (isset($this->ismultientitymanaged) && $this->ismultientitymanaged == 1) $sql.= ' AND te.entity IN ('.getEntity($this->element, 1).')';
  1141. //print $sql."<br>";
  1142. $result = $this->db->query($sql);
  1143. if (! $result)
  1144. {
  1145. $this->error=$this->db->lasterror();
  1146. return -1;
  1147. }
  1148. $row = $this->db->fetch_row($result);
  1149. $this->ref_previous = $row[0];
  1150. $sql = "SELECT MIN(te.".$fieldid.")";
  1151. $sql.= " FROM ".(empty($nodbprefix)?MAIN_DB_PREFIX:'').$this->table_element." as te";
  1152. if (isset($this->ismultientitymanaged) && $this->ismultientitymanaged == 2 || ($this->element != 'societe' && empty($this->isnolinkedbythird) && !$user->rights->societe->client->voir)) $sql.= ", ".MAIN_DB_PREFIX."societe as s"; // If we need to link to societe to limit select to entity
  1153. if (empty($this->isnolinkedbythird) && !$user->rights->societe->client->voir) $sql.= " LEFT JOIN ".MAIN_DB_PREFIX."societe_commerciaux as sc ON ".$alias.".rowid = sc.fk_soc";
  1154. $sql.= " WHERE te.".$fieldid." > '".$this->db->escape($this->ref)."'";
  1155. if (empty($this->isnolinkedbythird) && !$user->rights->societe->client->voir) $sql.= " AND sc.fk_user = " .$user->id;
  1156. if (! empty($filter)) $sql.=" AND ".$filter;
  1157. if (isset($this->ismultientitymanaged) && $this->ismultientitymanaged == 2 || ($this->element != 'societe' && empty($this->isnolinkedbythird) && !$user->rights->societe->client->voir)) $sql.= ' AND te.fk_soc = s.rowid'; // If we need to link to societe to limit select to entity
  1158. if (isset($this->ismultientitymanaged) && $this->ismultientitymanaged == 1) $sql.= ' AND te.entity IN ('.getEntity($this->element, 1).')';
  1159. // Rem: Bug in some mysql version: SELECT MIN(rowid) FROM llx_socpeople WHERE rowid > 1 when one row in database with rowid=1, returns 1 instead of null
  1160. //print $sql."<br>";
  1161. $result = $this->db->query($sql);
  1162. if (! $result)
  1163. {
  1164. $this->error=$this->db->lasterror();
  1165. return -2;
  1166. }
  1167. $row = $this->db->fetch_row($result);
  1168. $this->ref_next = $row[0];
  1169. return 1;
  1170. }
  1171. /**
  1172. * Return list of id of contacts of project
  1173. *
  1174. * @param string $source Source of contact: external (llx_socpeople) or internal (llx_user) or thirdparty (llx_societe)
  1175. * @return array Array of id of contacts (if source=external or internal)
  1176. * Array of id of third parties with at least one contact on project (if source=thirdparty)
  1177. */
  1178. function getListContactId($source='external')
  1179. {
  1180. $contactAlreadySelected = array();
  1181. $tab = $this->liste_contact(-1,$source);
  1182. $num=count($tab);
  1183. $i = 0;
  1184. while ($i < $num)
  1185. {
  1186. if ($source == 'thirdparty') $contactAlreadySelected[$i] = $tab[$i]['socid'];
  1187. else $contactAlreadySelected[$i] = $tab[$i]['id'];
  1188. $i++;
  1189. }
  1190. return $contactAlreadySelected;
  1191. }
  1192. /**
  1193. * Link element with a project
  1194. *
  1195. * @param int $projectid Project id to link element to
  1196. * @return int <0 if KO, >0 if OK
  1197. */
  1198. function setProject($projectid)
  1199. {
  1200. if (! $this->table_element)
  1201. {
  1202. dol_syslog(get_class($this)."::setProject was called on objet with property table_element not defined",LOG_ERR);
  1203. return -1;
  1204. }
  1205. $sql = 'UPDATE '.MAIN_DB_PREFIX.$this->table_element;
  1206. if ($projectid) $sql.= ' SET fk_projet = '.$projectid;
  1207. else $sql.= ' SET fk_projet = NULL';
  1208. $sql.= ' WHERE rowid = '.$this->id;
  1209. dol_syslog(get_class($this)."::setProject", LOG_DEBUG);
  1210. if ($this->db->query($sql))
  1211. {
  1212. $this->fk_project = $projectid;
  1213. return 1;
  1214. }
  1215. else
  1216. {
  1217. dol_print_error($this->db);
  1218. return -1;
  1219. }
  1220. }
  1221. /**
  1222. * Change the payments methods
  1223. *
  1224. * @param int $id Id of new payment method
  1225. * @return int >0 if OK, <0 if KO
  1226. */
  1227. function setPaymentMethods($id)
  1228. {
  1229. dol_syslog(get_class($this).'::setPaymentMethods('.$id.')');
  1230. if ($this->statut >= 0 || $this->element == 'societe')
  1231. {
  1232. // TODO uniformize field name
  1233. $fieldname = 'fk_mode_reglement';
  1234. if ($this->element == 'societe') $fieldname = 'mode_reglement';
  1235. if (get_class($this) == 'Fournisseur') $fieldname = 'mode_reglement_supplier';
  1236. $sql = 'UPDATE '.MAIN_DB_PREFIX.$this->table_element;
  1237. $sql .= ' SET '.$fieldname.' = '.$id;
  1238. $sql .= ' WHERE rowid='.$this->id;
  1239. if ($this->db->query($sql))
  1240. {
  1241. $this->mode_reglement_id = $id;
  1242. // for supplier
  1243. if (get_class($this) == 'Fournisseur') $this->mode_reglement_supplier_id = $id;
  1244. return 1;
  1245. }
  1246. else
  1247. {
  1248. dol_syslog(get_class($this).'::setPaymentMethods Erreur '.$sql.' - '.$this->db->error());
  1249. $this->error=$this->db->error();
  1250. return -1;
  1251. }
  1252. }
  1253. else
  1254. {
  1255. dol_syslog(get_class($this).'::setPaymentMethods, status of the object is incompatible');
  1256. $this->error='Status of the object is incompatible '.$this->statut;
  1257. return -2;
  1258. }
  1259. }
  1260. /**
  1261. * Change the multicurrency code
  1262. *
  1263. * @param string $code multicurrency code
  1264. * @return int >0 if OK, <0 if KO
  1265. */
  1266. function setMulticurrencyCode($code)
  1267. {
  1268. dol_syslog(get_class($this).'::setMulticurrencyCode('.$id.')');
  1269. if ($this->statut >= 0 || $this->element == 'societe')
  1270. {
  1271. $fieldname = 'multicurrency_code';
  1272. $sql = 'UPDATE '.MAIN_DB_PREFIX.$this->table_element;
  1273. $sql .= ' SET '.$fieldname.' = "'.$this->db->escape($code).'"';
  1274. $sql .= ' WHERE rowid='.$this->id;
  1275. if ($this->db->query($sql))
  1276. {
  1277. $this->multicurrency_code = $code;
  1278. list($fk_multicurrency, $rate) = MultiCurrency::getIdAndTxFromCode($this->db, $code);
  1279. if ($rate) $this->setMulticurrencyRate($rate);
  1280. return 1;
  1281. }
  1282. else
  1283. {
  1284. dol_syslog(get_class($this).'::setMulticurrencyCode Erreur '.$sql.' - '.$this->db->error());
  1285. $this->error=$this->db->error();
  1286. return -1;
  1287. }
  1288. }
  1289. else
  1290. {
  1291. dol_syslog(get_class($this).'::setMulticurrencyCode, status of the object is incompatible');
  1292. $this->error='Status of the object is incompatible '.$this->statut;
  1293. return -2;
  1294. }
  1295. }
  1296. /**
  1297. * Change the multicurrency rate
  1298. *
  1299. * @param double $rate multicurrency rate
  1300. * @return int >0 if OK, <0 if KO
  1301. */
  1302. function setMulticurrencyRate($rate)
  1303. {
  1304. dol_syslog(get_class($this).'::setMulticurrencyRate('.$id.')');
  1305. if ($this->statut >= 0 || $this->element == 'societe')
  1306. {
  1307. $fieldname = 'multicurrency_tx';
  1308. $sql = 'UPDATE '.MAIN_DB_PREFIX.$this->table_element;
  1309. $sql .= ' SET '.$fieldname.' = '.$rate;
  1310. $sql .= ' WHERE rowid='.$this->id;
  1311. if ($this->db->query($sql))
  1312. {
  1313. $this->multicurrency_tx = $rate;
  1314. // Update line price
  1315. if (!empty($this->lines))
  1316. {
  1317. foreach ($this->lines as &$line)
  1318. {
  1319. switch ($this->element) {
  1320. case 'propal':
  1321. $this->updateline($line->id, $line->subprice, $line->qty, $line->remise_percent, $line->tva_tx, $line->localtax1_tx, $line->localtax2_tx, $line->desc, 'HT', $line->info_bits, $line->special_code, $line->fk_parent_line, $line->skip_update_total, $line->fk_fournprice, $line->pa_ht, $line->label, $line->product_type, $line->date_start, $line->date_end, $line->array_options, $line->fk_unit);
  1322. break;
  1323. case 'commande':
  1324. $this->updateline($line->id, $line->desc, $line->subprice, $line->qty, $line->remise_percent, $line->tva_tx, $line->localtax1_tx, $line->localtax2_tx, 'HT', $line->info_bits, $line->date_start, $line->date_end, $line->product_type, $line->fk_parent_line, $line->skip_update_total, $line->fk_fournprice, $line->pa_ht, $line->label, $line->special_code, $line->array_options, $line->fk_unit);
  1325. break;
  1326. case 'facture':
  1327. $this->updateline($line->id, $line->desc, $line->subprice, $line->qty, $line->remise_percent, $line->date_start, $line->date_end, $line->tva_tx, $line->localtax1_tx, $line->localtax2_tx, 'HT', $line->info_bits, $line->product_type, $line->fk_parent_line, $line->skip_update_total, $line->fk_fournprice, $line->pa_ht, $line->label, $line->special_code, $line->array_options, $line->situation_percent, $line->fk_unit);
  1328. break;
  1329. case 'supplier_proposal':
  1330. $this->updateline($line->id, $line->subprice, $line->qty, $line->remise_percent, $line->tva_tx, $line->localtax1_tx, $line->localtax2_tx, $line->desc, 'HT', $line->info_bits, $line->special_code, $line->fk_parent_line, $line->skip_update_total, $line->fk_fournprice, $line->pa_ht, $line->label, $line->product_type, $line->array_options, $line->ref_fourn);
  1331. break;
  1332. case 'order_supplier':
  1333. $this->updateline($line->id, $line->desc, $line->subprice, $line->qty, $line->remise_percent, $line->tva_tx, $line->localtax1_tx, $line->localtax2_tx, 'HT', $line->info_bits, $line->product_type, false, $line->date_start, $line->date_end, $line->array_options, $line->fk_unit);
  1334. break;
  1335. default:
  1336. dol_syslog(get_class($this).'::setMulticurrencyRate no updateline defined', LOG_DEBUG);
  1337. break;
  1338. }
  1339. }
  1340. }
  1341. return 1;
  1342. }
  1343. else
  1344. {
  1345. dol_syslog(get_class($this).'::setMulticurrencyRate Erreur '.$sql.' - '.$this->db->error());
  1346. $this->error=$this->db->error();
  1347. return -1;
  1348. }
  1349. }
  1350. else
  1351. {
  1352. dol_syslog(get_class($this).'::setMulticurrencyRate, status of the object is incompatible');
  1353. $this->error='Status of the object is incompatible '.$this->statut;
  1354. return -2;
  1355. }
  1356. }
  1357. /**
  1358. * Change the payments terms
  1359. *
  1360. * @param int $id Id of new payment terms
  1361. * @return int >0 if OK, <0 if KO
  1362. */
  1363. function setPaymentTerms($id)
  1364. {
  1365. dol_syslog(get_class($this).'::setPaymentTerms('.$id.')');
  1366. if ($this->statut >= 0 || $this->element == 'societe')
  1367. {
  1368. // TODO uniformize field name
  1369. $fieldname = 'fk_cond_reglement';
  1370. if ($this->element == 'societe') $fieldname = 'cond_reglement';
  1371. if (get_class($this) == 'Fournisseur') $fieldname = 'cond_reglement_supplier';
  1372. $sql = 'UPDATE '.MAIN_DB_PREFIX.$this->table_element;
  1373. $sql .= ' SET '.$fieldname.' = '.$id;
  1374. $sql .= ' WHERE rowid='.$this->id;
  1375. if ($this->db->query($sql))
  1376. {
  1377. $this->cond_reglement_id = $id;
  1378. // for supplier
  1379. if (get_class($this) == 'Fournisseur') $this->cond_reglement_supplier_id = $id;
  1380. $this->cond_reglement = $id; // for compatibility
  1381. return 1;
  1382. }
  1383. else
  1384. {
  1385. dol_syslog(get_class($this).'::setPaymentTerms Erreur '.$sql.' - '.$this->db->error());
  1386. $this->error=$this->db->error();
  1387. return -1;
  1388. }
  1389. }
  1390. else
  1391. {
  1392. dol_syslog(get_class($this).'::setPaymentTerms, status of the object is incompatible');
  1393. $this->error='Status of the object is incompatible '.$this->statut;
  1394. return -2;
  1395. }
  1396. }
  1397. /**
  1398. * Define delivery address
  1399. *
  1400. * @param int $id Address id
  1401. * @return int <0 si ko, >0 si ok
  1402. */
  1403. function setDeliveryAddress($id)
  1404. {
  1405. $fieldname = 'fk_delivery_address';
  1406. if ($this->element == 'delivery' || $this->element == 'shipping') $fieldname = 'fk_address';
  1407. $sql = "UPDATE ".MAIN_DB_PREFIX.$this->table_element." SET ".$fieldname." = ".$id;
  1408. $sql.= " WHERE rowid = ".$this->id." AND fk_statut = 0";
  1409. if ($this->db->query($sql))
  1410. {
  1411. $this->fk_delivery_address = $id;
  1412. return 1;
  1413. }
  1414. else
  1415. {
  1416. $this->error=$this->db->error();
  1417. dol_syslog(get_class($this).'::setDeliveryAddress Erreur '.$sql.' - '.$this->error);
  1418. return -1;
  1419. }
  1420. }
  1421. /**
  1422. * Change the shipping method
  1423. *
  1424. * @param int $shipping_method_id Id of shipping method
  1425. * @return int 1 if OK, 0 if KO
  1426. */
  1427. function setShippingMethod($shipping_method_id)
  1428. {
  1429. if (! $this->table_element) {
  1430. dol_syslog(get_class($this)."::setShippingMethod was called on objet with property table_element not defined",LOG_ERR);
  1431. return -1;
  1432. }
  1433. if ($shipping_method_id<0) $shipping_method_id='NULL';
  1434. dol_syslog(get_class($this).'::setShippingMethod('.$shipping_method_id.')');
  1435. $sql = "UPDATE ".MAIN_DB_PREFIX.$this->table_element;
  1436. $sql.= " SET fk_shipping_method = ".$shipping_method_id;
  1437. $sql.= " WHERE rowid=".$this->id;
  1438. if ($this->db->query($sql)) {
  1439. $this->shipping_method_id = ($shipping_method_id=='NULL')?null:$shipping_method_id;
  1440. return 1;
  1441. } else {
  1442. dol_syslog(get_class($this).'::setShippingMethod Error ', LOG_DEBUG);
  1443. $this->error=$this->db->error();
  1444. return 0;
  1445. }
  1446. }
  1447. /**
  1448. * Change the warehouse
  1449. *
  1450. * @param int $warehouse_id Id of warehouse
  1451. * @return int 1 if OK, 0 if KO
  1452. */
  1453. function setWarehouse($warehouse_id)
  1454. {
  1455. if (! $this->table_element) {
  1456. dol_syslog(get_class($this)."::setWarehouse was called on objet with property table_element not defined",LOG_ERR);
  1457. return -1;
  1458. }
  1459. if ($warehouse_id<0) $warehouse_id='NULL';
  1460. dol_syslog(get_class($this).'::setWarehouse('.$warehouse_id.')');
  1461. $sql = "UPDATE ".MAIN_DB_PREFIX.$this->table_element;
  1462. $sql.= " SET fk_warehouse = ".$warehouse_id;
  1463. $sql.= " WHERE rowid=".$this->id;
  1464. if ($this->db->query($sql)) {
  1465. $this->warehouse_id = ($warehouse_id=='NULL')?null:$warehouse_id;
  1466. return 1;
  1467. } else {
  1468. dol_syslog(get_class($this).'::setWarehouse Error ', LOG_DEBUG);
  1469. $this->error=$this->db->error();
  1470. return 0;
  1471. }
  1472. }
  1473. /**
  1474. * Set last model used by doc generator
  1475. *
  1476. * @param User $user User object that make change
  1477. * @param string $modelpdf Modele name
  1478. * @return int <0 if KO, >0 if OK
  1479. */
  1480. function setDocModel($user, $modelpdf)
  1481. {
  1482. if (! $this->table_element)
  1483. {
  1484. dol_syslog(get_class($this)."::setDocModel was called on objet with property table_element not defined",LOG_ERR);
  1485. return -1;
  1486. }
  1487. $newmodelpdf=dol_trunc($modelpdf,255);
  1488. $sql = "UPDATE ".MAIN_DB_PREFIX.$this->table_element;
  1489. $sql.= " SET model_pdf = '".$this->db->escape($newmodelpdf)."'";
  1490. $sql.= " WHERE rowid = ".$this->id;
  1491. // if ($this->element == 'facture') $sql.= " AND fk_statut < 2";
  1492. // if ($this->element == 'propal') $sql.= " AND fk_statut = 0";
  1493. dol_syslog(get_class($this)."::setDocModel", LOG_DEBUG);
  1494. $resql=$this->db->query($sql);
  1495. if ($resql)
  1496. {
  1497. $this->modelpdf=$modelpdf;
  1498. return 1;
  1499. }
  1500. else
  1501. {
  1502. dol_print_error($this->db);
  1503. return 0;
  1504. }
  1505. }
  1506. /**
  1507. * Change the bank account
  1508. *
  1509. * @param int $fk_account Id of bank account
  1510. * @return int 1 if OK, 0 if KO
  1511. */
  1512. function setBankAccount($fk_account)
  1513. {
  1514. if (! $this->table_element) {
  1515. dol_syslog(get_class($this)."::setBankAccount was called on objet with property table_element not defined",LOG_ERR);
  1516. return -1;
  1517. }
  1518. if ($fk_account<0) $fk_account='NULL';
  1519. dol_syslog(get_class($this).'::setBankAccount('.$fk_account.')');
  1520. $sql = "UPDATE ".MAIN_DB_PREFIX.$this->table_element;
  1521. $sql.= " SET fk_account = ".$fk_account;
  1522. $sql.= " WHERE rowid=".$this->id;
  1523. if ($this->db->query($sql)) {
  1524. $this->fk_account = ($fk_account=='NULL')?null:$fk_account;
  1525. return 1;
  1526. } else {
  1527. dol_syslog(get_class($this).'::setBankAccount Error '.$sql.' - '.$this->db->error());
  1528. $this->error=$this->db->error();
  1529. return 0;
  1530. }
  1531. }
  1532. // TODO: Move line related operations to CommonObjectLine?
  1533. /**
  1534. * Save a new position (field rang) for details lines.
  1535. * You can choose to set position for lines with already a position or lines without any position defined.
  1536. *
  1537. * @param boolean $renum True to renum all already ordered lines, false to renum only not already ordered lines.
  1538. * @param string $rowidorder ASC or DESC
  1539. * @param boolean $fk_parent_line Table with fk_parent_line field or not
  1540. * @return int <0 if KO, >0 if OK
  1541. */
  1542. function line_order($renum=false, $rowidorder='ASC', $fk_parent_line=true)
  1543. {
  1544. if (! $this->table_element_line)
  1545. {
  1546. dol_syslog(get_class($this)."::line_order was called on objet with property table_element_line not defined",LOG_ERR);
  1547. return -1;
  1548. }
  1549. if (! $this->fk_element)
  1550. {
  1551. dol_syslog(get_class($this)."::line_order was called on objet with property fk_element not defined",LOG_ERR);
  1552. return -1;
  1553. }
  1554. // Count number of lines to reorder (according to choice $renum)
  1555. $nl=0;
  1556. $sql = 'SELECT count(rowid) FROM '.MAIN_DB_PREFIX.$this->table_element_line;
  1557. $sql.= ' WHERE '.$this->fk_element.'='.$this->id;
  1558. if (! $renum) $sql.= ' AND rang = 0';
  1559. if ($renum) $sql.= ' AND rang <> 0';
  1560. dol_syslog(get_class($this)."::line_order", LOG_DEBUG);
  1561. $resql = $this->db->query($sql);
  1562. if ($resql)
  1563. {
  1564. $row = $this->db->fetch_row($resql);
  1565. $nl = $row[0];
  1566. }
  1567. else dol_print_error($this->db);
  1568. if ($nl > 0)
  1569. {
  1570. // The goal of this part is to reorder all lines, with all children lines sharing the same
  1571. // counter that parents.
  1572. $rows=array();
  1573. // We first search all lines that are parent lines (for multilevel details lines)
  1574. $sql = 'SELECT rowid FROM '.MAIN_DB_PREFIX.$this->table_element_line;
  1575. $sql.= ' WHERE '.$this->fk_element.' = '.$this->id;
  1576. if ($fk_parent_line) $sql.= ' AND fk_parent_line IS NULL';
  1577. $sql.= ' ORDER BY rang ASC, rowid '.$rowidorder;
  1578. dol_syslog(get_class($this)."::line_order search all parent lines", LOG_DEBUG);
  1579. $resql = $this->db->query($sql);
  1580. if ($resql)
  1581. {
  1582. $i=0;
  1583. $num = $this->db->num_rows($resql);
  1584. while ($i < $num)
  1585. {
  1586. $row = $this->db->fetch_row($resql);
  1587. $rows[] = $row[0]; // Add parent line into array rows
  1588. $childrens = $this->getChildrenOfLine($row[0]);
  1589. if (! empty($childrens))
  1590. {
  1591. foreach($childrens as $child)
  1592. {
  1593. array_push($rows, $child);
  1594. }
  1595. }
  1596. $i++;
  1597. }
  1598. // Now we set a new number for each lines (parent and children with children included into parent tree)
  1599. if (! empty($rows))
  1600. {
  1601. foreach($rows as $key => $row)
  1602. {
  1603. $this->updateRangOfLine($row, ($key+1));
  1604. }
  1605. }
  1606. }
  1607. else
  1608. {
  1609. dol_print_error($this->db);
  1610. }
  1611. }
  1612. return 1;
  1613. }
  1614. /**
  1615. * Get children of line
  1616. *
  1617. * @param int $id Id of parent line
  1618. * @return array Array with list of children lines id
  1619. */
  1620. function getChildrenOfLine($id)
  1621. {
  1622. $rows=array();
  1623. $sql = 'SELECT rowid FROM '.MAIN_DB_PREFIX.$this->table_element_line;
  1624. $sql.= ' WHERE '.$this->fk_element.' = '.$this->id;
  1625. $sql.= ' AND fk_parent_line = '.$id;
  1626. $sql.= ' ORDER BY rang ASC';
  1627. dol_syslog(get_class($this)."::getChildrenOfLine search children lines for line ".$id."", LOG_DEBUG);
  1628. $resql = $this->db->query($sql);
  1629. if ($resql)
  1630. {
  1631. $i=0;
  1632. $num = $this->db->num_rows($resql);
  1633. while ($i < $num)
  1634. {
  1635. $row = $this->db->fetch_row($resql);
  1636. $rows[$i] = $row[0];
  1637. $i++;
  1638. }
  1639. }
  1640. return $rows;
  1641. }
  1642. /**
  1643. * Update a line to have a lower rank
  1644. *
  1645. * @param int $rowid Id of line
  1646. * @param boolean $fk_parent_line Table with fk_parent_line field or not
  1647. * @return void
  1648. */
  1649. function line_up($rowid, $fk_parent_line=true)
  1650. {
  1651. $this->line_order(false, 'ASC', $fk_parent_line);
  1652. // Get rang of line
  1653. $rang = $this->getRangOfLine($rowid);
  1654. // Update position of line
  1655. $this->updateLineUp($rowid, $rang);
  1656. }
  1657. /**
  1658. * Update a line to have a higher rank
  1659. *
  1660. * @param int $rowid Id of line
  1661. * @param boolean $fk_parent_line Table with fk_parent_line field or not
  1662. * @return void
  1663. */
  1664. function line_down($rowid, $fk_parent_line=true)
  1665. {
  1666. $this->line_order(false, 'ASC', $fk_parent_line);
  1667. // Get rang of line
  1668. $rang = $this->getRangOfLine($rowid);
  1669. // Get max value for rang
  1670. $max = $this->line_max();
  1671. // Update position of line
  1672. $this->updateLineDown($rowid, $rang, $max);
  1673. }
  1674. /**
  1675. * Update position of line (rang)
  1676. *
  1677. * @param int $rowid Id of line
  1678. * @param int $rang Position
  1679. * @return void
  1680. */
  1681. function updateRangOfLine($rowid,$rang)
  1682. {
  1683. $sql = 'UPDATE '.MAIN_DB_PREFIX.$this->table_element_line.' SET rang = '.$rang;
  1684. $sql.= ' WHERE rowid = '.$rowid;
  1685. dol_syslog(get_class($this)."::updateRangOfLine", LOG_DEBUG);
  1686. if (! $this->db->query($sql))
  1687. {
  1688. dol_print_error($this->db);
  1689. }
  1690. }
  1691. /**
  1692. * Update position of line with ajax (rang)
  1693. *
  1694. * @param array $rows Array of rows
  1695. * @return void
  1696. */
  1697. function line_ajaxorder($rows)
  1698. {
  1699. $num = count($rows);
  1700. for ($i = 0 ; $i < $num ; $i++)
  1701. {
  1702. $this->updateRangOfLine($rows[$i], ($i+1));
  1703. }
  1704. }
  1705. /**
  1706. * Update position of line up (rang)
  1707. *
  1708. * @param int $rowid Id of line
  1709. * @param int $rang Position
  1710. * @return void
  1711. */
  1712. function updateLineUp($rowid,$rang)
  1713. {
  1714. if ($rang > 1 )
  1715. {
  1716. $sql = 'UPDATE '.MAIN_DB_PREFIX.$this->table_element_line.' SET rang = '.$rang ;
  1717. $sql.= ' WHERE '.$this->fk_element.' = '.$this->id;
  1718. $sql.= ' AND rang = '.($rang - 1);
  1719. if ($this->db->query($sql) )
  1720. {
  1721. $sql = 'UPDATE '.MAIN_DB_PREFIX.$this->table_element_line.' SET rang = '.($rang - 1);
  1722. $sql.= ' WHERE rowid = '.$rowid;
  1723. if (! $this->db->query($sql) )
  1724. {
  1725. dol_print_error($this->db);
  1726. }
  1727. }
  1728. else
  1729. {
  1730. dol_print_error($this->db);
  1731. }
  1732. }
  1733. }
  1734. /**
  1735. * Update position of line down (rang)
  1736. *
  1737. * @param int $rowid Id of line
  1738. * @param int $rang Position
  1739. * @param int $max Max
  1740. * @return void
  1741. */
  1742. function updateLineDown($rowid,$rang,$max)
  1743. {
  1744. if ($rang < $max)
  1745. {
  1746. $sql = 'UPDATE '.MAIN_DB_PREFIX.$this->table_element_line.' SET rang = '.$rang;
  1747. $sql.= ' WHERE '.$this->fk_element.' = '.$this->id;
  1748. $sql.= ' AND rang = '.($rang+1);
  1749. if ($this->db->query($sql) )
  1750. {
  1751. $sql = 'UPDATE '.MAIN_DB_PREFIX.$this->table_element_line.' SET rang = '.($rang+1);
  1752. $sql.= ' WHERE rowid = '.$rowid;
  1753. if (! $this->db->query($sql) )
  1754. {
  1755. dol_print_error($this->db);
  1756. }
  1757. }
  1758. else
  1759. {
  1760. dol_print_error($this->db);
  1761. }
  1762. }
  1763. }
  1764. /**
  1765. * Get position of line (rang)
  1766. *
  1767. * @param int $rowid Id of line
  1768. * @return int Value of rang in table of lines
  1769. */
  1770. function getRangOfLine($rowid)
  1771. {
  1772. $sql = 'SELECT rang FROM '.MAIN_DB_PREFIX.$this->table_element_line;
  1773. $sql.= ' WHERE rowid ='.$rowid;
  1774. dol_syslog(get_class($this)."::getRangOfLine", LOG_DEBUG);
  1775. $resql = $this->db->query($sql);
  1776. if ($resql)
  1777. {
  1778. $row = $this->db->fetch_row($resql);
  1779. return $row[0];
  1780. }
  1781. }
  1782. /**
  1783. * Get rowid of the line relative to its position
  1784. *
  1785. * @param int $rang Rang value
  1786. * @return int Rowid of the line
  1787. */
  1788. function getIdOfLine($rang)
  1789. {
  1790. $sql = 'SELECT rowid FROM '.MAIN_DB_PREFIX.$this->table_element_line;
  1791. $sql.= ' WHERE '.$this->fk_element.' = '.$this->id;
  1792. $sql.= ' AND rang = '.$rang;
  1793. $resql = $this->db->query($sql);
  1794. if ($resql)
  1795. {
  1796. $row = $this->db->fetch_row($resql);
  1797. return $row[0];
  1798. }
  1799. }
  1800. /**
  1801. * Get max value used for position of line (rang)
  1802. *
  1803. * @param int $fk_parent_line Parent line id
  1804. * @return int Max value of rang in table of lines
  1805. */
  1806. function line_max($fk_parent_line=0)
  1807. {
  1808. // Search the last rang with fk_parent_line
  1809. if ($fk_parent_line)
  1810. {
  1811. $sql = 'SELECT max(rang) FROM '.MAIN_DB_PREFIX.$this->table_element_line;
  1812. $sql.= ' WHERE '.$this->fk_element.' = '.$this->id;
  1813. $sql.= ' AND fk_parent_line = '.$fk_parent_line;
  1814. dol_syslog(get_class($this)."::line_max", LOG_DEBUG);
  1815. $resql = $this->db->query($sql);
  1816. if ($resql)
  1817. {
  1818. $row = $this->db->fetch_row($resql);
  1819. if (! empty($row[0]))
  1820. {
  1821. return $row[0];
  1822. }
  1823. else
  1824. {
  1825. return $this->getRangOfLine($fk_parent_line);
  1826. }
  1827. }
  1828. }
  1829. // If not, search the last rang of element
  1830. else
  1831. {
  1832. $sql = 'SELECT max(rang) FROM '.MAIN_DB_PREFIX.$this->table_element_line;
  1833. $sql.= ' WHERE '.$this->fk_element.' = '.$this->id;
  1834. dol_syslog(get_class($this)."::line_max", LOG_DEBUG);
  1835. $resql = $this->db->query($sql);
  1836. if ($resql)
  1837. {
  1838. $row = $this->db->fetch_row($resql);
  1839. return $row[0];
  1840. }
  1841. }
  1842. }
  1843. /**
  1844. * Update external ref of element
  1845. *
  1846. * @param string $ref_ext Update field ref_ext
  1847. * @return int <0 if KO, >0 if OK
  1848. */
  1849. function update_ref_ext($ref_ext)
  1850. {
  1851. if (! $this->table_element)
  1852. {
  1853. dol_syslog(get_class($this)."::update_ref_ext was called on objet with property table_element not defined", LOG_ERR);
  1854. return -1;
  1855. }
  1856. $sql = 'UPDATE '.MAIN_DB_PREFIX.$this->table_element;
  1857. $sql.= " SET ref_ext = '".$this->db->escape($ref_ext)."'";
  1858. $sql.= " WHERE ".(isset($this->table_rowid)?$this->table_rowid:'rowid')." = ". $this->id;
  1859. dol_syslog(get_class($this)."::update_ref_ext", LOG_DEBUG);
  1860. if ($this->db->query($sql))
  1861. {
  1862. $this->ref_ext = $ref_ext;
  1863. return 1;
  1864. }
  1865. else
  1866. {
  1867. $this->error=$this->db->error();
  1868. return -1;
  1869. }
  1870. }
  1871. /**
  1872. * Update note of element
  1873. *
  1874. * @param string $note New value for note
  1875. * @param string $suffix '', '_public' or '_private'
  1876. * @return int <0 if KO, >0 if OK
  1877. */
  1878. function update_note($note,$suffix='')
  1879. {
  1880. if (! $this->table_element)
  1881. {
  1882. dol_syslog(get_class($this)."::update_note was called on objet with property table_element not defined", LOG_ERR);
  1883. return -1;
  1884. }
  1885. if (! in_array($suffix,array('','_public','_private')))
  1886. {
  1887. dol_syslog(get_class($this)."::update_note Parameter suffix must be empty, '_private' or '_public'", LOG_ERR);
  1888. return -2;
  1889. }
  1890. $sql = 'UPDATE '.MAIN_DB_PREFIX.$this->table_element;
  1891. $sql.= " SET note".$suffix." = ".(!empty($note)?("'".$this->db->escape($note)."'"):"NULL");
  1892. $sql.= " WHERE rowid =". $this->id;
  1893. dol_syslog(get_class($this)."::update_note", LOG_DEBUG);
  1894. if ($this->db->query($sql))
  1895. {
  1896. if ($suffix == '_public') $this->note_public = $note;
  1897. else if ($suffix == '_private') $this->note_private = $note;
  1898. else $this->note = $note;
  1899. return 1;
  1900. }
  1901. else
  1902. {
  1903. $this->error=$this->db->lasterror();
  1904. return -1;
  1905. }
  1906. }
  1907. /**
  1908. * Update public note (kept for backward compatibility)
  1909. *
  1910. * @param string $note New value for note
  1911. * @return int <0 if KO, >0 if OK
  1912. * @deprecated
  1913. * @see update_note()
  1914. */
  1915. function update_note_public($note)
  1916. {
  1917. return $this->update_note($note,'_public');
  1918. }
  1919. /**
  1920. * Update total_ht, total_ttc, total_vat, total_localtax1, total_localtax2 for an object (sum of lines).
  1921. * Must be called at end of methods addline or updateline.
  1922. *
  1923. * @param int $exclspec >0 = Exclude special product (product_type=9)
  1924. * @param string $roundingadjust 'none'=Do nothing, 'auto'=Use default method (MAIN_ROUNDOFTOTAL_NOT_TOTALOFROUND if defined, or '0'), '0'=Force mode total of rounding, '1'=Force mode rounding of total
  1925. * @param int $nodatabaseupdate 1=Do not update database. Update only properties of object.
  1926. * @param Societe $seller If roundingadjust is '0' or '1' or maybe 'auto', it means we recalculate total for lines before calculating total for object and for this, we need seller object.
  1927. * @return int <0 if KO, >0 if OK
  1928. */
  1929. function update_price($exclspec=0,$roundingadjust='none',$nodatabaseupdate=0,$seller=null)
  1930. {
  1931. global $conf;
  1932. include_once DOL_DOCUMENT_ROOT.'/core/lib/price.lib.php';
  1933. if ($roundingadjust == '-1') $roundingadjust='auto'; // For backward compatibility
  1934. $forcedroundingmode=$roundingadjust;
  1935. if ($forcedroundingmode == 'auto' && isset($conf->global->MAIN_ROUNDOFTOTAL_NOT_TOTALOFROUND)) $forcedroundingmode=$conf->global->MAIN_ROUNDOFTOTAL_NOT_TOTALOFROUND;
  1936. elseif ($forcedroundingmode == 'auto') $forcedroundingmode='0';
  1937. $error=0;
  1938. // Define constants to find lines to sum
  1939. $fieldtva='total_tva';
  1940. $fieldlocaltax1='total_localtax1';
  1941. $fieldlocaltax2='total_localtax2';
  1942. $fieldup='subprice';
  1943. if ($this->element == 'facture_fourn' || $this->element == 'invoice_supplier')
  1944. {
  1945. $fieldtva='tva';
  1946. $fieldup='pu_ht';
  1947. }
  1948. if ($this->element == 'expensereport')
  1949. {
  1950. $fieldup='value_unit';
  1951. }
  1952. $sql = 'SELECT rowid, qty, '.$fieldup.' as up, remise_percent, total_ht, '.$fieldtva.' as total_tva, total_ttc, '.$fieldlocaltax1.' as total_localtax1, '.$fieldlocaltax2.' as total_localtax2,';
  1953. $sql.= ' tva_tx as vatrate, localtax1_tx, localtax2_tx, localtax1_type, localtax2_type, info_bits, product_type';
  1954. if ($this->table_element_line == 'facturedet') $sql.= ', situation_percent';
  1955. $sql.= ', multicurrency_total_ht, multicurrency_total_tva, multicurrency_total_ttc';
  1956. $sql.= ' FROM '.MAIN_DB_PREFIX.$this->table_element_line;
  1957. $sql.= ' WHERE '.$this->fk_element.' = '.$this->id;
  1958. if ($exclspec)
  1959. {
  1960. $product_field='product_type';
  1961. if ($this->table_element_line == 'contratdet') $product_field=''; // contratdet table has no product_type field
  1962. if ($product_field) $sql.= ' AND '.$product_field.' <> 9';
  1963. }
  1964. $sql.= ' ORDER by rowid'; // We want to be sure to always use same order of line to not change lines differently when option MAIN_ROUNDOFTOTAL_NOT_TOTALOFROUND is used
  1965. dol_syslog(get_class($this)."::update_price", LOG_DEBUG);
  1966. $resql = $this->db->query($sql);
  1967. if ($resql)
  1968. {
  1969. $this->total_ht = 0;
  1970. $this->total_tva = 0;
  1971. $this->total_localtax1 = 0;
  1972. $this->total_localtax2 = 0;
  1973. $this->total_ttc = 0;
  1974. $total_ht_by_vats = array();
  1975. $total_tva_by_vats = array();
  1976. $total_ttc_by_vats = array();
  1977. $this->multicurrency_total_ht = 0;
  1978. $this->multicurrency_total_tva = 0;
  1979. $this->multicurrency_total_ttc = 0;
  1980. $num = $this->db->num_rows($resql);
  1981. $i = 0;
  1982. while ($i < $num)
  1983. {
  1984. $obj = $this->db->fetch_object($resql);
  1985. // Note: There is no check on detail line and no check on total, if $forcedroundingmode = 'none'
  1986. $multicurrency_tx = !empty($this->multicurrency_tx) ? $this->multicurrency_tx : 1;
  1987. if ($forcedroundingmode == '0') // Check if data on line are consistent. This may solve lines that were not consistent because set with $forcedroundingmode='auto'
  1988. {
  1989. $localtax_array=array($obj->localtax1_type,$obj->localtax1_tx,$obj->localtax2_type,$obj->localtax2_tx);
  1990. $tmpcal=calcul_price_total($obj->qty, $obj->up, $obj->remise_percent, $obj->vatrate, $obj->localtax1_tx, $obj->localtax2_tx, 0, 'HT', $obj->info_bits, $obj->product_type, $seller, $localtax_array, (isset($obj->situation_percent) ? $obj->situation_percent : 100), $multicurrency_tx);
  1991. $diff=price2num($tmpcal[1] - $obj->total_tva, 'MT', 1);
  1992. if ($diff)
  1993. {
  1994. $sqlfix="UPDATE ".MAIN_DB_PREFIX.$this->table_element_line." SET ".$fieldtva." = ".$tmpcal[1].", total_ttc = ".$tmpcal[2]." WHERE rowid = ".$obj->rowid;
  1995. dol_syslog('We found unconsistent data into detailed line (difference of '.$diff.') for line rowid = '.$obj->rowid." (total vat of line calculated=".$tmpcal[1].", database=".$obj->total_tva."). We fix the total_vat and total_ttc of line by running sqlfix = ".$sqlfix);
  1996. $resqlfix=$this->db->query($sqlfix);
  1997. if (! $resqlfix) dol_print_error($this->db,'Failed to update line');
  1998. $obj->total_tva = $tmpcal[1];
  1999. $obj->total_ttc = $tmpcal[2];
  2000. //
  2001. }
  2002. }
  2003. $this->total_ht += $obj->total_ht; // The only field visible at end of line detail
  2004. $this->total_tva += $obj->total_tva;
  2005. $this->total_localtax1 += $obj->total_localtax1;
  2006. $this->total_localtax2 += $obj->total_localtax2;
  2007. $this->total_ttc += $obj->total_ttc;
  2008. if (! isset($total_ht_by_vats[$obj->vatrate])) $total_ht_by_vats[$obj->vatrate]=0;
  2009. if (! isset($total_tva_by_vats[$obj->vatrate])) $total_tva_by_vats[$obj->vatrate]=0;
  2010. if (! isset($total_ttc_by_vats[$obj->vatrate])) $total_ttc_by_vats[$obj->vatrate]=0;
  2011. $total_ht_by_vats[$obj->vatrate] += $obj->total_ht;
  2012. $total_tva_by_vats[$obj->vatrate] += $obj->total_tva;
  2013. $total_ttc_by_vats[$obj->vatrate] += $obj->total_ttc;
  2014. if ($forcedroundingmode == '1') // Check if we need adjustement onto line for vat
  2015. {
  2016. $tmpvat=price2num($total_ht_by_vats[$obj->vatrate] * $obj->vatrate / 100, 'MT', 1);
  2017. $diff=price2num($total_tva_by_vats[$obj->vatrate]-$tmpvat, 'MT', 1);
  2018. //print 'Line '.$i.' rowid='.$obj->rowid.' vat_rate='.$obj->vatrate.' total_ht='.$obj->total_ht.' total_tva='.$obj->total_tva.' total_ttc='.$obj->total_ttc.' total_ht_by_vats='.$total_ht_by_vats[$obj->vatrate].' total_tva_by_vats='.$total_tva_by_vats[$obj->vatrate].' (new calculation = '.$tmpvat.') total_ttc_by_vats='.$total_ttc_by_vats[$obj->vatrate].($diff?" => DIFF":"")."<br>\n";
  2019. if ($diff)
  2020. {
  2021. if (abs($diff) > 0.1) { dol_syslog('A rounding difference was detected into TOTAL but is too high to be corrected', LOG_WARNING); exit; }
  2022. $sqlfix="UPDATE ".MAIN_DB_PREFIX.$this->table_element_line." SET ".$fieldtva." = ".($obj->total_tva - $diff).", total_ttc = ".($obj->total_ttc - $diff)." WHERE rowid = ".$obj->rowid;
  2023. dol_syslog('We found a difference of '.$diff.' for line rowid = '.$obj->rowid.". We fix the total_vat and total_ttc of line by running sqlfix = ".$sqlfix);
  2024. $resqlfix=$this->db->query($sqlfix);
  2025. if (! $resqlfix) dol_print_error($this->db,'Failed to update line');
  2026. $this->total_tva -= $diff;
  2027. $this->total_ttc -= $diff;
  2028. $total_tva_by_vats[$obj->vatrate] -= $diff;
  2029. $total_ttc_by_vats[$obj->vatrate] -= $diff;
  2030. }
  2031. }
  2032. $i++;
  2033. }
  2034. // Add revenue stamp to total
  2035. $this->total_ttc += isset($this->revenuestamp)?$this->revenuestamp:0;
  2036. // Situations totals
  2037. if ($this->situation_cycle_ref && $this->situation_counter > 1) {
  2038. $prev_sits = $this->get_prev_sits();
  2039. foreach ($prev_sits as $sit) {
  2040. $this->total_ht -= $sit->total_ht;
  2041. $this->total_tva -= $sit->total_tva;
  2042. $this->total_localtax1 -= $sit->total_localtax1;
  2043. $this->total_localtax2 -= $sit->total_localtax2;
  2044. $this->total_ttc -= $sit->total_ttc;
  2045. }
  2046. }
  2047. // Multicurrency
  2048. $this->multicurrency_total_ht += $this->total_ht * $multicurrency_tx;
  2049. $this->multicurrency_total_tva += $this->total_tva * $multicurrency_tx;
  2050. $this->multicurrency_total_ttc += $this->total_ttc * $multicurrency_tx;
  2051. $this->db->free($resql);
  2052. // Now update global field total_ht, total_ttc and tva
  2053. $fieldht='total_ht';
  2054. $fieldtva='tva';
  2055. $fieldlocaltax1='localtax1';
  2056. $fieldlocaltax2='localtax2';
  2057. $fieldttc='total_ttc';
  2058. // Specific code for backward compatibility with old field names
  2059. if ($this->element == 'facture' || $this->element == 'facturerec') $fieldht='total';
  2060. if ($this->element == 'facture_fourn' || $this->element == 'invoice_supplier') $fieldtva='total_tva';
  2061. if ($this->element == 'propal') $fieldttc='total';
  2062. if ($this->element == 'expensereport') $fieldtva='total_tva';
  2063. if ($this->element == 'supplier_proposal') $fieldttc='total';
  2064. if (empty($nodatabaseupdate))
  2065. {
  2066. $sql = 'UPDATE '.MAIN_DB_PREFIX.$this->table_element.' SET';
  2067. $sql .= " ".$fieldht."='".price2num($this->total_ht)."',";
  2068. $sql .= " ".$fieldtva."='".price2num($this->total_tva)."',";
  2069. $sql .= " ".$fieldlocaltax1."='".price2num($this->total_localtax1)."',";
  2070. $sql .= " ".$fieldlocaltax2."='".price2num($this->total_localtax2)."',";
  2071. $sql .= " ".$fieldttc."='".price2num($this->total_ttc)."'";
  2072. $sql .= ", multicurrency_total_ht='".price2num($this->multicurrency_total_ht, 'MT', 1)."'";
  2073. $sql .= ", multicurrency_total_tva='".price2num($this->multicurrency_total_tva, 'MT', 1)."'";
  2074. $sql .= ", multicurrency_total_ttc='".price2num($this->multicurrency_total_ttc, 'MT', 1)."'";
  2075. $sql .= ' WHERE rowid = '.$this->id;
  2076. //print "xx".$sql;
  2077. dol_syslog(get_class($this)."::update_price", LOG_DEBUG);
  2078. $resql=$this->db->query($sql);
  2079. if (! $resql)
  2080. {
  2081. $error++;
  2082. $this->error=$this->db->lasterror();
  2083. $this->errors[]=$this->db->lasterror();
  2084. }
  2085. }
  2086. if (! $error)
  2087. {
  2088. return 1;
  2089. }
  2090. else
  2091. {
  2092. return -1;
  2093. }
  2094. }
  2095. else
  2096. {
  2097. dol_print_error($this->db,'Bad request in update_price');
  2098. return -1;
  2099. }
  2100. }
  2101. /**
  2102. * Add objects linked in llx_element_element.
  2103. *
  2104. * @param string $origin Linked element type
  2105. * @param int $origin_id Linked element id
  2106. * @return int <=0 if KO, >0 if OK
  2107. * @see fetchObjectLinked, updateObjectLinked, deleteObjectLinked
  2108. */
  2109. function add_object_linked($origin=null, $origin_id=null)
  2110. {
  2111. $origin = (! empty($origin) ? $origin : $this->origin);
  2112. $origin_id = (! empty($origin_id) ? $origin_id : $this->origin_id);
  2113. $this->db->begin();
  2114. $sql = "INSERT INTO ".MAIN_DB_PREFIX."element_element (";
  2115. $sql.= "fk_source";
  2116. $sql.= ", sourcetype";
  2117. $sql.= ", fk_target";
  2118. $sql.= ", targettype";
  2119. $sql.= ") VALUES (";
  2120. $sql.= $origin_id;
  2121. $sql.= ", '".$origin."'";
  2122. $sql.= ", ".$this->id;
  2123. $sql.= ", '".$this->element."'";
  2124. $sql.= ")";
  2125. dol_syslog(get_class($this)."::add_object_linked", LOG_DEBUG);
  2126. if ($this->db->query($sql))
  2127. {
  2128. $this->db->commit();
  2129. return 1;
  2130. }
  2131. else
  2132. {
  2133. $this->error=$this->db->lasterror();
  2134. $this->db->rollback();
  2135. return 0;
  2136. }
  2137. }
  2138. /**
  2139. * Fetch array of objects linked to current object. Links are loaded into this->linkedObjects array and this->linkedObjectsIds
  2140. * Possible usage for parameters:
  2141. * - all parameters empty -> we look all link to current object (current object can be source or target)
  2142. * - one couple id+type is provided -> this will set $justsource or $justtarget
  2143. * - one couple id+type is provided and other type is provided -> this will set $justsource or $justtarget + criteria on other type
  2144. *
  2145. *
  2146. * @param int $sourceid Object source id (if not defined, id of object)
  2147. * @param string $sourcetype Object source type (if not defined, element name of object)
  2148. * @param int $targetid Object target id (if not defined, id of object)
  2149. * @param string $targettype Object target type (if not defined, elemennt name of object)
  2150. * @param string $clause 'OR' or 'AND' clause used when both source id and target id are provided
  2151. * @param int $alsosametype 0=Return only links to object that differs from source. 1=Include also link to objects of same type.
  2152. * @return void
  2153. * @see add_object_linked, updateObjectLinked, deleteObjectLinked
  2154. */
  2155. function fetchObjectLinked($sourceid=null,$sourcetype='',$targetid=null,$targettype='',$clause='OR',$alsosametype=1)
  2156. {
  2157. global $conf;
  2158. $this->linkedObjectsIds=array();
  2159. $this->linkedObjects=array();
  2160. $justsource=false;
  2161. $justtarget=false;
  2162. $withtargettype=false;
  2163. $withsourcetype=false;
  2164. if (! empty($sourceid) && ! empty($sourcetype) && empty($targetid))
  2165. {
  2166. $justsource=true; // the source (id and type) is a search criteria
  2167. if (! empty($targettype)) $withtargettype=true;
  2168. }
  2169. if (! empty($targetid) && ! empty($targettype) && empty($sourceid))
  2170. {
  2171. $justtarget=true; // the target (id and type) is a search criteria
  2172. if (! empty($sourcetype)) $withsourcetype=true;
  2173. }
  2174. $sourceid = (! empty($sourceid) ? $sourceid : $this->id);
  2175. $targetid = (! empty($targetid) ? $targetid : $this->id);
  2176. $sourcetype = (! empty($sourcetype) ? $sourcetype : $this->element);
  2177. $targettype = (! empty($targettype) ? $targettype : $this->element);
  2178. /*if (empty($sourceid) && empty($targetid))
  2179. {
  2180. dol_syslog('Bad usage of function. No source nor target id defined (nor as parameter nor as object id)', LOG_ERR);
  2181. return -1;
  2182. }*/
  2183. // Links between objects are stored in table element_element
  2184. $sql = 'SELECT rowid, fk_source, sourcetype, fk_target, targettype';
  2185. $sql.= ' FROM '.MAIN_DB_PREFIX.'element_element';
  2186. $sql.= " WHERE ";
  2187. if ($justsource || $justtarget)
  2188. {
  2189. if ($justsource)
  2190. {
  2191. $sql.= "fk_source = '".$sourceid."' AND sourcetype = '".$sourcetype."'";
  2192. if ($withtargettype) $sql.= " AND targettype = '".$targettype."'";
  2193. }
  2194. else if ($justtarget)
  2195. {
  2196. $sql.= "fk_target = '".$targetid."' AND targettype = '".$targettype."'";
  2197. if ($withsourcetype) $sql.= " AND sourcetype = '".$sourcetype."'";
  2198. }
  2199. }
  2200. else
  2201. {
  2202. $sql.= "(fk_source = '".$sourceid."' AND sourcetype = '".$sourcetype."')";
  2203. $sql.= " ".$clause." (fk_target = '".$targetid."' AND targettype = '".$targettype."')";
  2204. }
  2205. $sql .= ' ORDER BY sourcetype';
  2206. //print $sql;
  2207. dol_syslog(get_class($this)."::fetchObjectLink", LOG_DEBUG);
  2208. $resql = $this->db->query($sql);
  2209. if ($resql)
  2210. {
  2211. $num = $this->db->num_rows($resql);
  2212. $i = 0;
  2213. while ($i < $num)
  2214. {
  2215. $obj = $this->db->fetch_object($resql);
  2216. if ($justsource || $justtarget)
  2217. {
  2218. if ($justsource)
  2219. {
  2220. $this->linkedObjectsIds[$obj->targettype][$obj->rowid]=$obj->fk_target;
  2221. }
  2222. else if ($justtarget)
  2223. {
  2224. $this->linkedObjectsIds[$obj->sourcetype][$obj->rowid]=$obj->fk_source;
  2225. }
  2226. }
  2227. else
  2228. {
  2229. if ($obj->fk_source == $sourceid && $obj->sourcetype == $sourcetype)
  2230. {
  2231. $this->linkedObjectsIds[$obj->targettype][$obj->rowid]=$obj->fk_target;
  2232. }
  2233. if ($obj->fk_target == $targetid && $obj->targettype == $targettype)
  2234. {
  2235. $this->linkedObjectsIds[$obj->sourcetype][$obj->rowid]=$obj->fk_source;
  2236. }
  2237. }
  2238. $i++;
  2239. }
  2240. if (! empty($this->linkedObjectsIds))
  2241. {
  2242. foreach($this->linkedObjectsIds as $objecttype => $objectids)
  2243. {
  2244. // Parse element/subelement (ex: project_task)
  2245. $module = $element = $subelement = $objecttype;
  2246. if ($objecttype != 'supplier_proposal' && $objecttype != 'order_supplier' && $objecttype != 'invoice_supplier'
  2247. && preg_match('/^([^_]+)_([^_]+)/i',$objecttype,$regs))
  2248. {
  2249. $module = $element = $regs[1];
  2250. $subelement = $regs[2];
  2251. }
  2252. $classpath = $element.'/class';
  2253. // To work with non standard classpath or module name
  2254. if ($objecttype == 'facture') {
  2255. $classpath = 'compta/facture/class';
  2256. }
  2257. else if ($objecttype == 'facturerec') {
  2258. $classpath = 'compta/facture/class'; $module = 'facture';
  2259. }
  2260. else if ($objecttype == 'propal') {
  2261. $classpath = 'comm/propal/class';
  2262. }
  2263. else if ($objecttype == 'supplier_proposal') {
  2264. $classpath = 'supplier_proposal/class';
  2265. }
  2266. else if ($objecttype == 'shipping') {
  2267. $classpath = 'expedition/class'; $subelement = 'expedition'; $module = 'expedition_bon';
  2268. }
  2269. else if ($objecttype == 'delivery') {
  2270. $classpath = 'livraison/class'; $subelement = 'livraison'; $module = 'livraison_bon';
  2271. }
  2272. else if ($objecttype == 'invoice_supplier' || $objecttype == 'order_supplier') {
  2273. $classpath = 'fourn/class'; $module = 'fournisseur';
  2274. }
  2275. else if ($objecttype == 'fichinter') {
  2276. $classpath = 'fichinter/class'; $subelement = 'fichinter'; $module = 'ficheinter';
  2277. }
  2278. else if ($objecttype == 'subscription') {
  2279. $classpath = 'adherents/class'; $module = 'adherent';
  2280. }
  2281. // Set classfile
  2282. $classfile = strtolower($subelement); $classname = ucfirst($subelement);
  2283. if ($objecttype == 'invoice_supplier') {
  2284. $classfile = 'fournisseur.facture'; $classname = 'FactureFournisseur';
  2285. }
  2286. else if ($objecttype == 'order_supplier') {
  2287. $classfile = 'fournisseur.commande'; $classname = 'CommandeFournisseur';
  2288. }
  2289. else if ($objecttype == 'supplier_proposal') {
  2290. $classfile = 'supplier_proposal'; $classname = 'SupplierProposal';
  2291. }
  2292. else if ($objecttype == 'facturerec') {
  2293. $classfile = 'facture-rec'; $classname = 'FactureRec';
  2294. }
  2295. else if ($objecttype == 'subscription') {
  2296. $classfile = 'cotisation'; $classname = 'Cotisation';
  2297. }
  2298. // Here $module, $classfile and $classname are set
  2299. if ($conf->$module->enabled && (($element != $this->element) || $alsosametype))
  2300. {
  2301. dol_include_once('/'.$classpath.'/'.$classfile.'.class.php');
  2302. foreach($objectids as $i => $objectid) // $i is rowid into llx_element_element
  2303. {
  2304. $object = new $classname($this->db);
  2305. $ret = $object->fetch($objectid);
  2306. if ($ret >= 0)
  2307. {
  2308. $this->linkedObjects[$objecttype][$i] = $object;
  2309. }
  2310. }
  2311. }
  2312. }
  2313. }
  2314. }
  2315. else
  2316. {
  2317. dol_print_error($this->db);
  2318. }
  2319. }
  2320. /**
  2321. * Update object linked of a current object
  2322. *
  2323. * @param int $sourceid Object source id
  2324. * @param string $sourcetype Object source type
  2325. * @param int $targetid Object target id
  2326. * @param string $targettype Object target type
  2327. * @return int >0 if OK, <0 if KO
  2328. * @see add_object_linked, fetObjectLinked, deleteObjectLinked
  2329. */
  2330. function updateObjectLinked($sourceid=null, $sourcetype='', $targetid=null, $targettype='')
  2331. {
  2332. $updatesource=false;
  2333. $updatetarget=false;
  2334. if (! empty($sourceid) && ! empty($sourcetype) && empty($targetid) && empty($targettype)) $updatesource=true;
  2335. else if (empty($sourceid) && empty($sourcetype) && ! empty($targetid) && ! empty($targettype)) $updatetarget=true;
  2336. $sql = "UPDATE ".MAIN_DB_PREFIX."element_element SET ";
  2337. if ($updatesource)
  2338. {
  2339. $sql.= "fk_source = ".$sourceid;
  2340. $sql.= ", sourcetype = '".$sourcetype."'";
  2341. $sql.= " WHERE fk_target = ".$this->id;
  2342. $sql.= " AND targettype = '".$this->element."'";
  2343. }
  2344. else if ($updatetarget)
  2345. {
  2346. $sql.= "fk_target = ".$targetid;
  2347. $sql.= ", targettype = '".$targettype."'";
  2348. $sql.= " WHERE fk_source = ".$this->id;
  2349. $sql.= " AND sourcetype = '".$this->element."'";
  2350. }
  2351. dol_syslog(get_class($this)."::updateObjectLinked", LOG_DEBUG);
  2352. if ($this->db->query($sql))
  2353. {
  2354. return 1;
  2355. }
  2356. else
  2357. {
  2358. $this->error=$this->db->lasterror();
  2359. return -1;
  2360. }
  2361. }
  2362. /**
  2363. * Delete all links between an object $this
  2364. *
  2365. * @param int $sourceid Object source id
  2366. * @param string $sourcetype Object source type
  2367. * @param int $targetid Object target id
  2368. * @param string $targettype Object target type
  2369. * @param int $rowid Row id of line to delete. If defined, other parameters are not used.
  2370. * @return int >0 if OK, <0 if KO
  2371. * @see add_object_linked, updateObjectLinked, fetchObjectLinked
  2372. */
  2373. function deleteObjectLinked($sourceid=null, $sourcetype='', $targetid=null, $targettype='', $rowid='')
  2374. {
  2375. $deletesource=false;
  2376. $deletetarget=false;
  2377. if (! empty($sourceid) && ! empty($sourcetype) && empty($targetid) && empty($targettype)) $deletesource=true;
  2378. else if (empty($sourceid) && empty($sourcetype) && ! empty($targetid) && ! empty($targettype)) $deletetarget=true;
  2379. $sourceid = (! empty($sourceid) ? $sourceid : $this->id);
  2380. $sourcetype = (! empty($sourcetype) ? $sourcetype : $this->element);
  2381. $targetid = (! empty($targetid) ? $targetid : $this->id);
  2382. $targettype = (! empty($targettype) ? $targettype : $this->element);
  2383. $sql = "DELETE FROM ".MAIN_DB_PREFIX."element_element";
  2384. $sql.= " WHERE";
  2385. if ($rowid > 0)
  2386. {
  2387. $sql.=" rowid = ".$rowid;
  2388. }
  2389. else
  2390. {
  2391. if ($deletesource)
  2392. {
  2393. $sql.= " fk_source = ".$sourceid." AND sourcetype = '".$sourcetype."'";
  2394. $sql.= " AND fk_target = ".$this->id." AND targettype = '".$this->element."'";
  2395. }
  2396. else if ($deletetarget)
  2397. {
  2398. $sql.= " fk_target = ".$targetid." AND targettype = '".$targettype."'";
  2399. $sql.= " AND fk_source = ".$this->id." AND sourcetype = '".$this->element."'";
  2400. }
  2401. else
  2402. {
  2403. $sql.= " (fk_source = ".$this->id." AND sourcetype = '".$this->element."')";
  2404. $sql.= " OR";
  2405. $sql.= " (fk_target = ".$this->id." AND targettype = '".$this->element."')";
  2406. }
  2407. }
  2408. dol_syslog(get_class($this)."::deleteObjectLinked", LOG_DEBUG);
  2409. if ($this->db->query($sql))
  2410. {
  2411. return 1;
  2412. }
  2413. else
  2414. {
  2415. $this->error=$this->db->lasterror();
  2416. $this->errors[]=$this->error;
  2417. return -1;
  2418. }
  2419. }
  2420. /**
  2421. * Set status of an object
  2422. *
  2423. * @param int $status Status to set
  2424. * @param int $elementId Id of element to force (use this->id by default)
  2425. * @param string $elementType Type of element to force (use this->table_element by default)
  2426. * @return int <0 if KO, >0 if OK
  2427. */
  2428. function setStatut($status,$elementId=null,$elementType='')
  2429. {
  2430. global $user,$langs,$conf;
  2431. $elementId = (!empty($elementId)?$elementId:$this->id);
  2432. $elementTable = (!empty($elementType)?$elementType:$this->table_element);
  2433. $this->db->begin();
  2434. $fieldstatus="fk_statut";
  2435. if ($elementTable == 'user') $fieldstatus="statut";
  2436. if ($elementTable == 'expensereport') $fieldstatus="fk_statut";
  2437. if ($elementTable == 'commande_fournisseur_dispatch') $fieldstatus="status";
  2438. $sql = "UPDATE ".MAIN_DB_PREFIX.$elementTable;
  2439. $sql.= " SET ".$fieldstatus." = ".$status;
  2440. // If status = 1 = validated, update also fk_user_valid
  2441. if ($status == 1 && $elementTable == 'expensereport') $sql.=", fk_user_valid = ".$user->id;
  2442. $sql.= " WHERE rowid=".$elementId;
  2443. dol_syslog(get_class($this)."::setStatut", LOG_DEBUG);
  2444. if ($this->db->query($sql))
  2445. {
  2446. $error = 0;
  2447. $trigkey='';
  2448. if ($this->element == 'supplier_proposal' && $status == 2) $trigkey='SUPPLIER_PROPOSAL_CLOSE';
  2449. if ($this->element == 'fichinter' && $status == 2) $trigkey='FICHINTER_CLASSIFY_BILLED';
  2450. if ($this->element == 'fichinter' && $status == 1) $trigkey='FICHINTER_CLASSIFY_UNBILLED';
  2451. if ($trigkey)
  2452. {
  2453. // Appel des triggers
  2454. include_once DOL_DOCUMENT_ROOT . '/core/class/interfaces.class.php';
  2455. $interface=new Interfaces($this->db);
  2456. $result=$interface->run_triggers($trigkey,$this,$user,$langs,$conf);
  2457. if ($result < 0) {
  2458. $error++; $this->errors=$interface->errors;
  2459. }
  2460. // Fin appel triggers
  2461. }
  2462. if (! $error)
  2463. {
  2464. $this->db->commit();
  2465. $this->statut = $status;
  2466. return 1;
  2467. }
  2468. else
  2469. {
  2470. $this->db->rollback();
  2471. dol_syslog(get_class($this)."::setStatus ".$this->error,LOG_ERR);
  2472. return -1;
  2473. }
  2474. }
  2475. else
  2476. {
  2477. $this->error=$this->db->lasterror();
  2478. $this->db->rollback();
  2479. return -1;
  2480. }
  2481. }
  2482. /**
  2483. * Load type of canvas of an object if it exists
  2484. *
  2485. * @param int $id Record id
  2486. * @param string $ref Record ref
  2487. * @return int <0 if KO, 0 if nothing done, >0 if OK
  2488. */
  2489. function getCanvas($id=0,$ref='')
  2490. {
  2491. global $conf;
  2492. if (empty($id) && empty($ref)) return 0;
  2493. if (! empty($conf->global->MAIN_DISABLE_CANVAS)) return 0; // To increase speed. Not enabled by default.
  2494. // Clean parameters
  2495. $ref = trim($ref);
  2496. $sql = "SELECT rowid, canvas";
  2497. $sql.= " FROM ".MAIN_DB_PREFIX.$this->table_element;
  2498. $sql.= " WHERE entity IN (".getEntity($this->element, 1).")";
  2499. if (! empty($id)) $sql.= " AND rowid = ".$id;
  2500. if (! empty($ref)) $sql.= " AND ref = '".$this->db->escape($ref)."'";
  2501. $resql = $this->db->query($sql);
  2502. if ($resql)
  2503. {
  2504. $obj = $this->db->fetch_object($resql);
  2505. if ($obj)
  2506. {
  2507. $this->canvas = $obj->canvas;
  2508. return 1;
  2509. }
  2510. else return 0;
  2511. }
  2512. else
  2513. {
  2514. dol_print_error($this->db);
  2515. return -1;
  2516. }
  2517. }
  2518. /**
  2519. * Get special code of a line
  2520. *
  2521. * @param int $lineid Id of line
  2522. * @return int Special code
  2523. */
  2524. function getSpecialCode($lineid)
  2525. {
  2526. $sql = 'SELECT special_code FROM '.MAIN_DB_PREFIX.$this->table_element_line;
  2527. $sql.= ' WHERE rowid = '.$lineid;
  2528. $resql = $this->db->query($sql);
  2529. if ($resql)
  2530. {
  2531. $row = $this->db->fetch_row($resql);
  2532. return $row[0];
  2533. }
  2534. }
  2535. /**
  2536. * Function to check if an object is used by others.
  2537. * Check is done into this->childtables. There is no check into llx_element_element.
  2538. *
  2539. * @param int $id Id of object
  2540. * @return int <0 if KO, 0 if not used, >0 if already used
  2541. */
  2542. function isObjectUsed($id)
  2543. {
  2544. // Check parameters
  2545. if (! isset($this->childtables) || ! is_array($this->childtables) || count($this->childtables) == 0)
  2546. {
  2547. dol_print_error('Called isObjectUsed on a class with property this->childtables not defined');
  2548. return -1;
  2549. }
  2550. // Test if child exists
  2551. $haschild=0;
  2552. foreach($this->childtables as $table)
  2553. {
  2554. // Check if third party can be deleted
  2555. $sql = "SELECT COUNT(*) as nb from ".MAIN_DB_PREFIX.$table;
  2556. $sql.= " WHERE ".$this->fk_element." = ".$id;
  2557. $resql=$this->db->query($sql);
  2558. if ($resql)
  2559. {
  2560. $obj=$this->db->fetch_object($resql);
  2561. $haschild+=$obj->nb;
  2562. //print 'Found into table '.$table;
  2563. if ($haschild) break; // We found at least on, we stop here
  2564. }
  2565. else
  2566. {
  2567. $this->error=$this->db->lasterror();
  2568. return -1;
  2569. }
  2570. }
  2571. if ($haschild > 0)
  2572. {
  2573. $this->error="ErrorRecordHasChildren";
  2574. return $haschild;
  2575. }
  2576. else return 0;
  2577. }
  2578. /**
  2579. * Function to say how many lines object contains
  2580. *
  2581. * @param int $predefined -1=All, 0=Count free product/service only, 1=Count predefined product/service only, 2=Count predefined product, 3=Count predefined service
  2582. * @return int <0 if KO, 0 if no predefined products, nb of lines with predefined products if found
  2583. */
  2584. function hasProductsOrServices($predefined=-1)
  2585. {
  2586. $nb=0;
  2587. foreach($this->lines as $key => $val)
  2588. {
  2589. $qualified=0;
  2590. if ($predefined == -1) $qualified=1;
  2591. if ($predefined == 1 && $val->fk_product > 0) $qualified=1;
  2592. if ($predefined == 0 && $val->fk_product <= 0) $qualified=1;
  2593. if ($predefined == 2 && $val->fk_product > 0 && $val->product_type==0) $qualified=1;
  2594. if ($predefined == 3 && $val->fk_product > 0 && $val->product_type==1) $qualified=1;
  2595. if ($qualified) $nb++;
  2596. }
  2597. dol_syslog(get_class($this).'::hasProductsOrServices we found '.$nb.' qualified lines of products/servcies');
  2598. return $nb;
  2599. }
  2600. /**
  2601. * Function that returns the total amount HT of discounts applied for all lines.
  2602. *
  2603. * @return float
  2604. */
  2605. function getTotalDiscount()
  2606. {
  2607. $total_discount=0.00;
  2608. $sql = "SELECT subprice as pu_ht, qty, remise_percent, total_ht";
  2609. $sql.= " FROM ".MAIN_DB_PREFIX.$this->table_element."det";
  2610. $sql.= " WHERE ".$this->fk_element." = ".$this->id;
  2611. dol_syslog(get_class($this).'::getTotalDiscount', LOG_DEBUG);
  2612. $resql = $this->db->query($sql);
  2613. if ($resql)
  2614. {
  2615. $num=$this->db->num_rows($resql);
  2616. $i=0;
  2617. while ($i < $num)
  2618. {
  2619. $obj = $this->db->fetch_object($resql);
  2620. $pu_ht = $obj->pu_ht;
  2621. $qty= $obj->qty;
  2622. $total_ht = $obj->total_ht;
  2623. $total_discount_line = floatval(price2num(($pu_ht * $qty) - $total_ht, 'MT'));
  2624. $total_discount += $total_discount_line;
  2625. $i++;
  2626. }
  2627. }
  2628. //print $total_discount; exit;
  2629. return price2num($total_discount);
  2630. }
  2631. /**
  2632. * Return into unit=0, the calculated total of weight and volume of all lines * qty
  2633. * Calculate by adding weight and volume of each product line, so properties ->volume/volume_units/weight/weight_units must be loaded on line.
  2634. *
  2635. * @return array array('weight'=>...,'volume'=>...)
  2636. */
  2637. function getTotalWeightVolume()
  2638. {
  2639. $weightUnit=0;
  2640. $volumeUnit=0;
  2641. $totalWeight = '';
  2642. $totalVolume = '';
  2643. $totalOrdered = ''; // defined for shipment only
  2644. $totalToShip = ''; // defined for shipment only
  2645. foreach ($this->lines as $line)
  2646. {
  2647. $totalOrdered+=$line->qty_asked; // defined for shipment only
  2648. $totalToShip+=$line->qty_shipped; // defined for shipment only
  2649. // Define qty, weight, volume, weight_units, volume_units
  2650. if ($this->element == 'shipping') $qty=$line->qty_shipped; // for shipments
  2651. else $qty=$line->qty;
  2652. $weight=$line->weight;
  2653. $volume=$line->volume;
  2654. $weight_units=$line->weight_units;
  2655. $volume_units=$line->volume_units;
  2656. $weightUnit=0;
  2657. $volumeUnit=0;
  2658. if (! empty($weight_units)) $weightUnit = $weight_units;
  2659. if (! empty($volume_units)) $volumeUnit = $volume_units;
  2660. //var_dump($line->volume_units);
  2661. if ($weight_units < 50) // >50 means a standard unit (power of 10 of official unit) > 50 means an exotic unit (like inch)
  2662. {
  2663. $trueWeightUnit=pow(10, $weightUnit);
  2664. $totalWeight += $weight * $qty * $trueWeightUnit;
  2665. }
  2666. else
  2667. {
  2668. $totalWeight += $weight * $qty; // This may be wrong if we mix different units
  2669. }
  2670. if ($volume_units < 50) // >50 means a standard unit (power of 10 of official unit) > 50 means an exotic unit (like inch)
  2671. {
  2672. //print $line->volume."x".$line->volume_units."x".($line->volume_units < 50)."x".$volumeUnit;
  2673. $trueVolumeUnit=pow(10, $volumeUnit);
  2674. //print $line->volume;
  2675. $totalVolume += $volume * $qty * $trueVolumeUnit;
  2676. }
  2677. else
  2678. {
  2679. $totalVolume += $volume * $qty; // This may be wrong if we mix different units
  2680. }
  2681. }
  2682. return array('weight'=>$totalWeight, 'volume'=>$totalVolume, 'ordered'=>$totalOrdered, 'toship'=>$totalToShip);
  2683. }
  2684. /**
  2685. * Set extra parameters
  2686. *
  2687. * @return int <0 if KO, >0 if OK
  2688. */
  2689. function setExtraParameters()
  2690. {
  2691. $this->db->begin();
  2692. $extraparams = (! empty($this->extraparams) ? json_encode($this->extraparams) : null);
  2693. $sql = "UPDATE ".MAIN_DB_PREFIX.$this->table_element;
  2694. $sql.= " SET extraparams = ".(! empty($extraparams) ? "'".$this->db->escape($extraparams)."'" : "null");
  2695. $sql.= " WHERE rowid = ".$this->id;
  2696. dol_syslog(get_class($this)."::setExtraParameters", LOG_DEBUG);
  2697. $resql = $this->db->query($sql);
  2698. if (! $resql)
  2699. {
  2700. $this->error=$this->db->lasterror();
  2701. $this->db->rollback();
  2702. return -1;
  2703. }
  2704. else
  2705. {
  2706. $this->db->commit();
  2707. return 1;
  2708. }
  2709. }
  2710. /**
  2711. * Return incoterms informations
  2712. *
  2713. * @return string incoterms info
  2714. */
  2715. function display_incoterms()
  2716. {
  2717. $out = '';
  2718. $this->libelle_incoterms = '';
  2719. if (!empty($this->fk_incoterms))
  2720. {
  2721. $sql = 'SELECT code FROM '.MAIN_DB_PREFIX.'c_incoterms WHERE rowid = '.(int) $this->fk_incoterms;
  2722. $result = $this->db->query($sql);
  2723. if ($result)
  2724. {
  2725. $res = $this->db->fetch_object($result);
  2726. $out .= $res->code;
  2727. }
  2728. }
  2729. $out .= ' - '.$this->location_incoterms;
  2730. return $out;
  2731. }
  2732. /**
  2733. * Return incoterms informations for pdf display
  2734. *
  2735. * @return string incoterms info
  2736. */
  2737. function getIncotermsForPDF()
  2738. {
  2739. $sql = 'SELECT code FROM '.MAIN_DB_PREFIX.'c_incoterms WHERE rowid = '.(int) $this->fk_incoterms;
  2740. $resql = $this->db->query($sql);
  2741. if ($resql)
  2742. {
  2743. $res = $this->db->fetch_object($resql);
  2744. return 'Incoterm : '.$res->code.' - '.$this->location_incoterms;
  2745. }
  2746. else
  2747. {
  2748. $this->errors[] = $this->db->lasterror();
  2749. return false;
  2750. }
  2751. }
  2752. /**
  2753. * Define incoterms values of current object
  2754. *
  2755. * @param int $id_incoterm Id of incoterm to set or '' to remove
  2756. * @param string $location location of incoterm
  2757. * @return int <0 if KO, >0 if OK
  2758. */
  2759. function setIncoterms($id_incoterm, $location)
  2760. {
  2761. if ($this->id && $this->table_element)
  2762. {
  2763. $sql = "UPDATE ".MAIN_DB_PREFIX.$this->table_element;
  2764. $sql.= " SET fk_incoterms = ".($id_incoterm > 0 ? $id_incoterm : "null");
  2765. $sql.= ", location_incoterms = ".($id_incoterm > 0 ? "'".$this->db->escape($location)."'" : "null");
  2766. $sql.= " WHERE rowid = " . $this->id;
  2767. dol_syslog(get_class($this).'::setIncoterms', LOG_DEBUG);
  2768. $resql=$this->db->query($sql);
  2769. if ($resql)
  2770. {
  2771. $this->fk_incoterms = $id_incoterm;
  2772. $this->location_incoterms = $location;
  2773. $sql = 'SELECT libelle FROM '.MAIN_DB_PREFIX.'c_incoterms WHERE rowid = '.(int) $this->fk_incoterms;
  2774. $res = $this->db->query($sql);
  2775. if ($res)
  2776. {
  2777. $obj = $this->db->fetch_object($res);
  2778. $this->libelle_incoterms = $obj->libelle;
  2779. }
  2780. return 1;
  2781. }
  2782. else
  2783. {
  2784. $this->errors[] = $this->db->lasterror();
  2785. return -1;
  2786. }
  2787. }
  2788. else return -1;
  2789. }
  2790. /**
  2791. * Return if a country is inside the EEC (European Economic Community)
  2792. * TODO Add a field into dictionary
  2793. *
  2794. * @return boolean true = country inside EEC, false = country outside EEC
  2795. */
  2796. function isInEEC()
  2797. {
  2798. // List of all country codes that are in europe for european vat rules
  2799. // List found on http://ec.europa.eu/taxation_customs/common/faq/faq_1179_en.htm#9
  2800. $country_code_in_EEC=array(
  2801. 'AT', // Austria
  2802. 'BE', // Belgium
  2803. 'BG', // Bulgaria
  2804. 'CY', // Cyprus
  2805. 'CZ', // Czech republic
  2806. 'DE', // Germany
  2807. 'DK', // Danemark
  2808. 'EE', // Estonia
  2809. 'ES', // Spain
  2810. 'FI', // Finland
  2811. 'FR', // France
  2812. 'GB', // United Kingdom
  2813. 'GR', // Greece
  2814. 'HR', // Croatia
  2815. 'NL', // Holland
  2816. 'HU', // Hungary
  2817. 'IE', // Ireland
  2818. 'IM', // Isle of Man - Included in UK
  2819. 'IT', // Italy
  2820. 'LT', // Lithuania
  2821. 'LU', // Luxembourg
  2822. 'LV', // Latvia
  2823. 'MC', // Monaco - Included in France
  2824. 'MT', // Malta
  2825. //'NO', // Norway
  2826. 'PL', // Poland
  2827. 'PT', // Portugal
  2828. 'RO', // Romania
  2829. 'SE', // Sweden
  2830. 'SK', // Slovakia
  2831. 'SI', // Slovenia
  2832. 'UK', // United Kingdom
  2833. //'CH', // Switzerland - No. Swizerland in not in EEC
  2834. );
  2835. //print "dd".$this->country_code;
  2836. return in_array($this->country_code,$country_code_in_EEC);
  2837. }
  2838. // --------------------
  2839. // TODO: All functions here must be redesigned and moved as they are not business functions but output functions
  2840. // --------------------
  2841. /**
  2842. * Show linked object block.
  2843. *
  2844. * @return int <0 if KO, >0 if OK
  2845. * @deprecated 3.8 Use instead $form->showLinkedObjectBlock($object)
  2846. * @see Form::showLinkedObjectBlock
  2847. */
  2848. function showLinkedObjectBlock()
  2849. {
  2850. global $form;
  2851. return $form->showLinkedObjectBlock($this);
  2852. }
  2853. /* This is to show add lines */
  2854. /**
  2855. * Show add free and predefined products/services form
  2856. *
  2857. * @param int $dateSelector 1=Show also date range input fields
  2858. * @param Societe $seller Object thirdparty who sell
  2859. * @param Societe $buyer Object thirdparty who buy
  2860. * @return void
  2861. */
  2862. function formAddObjectLine($dateSelector,$seller,$buyer)
  2863. {
  2864. global $conf,$user,$langs,$object,$hookmanager;
  2865. global $form,$bcnd,$var;
  2866. //Line extrafield
  2867. require_once DOL_DOCUMENT_ROOT.'/core/class/extrafields.class.php';
  2868. $extrafieldsline = new ExtraFields($this->db);
  2869. $extralabelslines=$extrafieldsline->fetch_name_optionals_label($this->table_element_line);
  2870. // Output template part (modules that overwrite templates must declare this into descriptor)
  2871. // Use global variables + $dateSelector + $seller and $buyer
  2872. $dirtpls=array_merge($conf->modules_parts['tpl'],array('/core/tpl'));
  2873. foreach($dirtpls as $reldir)
  2874. {
  2875. $tpl = dol_buildpath($reldir.'/objectline_create.tpl.php');
  2876. if (empty($conf->file->strict_mode)) {
  2877. $res=@include $tpl;
  2878. } else {
  2879. $res=include $tpl; // for debug
  2880. }
  2881. if ($res) break;
  2882. }
  2883. }
  2884. /* This is to show array of line of details */
  2885. /**
  2886. * Return HTML table for object lines
  2887. * TODO Move this into an output class file (htmlline.class.php)
  2888. * If lines are into a template, title must also be into a template
  2889. * But for the moment we don't know if it'st possible as we keep a method available on overloaded objects.
  2890. *
  2891. * @param string $action Action code
  2892. * @param string $seller Object of seller third party
  2893. * @param string $buyer Object of buyer third party
  2894. * @param int $selected Object line selected
  2895. * @param int $dateSelector 1=Show also date range input fields
  2896. * @return void
  2897. */
  2898. function printObjectLines($action, $seller, $buyer, $selected=0, $dateSelector=0)
  2899. {
  2900. global $conf, $hookmanager, $langs, $user;
  2901. // TODO We should not use global var for this !
  2902. global $inputalsopricewithtax, $usemargins, $disableedit, $disablemove, $disableremove;
  2903. // Define usemargins
  2904. $usemargins=0;
  2905. if (! empty($conf->margin->enabled) && ! empty($this->element) && in_array($this->element,array('facture','propal','commande'))) $usemargins=1;
  2906. print '<tr class="liste_titre nodrag nodrop">';
  2907. if (! empty($conf->global->MAIN_VIEW_LINE_NUMBER)) print '<td class="linecolnum" align="center" width="5">&nbsp;</td>';
  2908. // Description
  2909. print '<td class="linecoldescription">'.$langs->trans('Description').'</td>';
  2910. if ($this->element == 'supplier_proposal')
  2911. {
  2912. print '<td class="linerefsupplier" align="right"><span id="title_fourn_ref">'.$langs->trans("SupplierProposalRefFourn").'</span></td>';
  2913. }
  2914. // VAT
  2915. print '<td class="linecolvat" align="right" width="50">'.$langs->trans('VAT').'</td>';
  2916. // Price HT
  2917. print '<td class="linecoluht" align="right" width="80">'.$langs->trans('PriceUHT').'</td>';
  2918. // Multicurrency
  2919. if (!empty($conf->multicurrency->enabled)) print '<td class="linecoluht_currency" align="right" width="80">'.$langs->trans('PriceUHTCurrency').'</td>';
  2920. if ($inputalsopricewithtax) print '<td align="right" width="80">'.$langs->trans('PriceUTTC').'</td>';
  2921. // Qty
  2922. print '<td class="linecolqty" align="right">'.$langs->trans('Qty').'</td>';
  2923. if($conf->global->PRODUCT_USE_UNITS)
  2924. {
  2925. print '<td class="linecoluseunit" align="left">'.$langs->trans('Unit').'</td>';
  2926. }
  2927. // Reduction short
  2928. print '<td class="linecoldiscount" align="right">'.$langs->trans('ReductionShort').'</td>';
  2929. if ($this->situation_cycle_ref) {
  2930. print '<td class="linecolcycleref" align="right">' . $langs->trans('Progress') . '</td>';
  2931. }
  2932. if ($usemargins && ! empty($conf->margin->enabled) && empty($user->societe_id))
  2933. {
  2934. if ($conf->global->MARGIN_TYPE == "1")
  2935. print '<td class="linecolmargin1 margininfos" align="right" width="80">'.$langs->trans('BuyingPrice').'</td>';
  2936. else
  2937. print '<td class="linecolmargin1 margininfos" align="right" width="80">'.$langs->trans('CostPrice').'</td>';
  2938. if (! empty($conf->global->DISPLAY_MARGIN_RATES) && $user->rights->margins->liretous)
  2939. print '<td class="linecolmargin2 margininfos" align="right" width="50">'.$langs->trans('MarginRate').'</td>';
  2940. if (! empty($conf->global->DISPLAY_MARK_RATES) && $user->rights->margins->liretous)
  2941. print '<td class="linecolmargin2 margininfos" align="right" width="50">'.$langs->trans('MarkRate').'</td>';
  2942. }
  2943. // Total HT
  2944. print '<td class="linecolht" align="right">'.$langs->trans('TotalHTShort').'</td>';
  2945. // Multicurrency
  2946. if (!empty($conf->multicurrency->enabled)) print '<td class="linecoltotalht_currency" align="right">'.$langs->trans('TotalHTShortCurrency').'</td>';
  2947. print '<td class="linecoledit"></td>'; // No width to allow autodim
  2948. print '<td class="linecoldelete" width="10"></td>';
  2949. print '<td class="linecolmove" width="10"></td>';
  2950. print "</tr>\n";
  2951. $num = count($this->lines);
  2952. $var = true;
  2953. $i = 0;
  2954. //Line extrafield
  2955. require_once DOL_DOCUMENT_ROOT.'/core/class/extrafields.class.php';
  2956. $extrafieldsline = new ExtraFields($this->db);
  2957. $extralabelslines=$extrafieldsline->fetch_name_optionals_label($this->table_element_line);
  2958. foreach ($this->lines as $line)
  2959. {
  2960. //Line extrafield
  2961. $line->fetch_optionals($line->id,$extralabelslines);
  2962. $var=!$var;
  2963. //if (is_object($hookmanager) && (($line->product_type == 9 && ! empty($line->special_code)) || ! empty($line->fk_parent_line)))
  2964. if (is_object($hookmanager)) // Old code is commented on preceding line.
  2965. {
  2966. if (empty($line->fk_parent_line))
  2967. {
  2968. $parameters = array('line'=>$line,'var'=>$var,'num'=>$num,'i'=>$i,'dateSelector'=>$dateSelector,'seller'=>$seller,'buyer'=>$buyer,'selected'=>$selected, 'extrafieldsline'=>$extrafieldsline);
  2969. $reshook = $hookmanager->executeHooks('printObjectLine', $parameters, $this, $action); // Note that $action and $object may have been modified by some hooks
  2970. }
  2971. else
  2972. {
  2973. $parameters = array('line'=>$line,'var'=>$var,'num'=>$num,'i'=>$i,'dateSelector'=>$dateSelector,'seller'=>$seller,'buyer'=>$buyer,'selected'=>$selected, 'extrafieldsline'=>$extrafieldsline);
  2974. $reshook = $hookmanager->executeHooks('printObjectSubLine', $parameters, $this, $action); // Note that $action and $object may have been modified by some hooks
  2975. }
  2976. }
  2977. if (empty($reshook))
  2978. {
  2979. $this->printObjectLine($action,$line,$var,$num,$i,$dateSelector,$seller,$buyer,$selected,$extrafieldsline);
  2980. }
  2981. $i++;
  2982. }
  2983. }
  2984. /**
  2985. * Return HTML content of a detail line
  2986. * TODO Move this into an output class file (htmlline.class.php)
  2987. *
  2988. * @param string $action GET/POST action
  2989. * @param CommonObjectLine $line Selected object line to output
  2990. * @param string $var Is it a an odd line (true)
  2991. * @param int $num Number of line (0)
  2992. * @param int $i I
  2993. * @param int $dateSelector 1=Show also date range input fields
  2994. * @param string $seller Object of seller third party
  2995. * @param string $buyer Object of buyer third party
  2996. * @param int $selected Object line selected
  2997. * @param int $extrafieldsline Object of extrafield line attribute
  2998. * @return void
  2999. */
  3000. function printObjectLine($action,$line,$var,$num,$i,$dateSelector,$seller,$buyer,$selected=0,$extrafieldsline=0)
  3001. {
  3002. global $conf,$langs,$user,$object,$hookmanager;
  3003. global $form,$bc,$bcdd;
  3004. global $object_rights, $disableedit, $disablemove; // TODO We should not use global var for this !
  3005. $object_rights = $this->getRights();
  3006. $element=$this->element;
  3007. $text=''; $description=''; $type=0;
  3008. // Show product and description
  3009. $type=(! empty($line->product_type)?$line->product_type:$line->fk_product_type);
  3010. // Try to enhance type detection using date_start and date_end for free lines where type was not saved.
  3011. if (! empty($line->date_start)) $type=1; // deprecated
  3012. if (! empty($line->date_end)) $type=1; // deprecated
  3013. // Ligne en mode visu
  3014. if ($action != 'editline' || $selected != $line->id)
  3015. {
  3016. // Product
  3017. if ($line->fk_product > 0)
  3018. {
  3019. $product_static = new Product($this->db);
  3020. $product_static->fetch($line->fk_product);
  3021. $product_static->ref = $line->ref; //can change ref in hook
  3022. $product_static->label = $line->label; //can change label in hook
  3023. $text=$product_static->getNomUrl(1);
  3024. // Define output language and label
  3025. if (! empty($conf->global->MAIN_MULTILANGS))
  3026. {
  3027. if (! is_object($this->thirdparty))
  3028. {
  3029. dol_print_error('','Error: Method printObjectLine was called on an object and object->fetch_thirdparty was not done before');
  3030. return;
  3031. }
  3032. $prod = new Product($this->db);
  3033. $prod->fetch($line->fk_product);
  3034. $outputlangs = $langs;
  3035. $newlang='';
  3036. if (empty($newlang) && GETPOST('lang_id')) $newlang=GETPOST('lang_id');
  3037. if (! empty($conf->global->PRODUIT_TEXTS_IN_THIRDPARTY_LANGUAGE) && empty($newlang)) $newlang=$this->thirdparty->default_lang; // For language to language of customer
  3038. if (! empty($newlang))
  3039. {
  3040. $outputlangs = new Translate("",$conf);
  3041. $outputlangs->setDefaultLang($newlang);
  3042. }
  3043. $label = (! empty($prod->multilangs[$outputlangs->defaultlang]["label"])) ? $prod->multilangs[$outputlangs->defaultlang]["label"] : $line->product_label;
  3044. }
  3045. else
  3046. {
  3047. $label = $line->product_label;
  3048. }
  3049. $text.= ' - '.(! empty($line->label)?$line->label:$label);
  3050. $description.=(! empty($conf->global->PRODUIT_DESC_IN_FORM)?'':dol_htmlentitiesbr($line->description)); // Description is what to show on popup. We shown nothing if already into desc.
  3051. }
  3052. $line->pu_ttc = price2num($line->subprice * (1 + ($line->tva_tx/100)), 'MU');
  3053. // Output template part (modules that overwrite templates must declare this into descriptor)
  3054. // Use global variables + $dateSelector + $seller and $buyer
  3055. $dirtpls=array_merge($conf->modules_parts['tpl'],array('/core/tpl'));
  3056. foreach($dirtpls as $reldir)
  3057. {
  3058. $tpl = dol_buildpath($reldir.'/objectline_view.tpl.php');
  3059. if (empty($conf->file->strict_mode)) {
  3060. $res=@include $tpl;
  3061. } else {
  3062. $res=include $tpl; // for debug
  3063. }
  3064. if ($res) break;
  3065. }
  3066. }
  3067. // Ligne en mode update
  3068. if ($this->statut == 0 && $action == 'editline' && $selected == $line->id)
  3069. {
  3070. $label = (! empty($line->label) ? $line->label : (($line->fk_product > 0) ? $line->product_label : ''));
  3071. if (! empty($conf->global->MAIN_HTML5_PLACEHOLDER)) $placeholder=' placeholder="'.$langs->trans("Label").'"';
  3072. else $placeholder=' title="'.$langs->trans("Label").'"';
  3073. $line->pu_ttc = price2num($line->subprice * (1 + ($line->tva_tx/100)), 'MU');
  3074. // Output template part (modules that overwrite templates must declare this into descriptor)
  3075. // Use global variables + $dateSelector + $seller and $buyer
  3076. $dirtpls=array_merge($conf->modules_parts['tpl'],array('/core/tpl'));
  3077. foreach($dirtpls as $reldir)
  3078. {
  3079. $tpl = dol_buildpath($reldir.'/objectline_edit.tpl.php');
  3080. if (empty($conf->file->strict_mode)) {
  3081. $res=@include $tpl;
  3082. } else {
  3083. $res=include $tpl; // for debug
  3084. }
  3085. if ($res) break;
  3086. }
  3087. }
  3088. }
  3089. /* This is to show array of line of details of source object */
  3090. /**
  3091. * Return HTML table table of source object lines
  3092. * TODO Move this and previous function into output html class file (htmlline.class.php).
  3093. * If lines are into a template, title must also be into a template
  3094. * But for the moment we don't know if it's possible as we keep a method available on overloaded objects.
  3095. *
  3096. * @return void
  3097. */
  3098. function printOriginLinesList()
  3099. {
  3100. global $langs, $hookmanager, $conf;
  3101. print '<tr class="liste_titre">';
  3102. print '<td>'.$langs->trans('Ref').'</td>';
  3103. print '<td>'.$langs->trans('Description').'</td>';
  3104. print '<td align="right">'.$langs->trans('VAT').'</td>';
  3105. print '<td align="right">'.$langs->trans('PriceUHT').'</td>';
  3106. if (!empty($conf->multicurrency->enabled)) print '<td align="right">'.$langs->trans('PriceUHTCurrency').'</td>';
  3107. print '<td align="right">'.$langs->trans('Qty').'</td>';
  3108. if($conf->global->PRODUCT_USE_UNITS)
  3109. {
  3110. print '<td align="left">'.$langs->trans('Unit').'</td>';
  3111. }
  3112. print '<td align="right">'.$langs->trans('ReductionShort').'</td></tr>';
  3113. $var = true;
  3114. $i = 0;
  3115. foreach ($this->lines as $line)
  3116. {
  3117. $var=!$var;
  3118. if (is_object($hookmanager) && (($line->product_type == 9 && ! empty($line->special_code)) || ! empty($line->fk_parent_line)))
  3119. {
  3120. if (empty($line->fk_parent_line))
  3121. {
  3122. $parameters=array('line'=>$line,'var'=>$var,'i'=>$i);
  3123. $action='';
  3124. $hookmanager->executeHooks('printOriginObjectLine',$parameters,$this,$action); // Note that $action and $object may have been modified by some hooks
  3125. }
  3126. }
  3127. else
  3128. {
  3129. $this->printOriginLine($line,$var);
  3130. }
  3131. $i++;
  3132. }
  3133. }
  3134. /**
  3135. * Return HTML with a line of table array of source object lines
  3136. * TODO Move this and previous function into output html class file (htmlline.class.php).
  3137. * If lines are into a template, title must also be into a template
  3138. * But for the moment we don't know if it's possible as we keep a method available on overloaded objects.
  3139. *
  3140. * @param CommonObjectLine $line Line
  3141. * @param string $var Var
  3142. * @return void
  3143. */
  3144. function printOriginLine($line,$var)
  3145. {
  3146. global $langs, $conf;
  3147. //var_dump($line);
  3148. if (!empty($line->date_start))
  3149. {
  3150. $date_start=$line->date_start;
  3151. }
  3152. else
  3153. {
  3154. $date_start=$line->date_debut_prevue;
  3155. if ($line->date_debut_reel) $date_start=$line->date_debut_reel;
  3156. }
  3157. if (!empty($line->date_end))
  3158. {
  3159. $date_end=$line->date_end;
  3160. }
  3161. else
  3162. {
  3163. $date_end=$line->date_fin_prevue;
  3164. if ($line->date_fin_reel) $date_end=$line->date_fin_reel;
  3165. }
  3166. $this->tpl['label'] = '';
  3167. if (! empty($line->fk_parent_line)) $this->tpl['label'].= img_picto('', 'rightarrow');
  3168. if (($line->info_bits & 2) == 2) // TODO Not sure this is used for source object
  3169. {
  3170. $discount=new DiscountAbsolute($this->db);
  3171. $discount->fk_soc = $this->socid;
  3172. $this->tpl['label'].= $discount->getNomUrl(0,'discount');
  3173. }
  3174. else if (! empty($line->fk_product))
  3175. {
  3176. $productstatic = new Product($this->db);
  3177. $productstatic->id = $line->fk_product;
  3178. $productstatic->ref = $line->ref;
  3179. $productstatic->type = $line->fk_product_type;
  3180. $this->tpl['label'].= $productstatic->getNomUrl(1);
  3181. $this->tpl['label'].= ' - '.(! empty($line->label)?$line->label:$line->product_label);
  3182. // Dates
  3183. if ($line->product_type == 1 && ($date_start || $date_end))
  3184. {
  3185. $this->tpl['label'].= get_date_range($date_start,$date_end);
  3186. }
  3187. }
  3188. else
  3189. {
  3190. $this->tpl['label'].= ($line->product_type == -1 ? '&nbsp;' : ($line->product_type == 1 ? img_object($langs->trans(''),'service') : img_object($langs->trans(''),'product')));
  3191. if (!empty($line->desc)) {
  3192. $this->tpl['label'].=$line->desc;
  3193. }else {
  3194. $this->tpl['label'].= ($line->label ? '&nbsp;'.$line->label : '');
  3195. }
  3196. // Dates
  3197. if ($line->product_type == 1 && ($date_start || $date_end))
  3198. {
  3199. $this->tpl['label'].= get_date_range($date_start,$date_end);
  3200. }
  3201. }
  3202. if (! empty($line->desc))
  3203. {
  3204. if ($line->desc == '(CREDIT_NOTE)') // TODO Not sure this is used for source object
  3205. {
  3206. $discount=new DiscountAbsolute($this->db);
  3207. $discount->fetch($line->fk_remise_except);
  3208. $this->tpl['description'] = $langs->transnoentities("DiscountFromCreditNote",$discount->getNomUrl(0));
  3209. }
  3210. elseif ($line->desc == '(DEPOSIT)') // TODO Not sure this is used for source object
  3211. {
  3212. $discount=new DiscountAbsolute($this->db);
  3213. $discount->fetch($line->fk_remise_except);
  3214. $this->tpl['description'] = $langs->transnoentities("DiscountFromDeposit",$discount->getNomUrl(0));
  3215. }
  3216. else
  3217. {
  3218. $this->tpl['description'] = dol_trunc($line->desc,60);
  3219. }
  3220. }
  3221. else
  3222. {
  3223. $this->tpl['description'] = '&nbsp;';
  3224. }
  3225. $this->tpl['vat_rate'] = vatrate($line->tva_tx, true);
  3226. $this->tpl['price'] = price($line->subprice);
  3227. $this->tpl['multicurrency_price'] = price($line->multicurrency_subprice);
  3228. $this->tpl['qty'] = (($line->info_bits & 2) != 2) ? $line->qty : '&nbsp;';
  3229. if($conf->global->PRODUCT_USE_UNITS) $this->tpl['unit'] = $line->getLabelOfUnit('long');
  3230. $this->tpl['remise_percent'] = (($line->info_bits & 2) != 2) ? vatrate($line->remise_percent, true) : '&nbsp;';
  3231. // Output template part (modules that overwrite templates must declare this into descriptor)
  3232. // Use global variables + $dateSelector + $seller and $buyer
  3233. $dirtpls=array_merge($conf->modules_parts['tpl'],array('/core/tpl'));
  3234. foreach($dirtpls as $reldir)
  3235. {
  3236. $tpl = dol_buildpath($reldir.'/originproductline.tpl.php');
  3237. if (empty($conf->file->strict_mode)) {
  3238. $res=@include $tpl;
  3239. } else {
  3240. $res=include $tpl; // for debug
  3241. }
  3242. if ($res) break;
  3243. }
  3244. }
  3245. /**
  3246. * Show the array with all margin infos
  3247. *
  3248. * @param bool $force_price Force price
  3249. * @return void
  3250. * @deprecated 3.8 Load FormMargin class and make a direct call to displayMarginInfos
  3251. */
  3252. function displayMarginInfos($force_price=false)
  3253. {
  3254. include_once DOL_DOCUMENT_ROOT.'/core/class/html.formmargin.class.php';
  3255. $formmargin=new FormMargin($this->db);
  3256. $formmargin->displayMarginInfos($this, $force_price);
  3257. }
  3258. /**
  3259. * Add resources to the current object : add entry into llx_element_resources
  3260. * Need $this->element & $this->id
  3261. *
  3262. * @param int $resource_id Resource id
  3263. * @param string $resource_type 'resource'
  3264. * @param int $busy Busy or not
  3265. * @param int $mandatory Mandatory or not
  3266. * @return int <=0 if KO, >0 if OK
  3267. */
  3268. function add_element_resource($resource_id, $resource_type, $busy=0, $mandatory=0)
  3269. {
  3270. $this->db->begin();
  3271. $sql = "INSERT INTO ".MAIN_DB_PREFIX."element_resources (";
  3272. $sql.= "resource_id";
  3273. $sql.= ", resource_type";
  3274. $sql.= ", element_id";
  3275. $sql.= ", element_type";
  3276. $sql.= ", busy";
  3277. $sql.= ", mandatory";
  3278. $sql.= ") VALUES (";
  3279. $sql.= $resource_id;
  3280. $sql.= ", '".$resource_type."'";
  3281. $sql.= ", '".$this->id."'";
  3282. $sql.= ", '".$this->element."'";
  3283. $sql.= ", '".$busy."'";
  3284. $sql.= ", '".$mandatory."'";
  3285. $sql.= ")";
  3286. dol_syslog(get_class($this)."::add_element_resource", LOG_DEBUG);
  3287. if ($this->db->query($sql))
  3288. {
  3289. $this->db->commit();
  3290. return 1;
  3291. }
  3292. else
  3293. {
  3294. $this->error=$this->db->lasterror();
  3295. $this->db->rollback();
  3296. return 0;
  3297. }
  3298. }
  3299. /**
  3300. * Delete a link to resource line
  3301. *
  3302. * @param int $rowid Id of resource line to delete
  3303. * @param int $element element name (for trigger) TODO: use $this->element into commonobject class
  3304. * @param int $notrigger Disable all triggers
  3305. * @return int >0 if OK, <0 if KO
  3306. */
  3307. function delete_resource($rowid, $element, $notrigger=0)
  3308. {
  3309. global $user;
  3310. $this->db->begin();
  3311. $sql = "DELETE FROM ".MAIN_DB_PREFIX."element_resources";
  3312. $sql.= " WHERE rowid=".$rowid;
  3313. dol_syslog(get_class($this)."::delete_resource", LOG_DEBUG);
  3314. $resql=$this->db->query($sql);
  3315. if (! $resql)
  3316. {
  3317. $this->error=$this->db->lasterror();
  3318. $this->db->rollback();
  3319. return -1;
  3320. }
  3321. else
  3322. {
  3323. if (! $notrigger)
  3324. {
  3325. $result=$this->call_trigger(strtoupper($element).'_DELETE_RESOURCE', $user);
  3326. if ($result < 0) { $this->db->rollback(); return -1; }
  3327. }
  3328. $this->db->commit();
  3329. return 1;
  3330. }
  3331. }
  3332. /**
  3333. * Overwrite magic function to solve problem of cloning object that are kept as references
  3334. *
  3335. * @return void
  3336. */
  3337. function __clone()
  3338. {
  3339. // Force a copy of this->lines, otherwise it will point to same object.
  3340. if (isset($this->lines) && is_array($this->lines))
  3341. {
  3342. $nboflines=count($this->lines);
  3343. for($i=0; $i < $nboflines; $i++)
  3344. {
  3345. $this->lines[$i] = clone $this->lines[$i];
  3346. }
  3347. }
  3348. }
  3349. /**
  3350. * Common function for all objects extending CommonObject for generating documents
  3351. *
  3352. * @param string $modelspath Relative folder where generators are placed
  3353. * @param string $modele Generator to use. Caller must set it to obj->modelpdf or GETPOST('modelpdf') for example.
  3354. * @param Translate $outputlangs Language to use
  3355. * @param int $hidedetails 1 to hide details. 0 by default
  3356. * @param int $hidedesc 1 to hide product description. 0 by default
  3357. * @param int $hideref 1 to hide product reference. 0 by default
  3358. * @return int >0 if OK, <0 if KO
  3359. */
  3360. protected function commonGenerateDocument($modelspath, $modele, $outputlangs, $hidedetails, $hidedesc, $hideref)
  3361. {
  3362. global $conf, $langs;
  3363. $srctemplatepath='';
  3364. // Increase limit for PDF build
  3365. $err=error_reporting();
  3366. error_reporting(0);
  3367. @set_time_limit(120);
  3368. error_reporting($err);
  3369. // If selected model is a filename template (then $modele="modelname" or "modelname:filename")
  3370. $tmp=explode(':',$modele,2);
  3371. if (! empty($tmp[1]))
  3372. {
  3373. $modele=$tmp[0];
  3374. $srctemplatepath=$tmp[1];
  3375. }
  3376. // Search template files
  3377. $file=''; $classname=''; $filefound=0;
  3378. $dirmodels=array('/');
  3379. if (is_array($conf->modules_parts['models'])) $dirmodels=array_merge($dirmodels,$conf->modules_parts['models']);
  3380. foreach($dirmodels as $reldir)
  3381. {
  3382. foreach(array('doc','pdf') as $prefix)
  3383. {
  3384. $file = $prefix."_".$modele.".modules.php";
  3385. // On verifie l'emplacement du modele
  3386. $file=dol_buildpath($reldir.$modelspath.$file,0);
  3387. if (file_exists($file))
  3388. {
  3389. $filefound=1;
  3390. $classname=$prefix.'_'.$modele;
  3391. break;
  3392. }
  3393. }
  3394. if ($filefound) break;
  3395. }
  3396. // If generator was found
  3397. if ($filefound)
  3398. {
  3399. require_once $file;
  3400. $obj = new $classname($this->db);
  3401. // If generator is ODT, we must have srctemplatepath defined, if not we set it.
  3402. if ($obj->type == 'odt' && empty($srctemplatepath))
  3403. {
  3404. $varfortemplatedir=$obj->scandir;
  3405. if ($varfortemplatedir && ! empty($conf->global->$varfortemplatedir))
  3406. {
  3407. $dirtoscan=$conf->global->$varfortemplatedir;
  3408. $listoffiles=array();
  3409. // Now we add first model found in directories scanned
  3410. $listofdir=explode(',',$dirtoscan);
  3411. foreach($listofdir as $key=>$tmpdir)
  3412. {
  3413. $tmpdir=trim($tmpdir);
  3414. $tmpdir=preg_replace('/DOL_DATA_ROOT/',DOL_DATA_ROOT,$tmpdir);
  3415. if (! $tmpdir) { unset($listofdir[$key]); continue; }
  3416. if (is_dir($tmpdir))
  3417. {
  3418. $tmpfiles=dol_dir_list($tmpdir,'files',0,'\.od(s|t)$','','name',SORT_ASC,0);
  3419. if (count($tmpfiles)) $listoffiles=array_merge($listoffiles,$tmpfiles);
  3420. }
  3421. }
  3422. if (count($listoffiles))
  3423. {
  3424. foreach($listoffiles as $record)
  3425. {
  3426. $srctemplatepath=$record['fullname'];
  3427. break;
  3428. }
  3429. }
  3430. }
  3431. if (empty($srctemplatepath))
  3432. {
  3433. $this->error='ErrorGenerationAskedForOdtTemplateWithSrcFileNotDefined';
  3434. return -1;
  3435. }
  3436. }
  3437. if ($obj->type == 'odt' && ! empty($srctemplatepath))
  3438. {
  3439. if (! dol_is_file($srctemplatepath))
  3440. {
  3441. $this->error='ErrorGenerationAskedForOdtTemplateWithSrcFileNotFound';
  3442. return -1;
  3443. }
  3444. }
  3445. // We save charset_output to restore it because write_file can change it if needed for
  3446. // output format that does not support UTF8.
  3447. $sav_charset_output=$outputlangs->charset_output;
  3448. if ($obj->write_file($this, $outputlangs, $srctemplatepath, $hidedetails, $hidedesc, $hideref) > 0)
  3449. {
  3450. $outputlangs->charset_output=$sav_charset_output;
  3451. // We delete old preview
  3452. require_once DOL_DOCUMENT_ROOT.'/core/lib/files.lib.php';
  3453. dol_delete_preview($this);
  3454. // Success in building document. We build meta file.
  3455. dol_meta_create($this);
  3456. return 1;
  3457. }
  3458. else
  3459. {
  3460. $outputlangs->charset_output=$sav_charset_output;
  3461. dol_print_error($this->db, "Error generating document for ".__CLASS__.". Error: ".$obj->error, $obj->errors);
  3462. return -1;
  3463. }
  3464. }
  3465. else
  3466. {
  3467. $this->error=$langs->trans("Error")." ".$langs->trans("ErrorFileDoesNotExists",$file);
  3468. dol_print_error('',$this->error);
  3469. return -1;
  3470. }
  3471. }
  3472. /**
  3473. * Build thumb
  3474. *
  3475. * @param string $file Path file in UTF8 to original file to create thumbs from.
  3476. * @return void
  3477. */
  3478. function addThumbs($file)
  3479. {
  3480. global $maxwidthsmall, $maxheightsmall, $maxwidthmini, $maxheightmini, $quality;
  3481. require_once DOL_DOCUMENT_ROOT .'/core/lib/images.lib.php'; // This define also $maxwidthsmall, $quality, ...
  3482. $file_osencoded=dol_osencode($file);
  3483. if (file_exists($file_osencoded))
  3484. {
  3485. // Create small thumbs for company (Ratio is near 16/9)
  3486. // Used on logon for example
  3487. vignette($file_osencoded, $maxwidthsmall, $maxheightsmall, '_small', $quality);
  3488. // Create mini thumbs for company (Ratio is near 16/9)
  3489. // Used on menu or for setup page for example
  3490. vignette($file_osencoded, $maxwidthmini, $maxheightmini, '_mini', $quality);
  3491. }
  3492. }
  3493. /* Functions common to commonobject and commonobjectline */
  3494. /* For default values */
  3495. /**
  3496. * Return the default value to use for a field when showing the create form of object.
  3497. * Return values in this order:
  3498. * 1) If parameter is available into POST, we return it first.
  3499. * 2) If not but an alternate value was provided as parameter of function, we return it.
  3500. * 3) If not but a constant $conf->global->OBJECTELEMENT_FIELDNAME is set, we return it (It is better to use the dedicated table).
  3501. * 4) Return value found into database (TODO No yet implemented)
  3502. *
  3503. * @param string $fieldname Name of field
  3504. * @param string $alternatevalue Alternate value to use
  3505. * @return string|string[] Default value (can be an array if the GETPOST return an array)
  3506. **/
  3507. function getDefaultCreateValueFor($fieldname, $alternatevalue=null)
  3508. {
  3509. global $conf, $_POST;
  3510. // If param here has been posted, we use this value first.
  3511. if (isset($_POST[$fieldname])) return GETPOST($fieldname, 2);
  3512. if (isset($alternatevalue)) return $alternatevalue;
  3513. $newelement=$this->element;
  3514. if ($newelement == 'facture') $newelement='invoice';
  3515. if ($newelement == 'commande') $newelement='order';
  3516. if (empty($newelement))
  3517. {
  3518. dol_syslog("Ask a default value using common method getDefaultCreateValueForField on an object with no property ->element defined. Return empty string.", LOG_WARNING);
  3519. return '';
  3520. }
  3521. $keyforfieldname=strtoupper($newelement.'_DEFAULT_'.$fieldname);
  3522. //var_dump($keyforfieldname);
  3523. if (isset($conf->global->$keyforfieldname)) return $conf->global->$keyforfieldname;
  3524. // TODO Ad here a scan into table llx_overwrite_default with a filter on $this->element and $fieldname
  3525. }
  3526. /* For triggers */
  3527. /**
  3528. * Call trigger based on this instance.
  3529. * Some context information may also be provided into array property this->context.
  3530. * NB: Error from trigger are stacked in interface->errors
  3531. * NB2: If return code of triggers are < 0, action calling trigger should cancel all transaction.
  3532. *
  3533. * @param string $trigger_name trigger's name to execute
  3534. * @param User $user Object user
  3535. * @return int Result of run_triggers
  3536. */
  3537. function call_trigger($trigger_name, $user)
  3538. {
  3539. global $langs,$conf;
  3540. include_once DOL_DOCUMENT_ROOT . '/core/class/interfaces.class.php';
  3541. $interface=new Interfaces($this->db);
  3542. $result=$interface->run_triggers($trigger_name,$this,$user,$langs,$conf);
  3543. if ($result < 0)
  3544. {
  3545. if (!empty($this->errors))
  3546. {
  3547. $this->errors=array_unique(array_merge($this->errors,$interface->errors)); // We use array_unique because when a trigger call another trigger on same object, this->errors is added twice.
  3548. }
  3549. else
  3550. {
  3551. $this->errors=$interface->errors;
  3552. }
  3553. }
  3554. return $result;
  3555. }
  3556. /* Functions for extrafields */
  3557. /**
  3558. * Function to get extra fields of a member into $this->array_options
  3559. * This method is in most cases called by method fetch of objects but you can call it separately.
  3560. *
  3561. * @param int $rowid Id of line
  3562. * @param array $optionsArray Array resulting of call of extrafields->fetch_name_optionals_label()
  3563. * @return int <0 if error, 0 if no optionals to find nor found, 1 if a line is found and optional loaded
  3564. */
  3565. function fetch_optionals($rowid=null,$optionsArray=null)
  3566. {
  3567. if (empty($rowid)) $rowid=$this->id;
  3568. //To avoid SQL errors. Probably not the better solution though
  3569. if (!$this->table_element) {
  3570. return 0;
  3571. }
  3572. if (! is_array($optionsArray))
  3573. {
  3574. // optionsArray not already loaded, so we load it
  3575. require_once DOL_DOCUMENT_ROOT.'/core/class/extrafields.class.php';
  3576. $extrafields = new ExtraFields($this->db);
  3577. $optionsArray = $extrafields->fetch_name_optionals_label($this->table_element);
  3578. }
  3579. // Request to get complementary values
  3580. if (count($optionsArray) > 0)
  3581. {
  3582. $sql = "SELECT rowid";
  3583. foreach ($optionsArray as $name => $label)
  3584. {
  3585. $sql.= ", ".$name;
  3586. }
  3587. $sql.= " FROM ".MAIN_DB_PREFIX.$this->table_element."_extrafields";
  3588. $sql.= " WHERE fk_object = ".$rowid;
  3589. dol_syslog(get_class($this)."::fetch_optionals", LOG_DEBUG);
  3590. $resql=$this->db->query($sql);
  3591. if ($resql)
  3592. {
  3593. $numrows=$this->db->num_rows($resql);
  3594. if ($numrows)
  3595. {
  3596. $tab = $this->db->fetch_array($resql);
  3597. foreach ($tab as $key => $value)
  3598. {
  3599. // Test fetch_array ! is_int($key) because fetch_array result is a mix table with Key as alpha and Key as int (depend db engine)
  3600. if ($key != 'rowid' && $key != 'tms' && $key != 'fk_member' && ! is_int($key))
  3601. {
  3602. // we can add this attribute to object
  3603. $this->array_options["options_".$key]=$value;
  3604. }
  3605. }
  3606. }
  3607. $this->db->free($resql);
  3608. if ($numrows) return $numrows;
  3609. else return 0;
  3610. }
  3611. else
  3612. {
  3613. dol_print_error($this->db);
  3614. return -1;
  3615. }
  3616. }
  3617. return 0;
  3618. }
  3619. /**
  3620. * Delete all extra fields values for the current object.
  3621. *
  3622. * @return int <0 if KO, >0 if OK
  3623. */
  3624. function deleteExtraFields()
  3625. {
  3626. $this->db->begin();
  3627. $sql_del = "DELETE FROM ".MAIN_DB_PREFIX.$this->table_element."_extrafields WHERE fk_object = ".$this->id;
  3628. dol_syslog(get_class($this)."::deleteExtraFields delete", LOG_DEBUG);
  3629. $resql=$this->db->query($sql_del);
  3630. if (! $resql)
  3631. {
  3632. $this->error=$this->db->lasterror();
  3633. $this->db->rollback();
  3634. return -1;
  3635. }
  3636. else
  3637. {
  3638. $this->db->commit();
  3639. return 1;
  3640. }
  3641. }
  3642. /**
  3643. * Add/Update all extra fields values for the current object.
  3644. * Data to describe values to insert/update are stored into $this->array_options=array('options_codeforfield1'=>'valueforfield1', 'options_codeforfield2'=>'valueforfield2', ...)
  3645. * This function delete record with all extrafields and insert them again from the array $this->array_options.
  3646. *
  3647. * @return int -1=error, O=did nothing, 1=OK
  3648. */
  3649. function insertExtraFields()
  3650. {
  3651. global $conf,$langs;
  3652. $error=0;
  3653. if (! empty($conf->global->MAIN_EXTRAFIELDS_DISABLED)) return 0; // For avoid conflicts if trigger used
  3654. if (! empty($this->array_options))
  3655. {
  3656. // Check parameters
  3657. $langs->load('admin');
  3658. require_once DOL_DOCUMENT_ROOT.'/core/class/extrafields.class.php';
  3659. $extrafields = new ExtraFields($this->db);
  3660. $extrafields->fetch_name_optionals_label($this->table_element);
  3661. foreach($this->array_options as $key => $value)
  3662. {
  3663. $attributeKey = substr($key,8); // Remove 'options_' prefix
  3664. $attributeType = $extrafields->attribute_type[$attributeKey];
  3665. $attributeLabel = $extrafields->attribute_label[$attributeKey];
  3666. $attributeParam = $extrafields->attribute_param[$attributeKey];
  3667. switch ($attributeType)
  3668. {
  3669. case 'int':
  3670. if (!is_numeric($value) && $value!='')
  3671. {
  3672. $this->errors[]=$langs->trans("ExtraFieldHasWrongValue",$attributeLabel);
  3673. return -1;
  3674. }
  3675. elseif ($value=='')
  3676. {
  3677. $this->array_options[$key] = null;
  3678. }
  3679. break;
  3680. case 'price':
  3681. $this->array_options[$key] = price2num($this->array_options[$key]);
  3682. break;
  3683. case 'date':
  3684. $this->array_options[$key]=$this->db->idate($this->array_options[$key]);
  3685. break;
  3686. case 'datetime':
  3687. $this->array_options[$key]=$this->db->idate($this->array_options[$key]);
  3688. break;
  3689. case 'link':
  3690. $param_list=array_keys($attributeParam ['options']);
  3691. // 0 : ObjectName
  3692. // 1 : classPath
  3693. $InfoFieldList = explode(":", $param_list[0]);
  3694. dol_include_once($InfoFieldList[1]);
  3695. if ($InfoFieldList[0] && class_exists($InfoFieldList[0]))
  3696. {
  3697. $object = new $InfoFieldList[0]($this->db);
  3698. if ($value)
  3699. {
  3700. $res=$object->fetch(0,$value);
  3701. if ($res > 0) $this->array_options[$key]=$object->id;
  3702. else
  3703. {
  3704. $this->error="Ref '".$value."' for object '".$object->element."' not found";
  3705. $this->db->rollback();
  3706. return -1;
  3707. }
  3708. }
  3709. }
  3710. else
  3711. {
  3712. dol_syslog('Error bad setup of extrafield', LOG_WARNING);
  3713. }
  3714. break;
  3715. }
  3716. }
  3717. $this->db->begin();
  3718. $sql_del = "DELETE FROM ".MAIN_DB_PREFIX.$this->table_element."_extrafields WHERE fk_object = ".$this->id;
  3719. dol_syslog(get_class($this)."::insertExtraFields delete", LOG_DEBUG);
  3720. $this->db->query($sql_del);
  3721. $sql = "INSERT INTO ".MAIN_DB_PREFIX.$this->table_element."_extrafields (fk_object";
  3722. foreach($this->array_options as $key => $value)
  3723. {
  3724. $attributeKey = substr($key,8); // Remove 'options_' prefix
  3725. // Add field of attribut
  3726. if ($extrafields->attribute_type[$attributeKey] != 'separate') // Only for other type of separate
  3727. $sql.=",".$attributeKey;
  3728. }
  3729. $sql .= ") VALUES (".$this->id;
  3730. foreach($this->array_options as $key => $value)
  3731. {
  3732. $attributeKey = substr($key,8); // Remove 'options_' prefix
  3733. // Add field o fattribut
  3734. if($extrafields->attribute_type[$attributeKey] != 'separate') // Only for other type of separate)
  3735. {
  3736. if ($this->array_options[$key] != '')
  3737. {
  3738. $sql.=",'".$this->db->escape($this->array_options[$key])."'";
  3739. }
  3740. else
  3741. {
  3742. $sql.=",null";
  3743. }
  3744. }
  3745. }
  3746. $sql.=")";
  3747. dol_syslog(get_class($this)."::insertExtraFields insert", LOG_DEBUG);
  3748. $resql = $this->db->query($sql);
  3749. if (! $resql)
  3750. {
  3751. $this->error=$this->db->lasterror();
  3752. $this->db->rollback();
  3753. return -1;
  3754. }
  3755. else
  3756. {
  3757. $this->db->commit();
  3758. return 1;
  3759. }
  3760. }
  3761. else return 0;
  3762. }
  3763. /**
  3764. * Update an exta field value for the current object.
  3765. * Data to describe values to insert/update are stored into $this->array_options=array('options_codeforfield1'=>'valueforfield1', 'options_codeforfield2'=>'valueforfield2', ...)
  3766. * This function delte record with all extrafields and insert them again from the array $this->array_options.
  3767. *
  3768. * @param string $key Key of the extrafield
  3769. * @return int -1=error, O=did nothing, 1=OK
  3770. */
  3771. function updateExtraField($key)
  3772. {
  3773. global $conf,$langs;
  3774. $error=0;
  3775. if (! empty($conf->global->MAIN_EXTRAFIELDS_DISABLED)) return 0; // For avoid conflicts if trigger used
  3776. if (! empty($this->array_options) && isset($this->array_options["options_".$key]))
  3777. {
  3778. // Check parameters
  3779. $langs->load('admin');
  3780. require_once DOL_DOCUMENT_ROOT.'/core/class/extrafields.class.php';
  3781. $extrafields = new ExtraFields($this->db);
  3782. $target_extrafields=$extrafields->fetch_name_optionals_label($this->table_element);
  3783. $value=$this->array_options["options_".$key];
  3784. $attributeType = $extrafields->attribute_type[$key];
  3785. $attributeLabel = $extrafields->attribute_label[$key];
  3786. $attributeParam = $extrafields->attribute_param[$key];
  3787. switch ($attributeType)
  3788. {
  3789. case 'int':
  3790. if (!is_numeric($value) && $value!='')
  3791. {
  3792. $this->errors[]=$langs->trans("ExtraFieldHasWrongValue",$attributeLabel);
  3793. return -1;
  3794. }
  3795. elseif ($value=='')
  3796. {
  3797. $this->array_options["options_".$key] = null;
  3798. }
  3799. break;
  3800. case 'price':
  3801. $this->array_options["options_".$key] = price2num($this->array_options["options_".$key]);
  3802. break;
  3803. case 'date':
  3804. $this->array_options["options_".$key]=$this->db->idate($this->array_options["options_".$key]);
  3805. break;
  3806. case 'datetime':
  3807. $this->array_options["options_".$key]=$this->db->idate($this->array_options["options_".$key]);
  3808. break;
  3809. case 'link':
  3810. $param_list=array_keys($attributeParam ['options']);
  3811. // 0 : ObjectName
  3812. // 1 : classPath
  3813. $InfoFieldList = explode(":", $param_list[0]);
  3814. dol_include_once($InfoFieldList[1]);
  3815. $object = new $InfoFieldList[0]($this->db);
  3816. if ($value)
  3817. {
  3818. $object->fetch(0,$value);
  3819. $this->array_options["options_".$key]=$object->id;
  3820. }
  3821. break;
  3822. }
  3823. $this->db->begin();
  3824. $sql = "UPDATE ".MAIN_DB_PREFIX.$this->table_element."_extrafields SET ".$key."='".$this->db->escape($this->array_options["options_".$key])."'";
  3825. $sql .= " WHERE fk_object = ".$this->id;
  3826. $resql = $this->db->query($sql);
  3827. if (! $resql)
  3828. {
  3829. $this->error=$this->db->lasterror();
  3830. $this->db->rollback();
  3831. return -1;
  3832. }
  3833. else
  3834. {
  3835. $this->db->commit();
  3836. return 1;
  3837. }
  3838. }
  3839. else return 0;
  3840. }
  3841. /**
  3842. * Function to show lines of extrafields with output datas
  3843. *
  3844. * @param Extrafields $extrafields Extrafield Object
  3845. * @param string $mode Show output (view) or input (edit) for extrafield
  3846. * @param array $params Optional parameters
  3847. * @param string $keyprefix Prefix string to add into name and id of field (can be used to avoid duplicate names)
  3848. *
  3849. * @return string
  3850. */
  3851. function showOptionals($extrafields, $mode='view', $params=null, $keyprefix='')
  3852. {
  3853. global $_POST, $conf, $langs;
  3854. $out = '';
  3855. if (count($extrafields->attribute_label) > 0)
  3856. {
  3857. $out .= "\n";
  3858. $out .= '<!-- showOptionalsInput --> ';
  3859. $out .= "\n";
  3860. $e = 0;
  3861. foreach($extrafields->attribute_label as $key=>$label)
  3862. {
  3863. if (is_array($params) && count($params)>0) {
  3864. if (array_key_exists('colspan',$params)) {
  3865. $colspan=$params['colspan'];
  3866. }
  3867. }else {
  3868. $colspan='3';
  3869. }
  3870. switch($mode) {
  3871. case "view":
  3872. $value=$this->array_options["options_".$key];
  3873. break;
  3874. case "edit":
  3875. if (isset($_POST["options_" . $key])) {
  3876. if (is_array($_POST["options_" . $key])) {
  3877. // $_POST["options"] is an array but following code expects a comma separated string
  3878. $value = implode(",", $_POST["options_" . $key]);
  3879. } else {
  3880. $value = $_POST["options_" . $key];
  3881. }
  3882. } else {
  3883. $value = $this->array_options["options_" . $key];
  3884. }
  3885. break;
  3886. }
  3887. if ($extrafields->attribute_type[$key] == 'separate')
  3888. {
  3889. $out .= $extrafields->showSeparator($key);
  3890. }
  3891. else
  3892. {
  3893. $csstyle='';
  3894. $class=(!empty($extrafields->attribute_hidden[$key]) ? 'class="hideobject" ' : '');
  3895. if (is_array($params) && count($params)>0) {
  3896. if (array_key_exists('style',$params)) {
  3897. $csstyle=$params['style'];
  3898. }
  3899. }
  3900. if ( !empty($conf->global->MAIN_EXTRAFIELDS_USE_TWO_COLUMS) && ($e % 2) == 0)
  3901. {
  3902. $out .= '<tr '.$class.$csstyle.'>';
  3903. $colspan='0';
  3904. }
  3905. else
  3906. {
  3907. $out .= '<tr '.$class.$csstyle.'>';
  3908. }
  3909. // Convert date into timestamp format
  3910. if (in_array($extrafields->attribute_type[$key],array('date','datetime')))
  3911. {
  3912. $value = isset($_POST["options_".$key])?dol_mktime($_POST["options_".$key."hour"], $_POST["options_".$key."min"], 0, $_POST["options_".$key."month"], $_POST["options_".$key."day"], $_POST["options_".$key."year"]):$this->db->jdate($this->array_options['options_'.$key]);
  3913. }
  3914. if($extrafields->attribute_required[$key])
  3915. $label = '<span'.($mode != 'view' ? ' class="fieldrequired"':'').'>'.$label.'</span>';
  3916. $out .= '<td>'.$langs->trans($label).'</td>';
  3917. $html_id = !empty($this->id) ? $this->element.'_extras_'.$key.'_'.$this->id : '';
  3918. $out .='<td id="'.$html_id.'" class="'.$this->element.'_extras_'.$key.'" '.($colspan?' colspan="'.$colspan.'"':'').'>';
  3919. switch($mode) {
  3920. case "view":
  3921. $out .= $extrafields->showOutputField($key,$value);
  3922. break;
  3923. case "edit":
  3924. $out .= $extrafields->showInputField($key,$value,'',$keyprefix,'',0,$this->id);
  3925. break;
  3926. }
  3927. $out .= '</td>';
  3928. if (! empty($conf->global->MAIN_EXTRAFIELDS_USE_TWO_COLUMS) && (($e % 2) == 1)) $out .= '</tr>';
  3929. else $out .= '</tr>';
  3930. $e++;
  3931. }
  3932. }
  3933. $out .= "\n";
  3934. $out .= '
  3935. <script type="text/javascript">
  3936. jQuery(document).ready(function() {
  3937. function showOptions(child_list, parent_list)
  3938. {
  3939. var val = $("select[name=\"options_"+parent_list+"\"]").val();
  3940. var parentVal = parent_list + ":" + val;
  3941. if(val > 0) {
  3942. $("select[name=\""+child_list+"\"] option[parent]").hide();
  3943. $("select[name=\""+child_list+"\"] option[parent=\""+parentVal+"\"]").show();
  3944. } else {
  3945. $("select[name=\""+child_list+"\"] option").show();
  3946. }
  3947. }
  3948. function setListDependencies() {
  3949. jQuery("select option[parent]").parent().each(function() {
  3950. var child_list = $(this).attr("name");
  3951. var parent = $(this).find("option[parent]:first").attr("parent");
  3952. var infos = parent.split(":");
  3953. var parent_list = infos[0];
  3954. $("select[name=\"options_"+parent_list+"\"]").change(function() {
  3955. showOptions(child_list, parent_list);
  3956. });
  3957. });
  3958. }
  3959. setListDependencies();
  3960. });
  3961. </script>'."\n";
  3962. $out .= '<!-- /showOptionalsInput --> '."\n";
  3963. }
  3964. return $out;
  3965. }
  3966. /**
  3967. * Returns the rights used for this class
  3968. * @return stdClass
  3969. */
  3970. public function getRights()
  3971. {
  3972. global $user;
  3973. $element = $this->element;
  3974. if ($element == 'facturerec') $element='facture';
  3975. return $user->rights->{$element};
  3976. }
  3977. /**
  3978. * Function used to replace a thirdparty id with another one.
  3979. * This function is meant to be called from replaceThirdparty with the appropiate tables
  3980. * Column name fk_soc MUST be used to identify thirdparties
  3981. *
  3982. * @param DoliDB $db Database handler
  3983. * @param int $origin_id Old thirdparty id (the thirdparty to delete)
  3984. * @param int $dest_id New thirdparty id (the thirdparty that will received element of the other)
  3985. * @param string[] $tables Tables that need to be changed
  3986. * @param int $ignoreerrors Ignore errors. Return true even if errors. We need this when replacement can fails like for categories (categorie of old thirdparty may already exists on new one)
  3987. * @return bool
  3988. */
  3989. public static function commonReplaceThirdparty(DoliDB $db, $origin_id, $dest_id, array $tables, $ignoreerrors=0)
  3990. {
  3991. foreach ($tables as $table)
  3992. {
  3993. $sql = 'UPDATE '.MAIN_DB_PREFIX.$table.' SET fk_soc = '.$dest_id.' WHERE fk_soc = '.$origin_id;
  3994. if (! $db->query($sql))
  3995. {
  3996. if ($ignoreerrors) return true; // TODO Not enough. If there is A-B on kept thirdarty and B-C on old one, we must get A-B-C after merge. Not A-B.
  3997. //$this->errors = $db->lasterror();
  3998. return false;
  3999. }
  4000. }
  4001. return true;
  4002. }
  4003. /**
  4004. * Get buy price to use for margin calculation. This function is called when buy price is unknown.
  4005. * set buy price = sell price if ForceBuyingPriceIfNull configured,
  4006. * else if calculation MARGIN_TYPE = 'costprice' and costprice is defined, use costprice as buyprice
  4007. * else if calculation MARGIN_TYPE = 'pmp' and pmp is calculated, use pmp as buyprice
  4008. * else set min buy price as buy price
  4009. *
  4010. * @param float $unitPrice product unit price
  4011. * @param float $discountPercent line discount percent
  4012. * @param int $fk_product product id
  4013. *
  4014. * @return float <0 if ko, buyprice if ok
  4015. */
  4016. public function defineBuyPrice($unitPrice = 0, $discountPercent = 0, $fk_product = 0)
  4017. {
  4018. global $conf;
  4019. $buyPrice = 0;
  4020. if (($unitPrice > 0) && (isset($conf->global->ForceBuyingPriceIfNull) && $conf->global->ForceBuyingPriceIfNull == 1)) // In most cases, test here is false
  4021. {
  4022. $buyPrice = $unitPrice * (1 - $discountPercent / 100);
  4023. }
  4024. else
  4025. {
  4026. // Get cost price for margin calculation
  4027. if (! empty($fk_product))
  4028. {
  4029. if (isset($conf->global->MARGIN_TYPE) && $conf->global->MARGIN_TYPE == 'costprice')
  4030. {
  4031. require_once DOL_DOCUMENT_ROOT.'/product/class/product.class.php';
  4032. $product = new Product($this->db);
  4033. $result = $product->fetch($fk_product);
  4034. if ($result <= 0)
  4035. {
  4036. $this->errors[] = 'ErrorProductIdDoesNotExists';
  4037. return -1;
  4038. }
  4039. if ($product->cost_price > 0)
  4040. {
  4041. $buyPrice = $product->cost_price;
  4042. }
  4043. else if ($product->pmp > 0)
  4044. {
  4045. $buyPrice = $product->pmp;
  4046. }
  4047. }
  4048. else if (isset($conf->global->MARGIN_TYPE) && $conf->global->MARGIN_TYPE == 'pmp')
  4049. {
  4050. require_once DOL_DOCUMENT_ROOT.'/product/class/product.class.php';
  4051. $product = new Product($this->db);
  4052. $result = $product->fetch($fk_product);
  4053. if ($result <= 0)
  4054. {
  4055. $this->errors[] = 'ErrorProductIdDoesNotExists';
  4056. return -1;
  4057. }
  4058. if ($product->pmp > 0)
  4059. {
  4060. $buyPrice = $product->pmp;
  4061. }
  4062. }
  4063. if (empty($buyPrice) && isset($conf->global->MARGIN_TYPE) && in_array($conf->global->MARGIN_TYPE, array('1','pmp','costprice')))
  4064. {
  4065. require_once DOL_DOCUMENT_ROOT.'/fourn/class/fournisseur.product.class.php';
  4066. $productFournisseur = new ProductFournisseur($this->db);
  4067. if (($result = $productFournisseur->find_min_price_product_fournisseur($fk_product)) > 0)
  4068. {
  4069. $buyPrice = $productFournisseur->fourn_unitprice;
  4070. }
  4071. else if ($result < 0)
  4072. {
  4073. $this->errors[] = $productFournisseur->error;
  4074. return -2;
  4075. }
  4076. }
  4077. }
  4078. }
  4079. return $buyPrice;
  4080. }
  4081. }