commonobject.class.php 368 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558155915601561156215631564156515661567156815691570157115721573157415751576157715781579158015811582158315841585158615871588158915901591159215931594159515961597159815991600160116021603160416051606160716081609161016111612161316141615161616171618161916201621162216231624162516261627162816291630163116321633163416351636163716381639164016411642164316441645164616471648164916501651165216531654165516561657165816591660166116621663166416651666166716681669167016711672167316741675167616771678167916801681168216831684168516861687168816891690169116921693169416951696169716981699170017011702170317041705170617071708170917101711171217131714171517161717171817191720172117221723172417251726172717281729173017311732173317341735173617371738173917401741174217431744174517461747174817491750175117521753175417551756175717581759176017611762176317641765176617671768176917701771177217731774177517761777177817791780178117821783178417851786178717881789179017911792179317941795179617971798179918001801180218031804180518061807180818091810181118121813181418151816181718181819182018211822182318241825182618271828182918301831183218331834183518361837183818391840184118421843184418451846184718481849185018511852185318541855185618571858185918601861186218631864186518661867186818691870187118721873187418751876187718781879188018811882188318841885188618871888188918901891189218931894189518961897189818991900190119021903190419051906190719081909191019111912191319141915191619171918191919201921192219231924192519261927192819291930193119321933193419351936193719381939194019411942194319441945194619471948194919501951195219531954195519561957195819591960196119621963196419651966196719681969197019711972197319741975197619771978197919801981198219831984198519861987198819891990199119921993199419951996199719981999200020012002200320042005200620072008200920102011201220132014201520162017201820192020202120222023202420252026202720282029203020312032203320342035203620372038203920402041204220432044204520462047204820492050205120522053205420552056205720582059206020612062206320642065206620672068206920702071207220732074207520762077207820792080208120822083208420852086208720882089209020912092209320942095209620972098209921002101210221032104210521062107210821092110211121122113211421152116211721182119212021212122212321242125212621272128212921302131213221332134213521362137213821392140214121422143214421452146214721482149215021512152215321542155215621572158215921602161216221632164216521662167216821692170217121722173217421752176217721782179218021812182218321842185218621872188218921902191219221932194219521962197219821992200220122022203220422052206220722082209221022112212221322142215221622172218221922202221222222232224222522262227222822292230223122322233223422352236223722382239224022412242224322442245224622472248224922502251225222532254225522562257225822592260226122622263226422652266226722682269227022712272227322742275227622772278227922802281228222832284228522862287228822892290229122922293229422952296229722982299230023012302230323042305230623072308230923102311231223132314231523162317231823192320232123222323232423252326232723282329233023312332233323342335233623372338233923402341234223432344234523462347234823492350235123522353235423552356235723582359236023612362236323642365236623672368236923702371237223732374237523762377237823792380238123822383238423852386238723882389239023912392239323942395239623972398239924002401240224032404240524062407240824092410241124122413241424152416241724182419242024212422242324242425242624272428242924302431243224332434243524362437243824392440244124422443244424452446244724482449245024512452245324542455245624572458245924602461246224632464246524662467246824692470247124722473247424752476247724782479248024812482248324842485248624872488248924902491249224932494249524962497249824992500250125022503250425052506250725082509251025112512251325142515251625172518251925202521252225232524252525262527252825292530253125322533253425352536253725382539254025412542254325442545254625472548254925502551255225532554255525562557255825592560256125622563256425652566256725682569257025712572257325742575257625772578257925802581258225832584258525862587258825892590259125922593259425952596259725982599260026012602260326042605260626072608260926102611261226132614261526162617261826192620262126222623262426252626262726282629263026312632263326342635263626372638263926402641264226432644264526462647264826492650265126522653265426552656265726582659266026612662266326642665266626672668266926702671267226732674267526762677267826792680268126822683268426852686268726882689269026912692269326942695269626972698269927002701270227032704270527062707270827092710271127122713271427152716271727182719272027212722272327242725272627272728272927302731273227332734273527362737273827392740274127422743274427452746274727482749275027512752275327542755275627572758275927602761276227632764276527662767276827692770277127722773277427752776277727782779278027812782278327842785278627872788278927902791279227932794279527962797279827992800280128022803280428052806280728082809281028112812281328142815281628172818281928202821282228232824282528262827282828292830283128322833283428352836283728382839284028412842284328442845284628472848284928502851285228532854285528562857285828592860286128622863286428652866286728682869287028712872287328742875287628772878287928802881288228832884288528862887288828892890289128922893289428952896289728982899290029012902290329042905290629072908290929102911291229132914291529162917291829192920292129222923292429252926292729282929293029312932293329342935293629372938293929402941294229432944294529462947294829492950295129522953295429552956295729582959296029612962296329642965296629672968296929702971297229732974297529762977297829792980298129822983298429852986298729882989299029912992299329942995299629972998299930003001300230033004300530063007300830093010301130123013301430153016301730183019302030213022302330243025302630273028302930303031303230333034303530363037303830393040304130423043304430453046304730483049305030513052305330543055305630573058305930603061306230633064306530663067306830693070307130723073307430753076307730783079308030813082308330843085308630873088308930903091309230933094309530963097309830993100310131023103310431053106310731083109311031113112311331143115311631173118311931203121312231233124312531263127312831293130313131323133313431353136313731383139314031413142314331443145314631473148314931503151315231533154315531563157315831593160316131623163316431653166316731683169317031713172317331743175317631773178317931803181318231833184318531863187318831893190319131923193319431953196319731983199320032013202320332043205320632073208320932103211321232133214321532163217321832193220322132223223322432253226322732283229323032313232323332343235323632373238323932403241324232433244324532463247324832493250325132523253325432553256325732583259326032613262326332643265326632673268326932703271327232733274327532763277327832793280328132823283328432853286328732883289329032913292329332943295329632973298329933003301330233033304330533063307330833093310331133123313331433153316331733183319332033213322332333243325332633273328332933303331333233333334333533363337333833393340334133423343334433453346334733483349335033513352335333543355335633573358335933603361336233633364336533663367336833693370337133723373337433753376337733783379338033813382338333843385338633873388338933903391339233933394339533963397339833993400340134023403340434053406340734083409341034113412341334143415341634173418341934203421342234233424342534263427342834293430343134323433343434353436343734383439344034413442344334443445344634473448344934503451345234533454345534563457345834593460346134623463346434653466346734683469347034713472347334743475347634773478347934803481348234833484348534863487348834893490349134923493349434953496349734983499350035013502350335043505350635073508350935103511351235133514351535163517351835193520352135223523352435253526352735283529353035313532353335343535353635373538353935403541354235433544354535463547354835493550355135523553355435553556355735583559356035613562356335643565356635673568356935703571357235733574357535763577357835793580358135823583358435853586358735883589359035913592359335943595359635973598359936003601360236033604360536063607360836093610361136123613361436153616361736183619362036213622362336243625362636273628362936303631363236333634363536363637363836393640364136423643364436453646364736483649365036513652365336543655365636573658365936603661366236633664366536663667366836693670367136723673367436753676367736783679368036813682368336843685368636873688368936903691369236933694369536963697369836993700370137023703370437053706370737083709371037113712371337143715371637173718371937203721372237233724372537263727372837293730373137323733373437353736373737383739374037413742374337443745374637473748374937503751375237533754375537563757375837593760376137623763376437653766376737683769377037713772377337743775377637773778377937803781378237833784378537863787378837893790379137923793379437953796379737983799380038013802380338043805380638073808380938103811381238133814381538163817381838193820382138223823382438253826382738283829383038313832383338343835383638373838383938403841384238433844384538463847384838493850385138523853385438553856385738583859386038613862386338643865386638673868386938703871387238733874387538763877387838793880388138823883388438853886388738883889389038913892389338943895389638973898389939003901390239033904390539063907390839093910391139123913391439153916391739183919392039213922392339243925392639273928392939303931393239333934393539363937393839393940394139423943394439453946394739483949395039513952395339543955395639573958395939603961396239633964396539663967396839693970397139723973397439753976397739783979398039813982398339843985398639873988398939903991399239933994399539963997399839994000400140024003400440054006400740084009401040114012401340144015401640174018401940204021402240234024402540264027402840294030403140324033403440354036403740384039404040414042404340444045404640474048404940504051405240534054405540564057405840594060406140624063406440654066406740684069407040714072407340744075407640774078407940804081408240834084408540864087408840894090409140924093409440954096409740984099410041014102410341044105410641074108410941104111411241134114411541164117411841194120412141224123412441254126412741284129413041314132413341344135413641374138413941404141414241434144414541464147414841494150415141524153415441554156415741584159416041614162416341644165416641674168416941704171417241734174417541764177417841794180418141824183418441854186418741884189419041914192419341944195419641974198419942004201420242034204420542064207420842094210421142124213421442154216421742184219422042214222422342244225422642274228422942304231423242334234423542364237423842394240424142424243424442454246424742484249425042514252425342544255425642574258425942604261426242634264426542664267426842694270427142724273427442754276427742784279428042814282428342844285428642874288428942904291429242934294429542964297429842994300430143024303430443054306430743084309431043114312431343144315431643174318431943204321432243234324432543264327432843294330433143324333433443354336433743384339434043414342434343444345434643474348434943504351435243534354435543564357435843594360436143624363436443654366436743684369437043714372437343744375437643774378437943804381438243834384438543864387438843894390439143924393439443954396439743984399440044014402440344044405440644074408440944104411441244134414441544164417441844194420442144224423442444254426442744284429443044314432443344344435443644374438443944404441444244434444444544464447444844494450445144524453445444554456445744584459446044614462446344644465446644674468446944704471447244734474447544764477447844794480448144824483448444854486448744884489449044914492449344944495449644974498449945004501450245034504450545064507450845094510451145124513451445154516451745184519452045214522452345244525452645274528452945304531453245334534453545364537453845394540454145424543454445454546454745484549455045514552455345544555455645574558455945604561456245634564456545664567456845694570457145724573457445754576457745784579458045814582458345844585458645874588458945904591459245934594459545964597459845994600460146024603460446054606460746084609461046114612461346144615461646174618461946204621462246234624462546264627462846294630463146324633463446354636463746384639464046414642464346444645464646474648464946504651465246534654465546564657465846594660466146624663466446654666466746684669467046714672467346744675467646774678467946804681468246834684468546864687468846894690469146924693469446954696469746984699470047014702470347044705470647074708470947104711471247134714471547164717471847194720472147224723472447254726472747284729473047314732473347344735473647374738473947404741474247434744474547464747474847494750475147524753475447554756475747584759476047614762476347644765476647674768476947704771477247734774477547764777477847794780478147824783478447854786478747884789479047914792479347944795479647974798479948004801480248034804480548064807480848094810481148124813481448154816481748184819482048214822482348244825482648274828482948304831483248334834483548364837483848394840484148424843484448454846484748484849485048514852485348544855485648574858485948604861486248634864486548664867486848694870487148724873487448754876487748784879488048814882488348844885488648874888488948904891489248934894489548964897489848994900490149024903490449054906490749084909491049114912491349144915491649174918491949204921492249234924492549264927492849294930493149324933493449354936493749384939494049414942494349444945494649474948494949504951495249534954495549564957495849594960496149624963496449654966496749684969497049714972497349744975497649774978497949804981498249834984498549864987498849894990499149924993499449954996499749984999500050015002500350045005500650075008500950105011501250135014501550165017501850195020502150225023502450255026502750285029503050315032503350345035503650375038503950405041504250435044504550465047504850495050505150525053505450555056505750585059506050615062506350645065506650675068506950705071507250735074507550765077507850795080508150825083508450855086508750885089509050915092509350945095509650975098509951005101510251035104510551065107510851095110511151125113511451155116511751185119512051215122512351245125512651275128512951305131513251335134513551365137513851395140514151425143514451455146514751485149515051515152515351545155515651575158515951605161516251635164516551665167516851695170517151725173517451755176517751785179518051815182518351845185518651875188518951905191519251935194519551965197519851995200520152025203520452055206520752085209521052115212521352145215521652175218521952205221522252235224522552265227522852295230523152325233523452355236523752385239524052415242524352445245524652475248524952505251525252535254525552565257525852595260526152625263526452655266526752685269527052715272527352745275527652775278527952805281528252835284528552865287528852895290529152925293529452955296529752985299530053015302530353045305530653075308530953105311531253135314531553165317531853195320532153225323532453255326532753285329533053315332533353345335533653375338533953405341534253435344534553465347534853495350535153525353535453555356535753585359536053615362536353645365536653675368536953705371537253735374537553765377537853795380538153825383538453855386538753885389539053915392539353945395539653975398539954005401540254035404540554065407540854095410541154125413541454155416541754185419542054215422542354245425542654275428542954305431543254335434543554365437543854395440544154425443544454455446544754485449545054515452545354545455545654575458545954605461546254635464546554665467546854695470547154725473547454755476547754785479548054815482548354845485548654875488548954905491549254935494549554965497549854995500550155025503550455055506550755085509551055115512551355145515551655175518551955205521552255235524552555265527552855295530553155325533553455355536553755385539554055415542554355445545554655475548554955505551555255535554555555565557555855595560556155625563556455655566556755685569557055715572557355745575557655775578557955805581558255835584558555865587558855895590559155925593559455955596559755985599560056015602560356045605560656075608560956105611561256135614561556165617561856195620562156225623562456255626562756285629563056315632563356345635563656375638563956405641564256435644564556465647564856495650565156525653565456555656565756585659566056615662566356645665566656675668566956705671567256735674567556765677567856795680568156825683568456855686568756885689569056915692569356945695569656975698569957005701570257035704570557065707570857095710571157125713571457155716571757185719572057215722572357245725572657275728572957305731573257335734573557365737573857395740574157425743574457455746574757485749575057515752575357545755575657575758575957605761576257635764576557665767576857695770577157725773577457755776577757785779578057815782578357845785578657875788578957905791579257935794579557965797579857995800580158025803580458055806580758085809581058115812581358145815581658175818581958205821582258235824582558265827582858295830583158325833583458355836583758385839584058415842584358445845584658475848584958505851585258535854585558565857585858595860586158625863586458655866586758685869587058715872587358745875587658775878587958805881588258835884588558865887588858895890589158925893589458955896589758985899590059015902590359045905590659075908590959105911591259135914591559165917591859195920592159225923592459255926592759285929593059315932593359345935593659375938593959405941594259435944594559465947594859495950595159525953595459555956595759585959596059615962596359645965596659675968596959705971597259735974597559765977597859795980598159825983598459855986598759885989599059915992599359945995599659975998599960006001600260036004600560066007600860096010601160126013601460156016601760186019602060216022602360246025602660276028602960306031603260336034603560366037603860396040604160426043604460456046604760486049605060516052605360546055605660576058605960606061606260636064606560666067606860696070607160726073607460756076607760786079608060816082608360846085608660876088608960906091609260936094609560966097609860996100610161026103610461056106610761086109611061116112611361146115611661176118611961206121612261236124612561266127612861296130613161326133613461356136613761386139614061416142614361446145614661476148614961506151615261536154615561566157615861596160616161626163616461656166616761686169617061716172617361746175617661776178617961806181618261836184618561866187618861896190619161926193619461956196619761986199620062016202620362046205620662076208620962106211621262136214621562166217621862196220622162226223622462256226622762286229623062316232623362346235623662376238623962406241624262436244624562466247624862496250625162526253625462556256625762586259626062616262626362646265626662676268626962706271627262736274627562766277627862796280628162826283628462856286628762886289629062916292629362946295629662976298629963006301630263036304630563066307630863096310631163126313631463156316631763186319632063216322632363246325632663276328632963306331633263336334633563366337633863396340634163426343634463456346634763486349635063516352635363546355635663576358635963606361636263636364636563666367636863696370637163726373637463756376637763786379638063816382638363846385638663876388638963906391639263936394639563966397639863996400640164026403640464056406640764086409641064116412641364146415641664176418641964206421642264236424642564266427642864296430643164326433643464356436643764386439644064416442644364446445644664476448644964506451645264536454645564566457645864596460646164626463646464656466646764686469647064716472647364746475647664776478647964806481648264836484648564866487648864896490649164926493649464956496649764986499650065016502650365046505650665076508650965106511651265136514651565166517651865196520652165226523652465256526652765286529653065316532653365346535653665376538653965406541654265436544654565466547654865496550655165526553655465556556655765586559656065616562656365646565656665676568656965706571657265736574657565766577657865796580658165826583658465856586658765886589659065916592659365946595659665976598659966006601660266036604660566066607660866096610661166126613661466156616661766186619662066216622662366246625662666276628662966306631663266336634663566366637663866396640664166426643664466456646664766486649665066516652665366546655665666576658665966606661666266636664666566666667666866696670667166726673667466756676667766786679668066816682668366846685668666876688668966906691669266936694669566966697669866996700670167026703670467056706670767086709671067116712671367146715671667176718671967206721672267236724672567266727672867296730673167326733673467356736673767386739674067416742674367446745674667476748674967506751675267536754675567566757675867596760676167626763676467656766676767686769677067716772677367746775677667776778677967806781678267836784678567866787678867896790679167926793679467956796679767986799680068016802680368046805680668076808680968106811681268136814681568166817681868196820682168226823682468256826682768286829683068316832683368346835683668376838683968406841684268436844684568466847684868496850685168526853685468556856685768586859686068616862686368646865686668676868686968706871687268736874687568766877687868796880688168826883688468856886688768886889689068916892689368946895689668976898689969006901690269036904690569066907690869096910691169126913691469156916691769186919692069216922692369246925692669276928692969306931693269336934693569366937693869396940694169426943694469456946694769486949695069516952695369546955695669576958695969606961696269636964696569666967696869696970697169726973697469756976697769786979698069816982698369846985698669876988698969906991699269936994699569966997699869997000700170027003700470057006700770087009701070117012701370147015701670177018701970207021702270237024702570267027702870297030703170327033703470357036703770387039704070417042704370447045704670477048704970507051705270537054705570567057705870597060706170627063706470657066706770687069707070717072707370747075707670777078707970807081708270837084708570867087708870897090709170927093709470957096709770987099710071017102710371047105710671077108710971107111711271137114711571167117711871197120712171227123712471257126712771287129713071317132713371347135713671377138713971407141714271437144714571467147714871497150715171527153715471557156715771587159716071617162716371647165716671677168716971707171717271737174717571767177717871797180718171827183718471857186718771887189719071917192719371947195719671977198719972007201720272037204720572067207720872097210721172127213721472157216721772187219722072217222722372247225722672277228722972307231723272337234723572367237723872397240724172427243724472457246724772487249725072517252725372547255725672577258725972607261726272637264726572667267726872697270727172727273727472757276727772787279728072817282728372847285728672877288728972907291729272937294729572967297729872997300730173027303730473057306730773087309731073117312731373147315731673177318731973207321732273237324732573267327732873297330733173327333733473357336733773387339734073417342734373447345734673477348734973507351735273537354735573567357735873597360736173627363736473657366736773687369737073717372737373747375737673777378737973807381738273837384738573867387738873897390739173927393739473957396739773987399740074017402740374047405740674077408740974107411741274137414741574167417741874197420742174227423742474257426742774287429743074317432743374347435743674377438743974407441744274437444744574467447744874497450745174527453745474557456745774587459746074617462746374647465746674677468746974707471747274737474747574767477747874797480748174827483748474857486748774887489749074917492749374947495749674977498749975007501750275037504750575067507750875097510751175127513751475157516751775187519752075217522752375247525752675277528752975307531753275337534753575367537753875397540754175427543754475457546754775487549755075517552755375547555755675577558755975607561756275637564756575667567756875697570757175727573757475757576757775787579758075817582758375847585758675877588758975907591759275937594759575967597759875997600760176027603760476057606760776087609761076117612761376147615761676177618761976207621762276237624762576267627762876297630763176327633763476357636763776387639764076417642764376447645764676477648764976507651765276537654765576567657765876597660766176627663766476657666766776687669767076717672767376747675767676777678767976807681768276837684768576867687768876897690769176927693769476957696769776987699770077017702770377047705770677077708770977107711771277137714771577167717771877197720772177227723772477257726772777287729773077317732773377347735773677377738773977407741774277437744774577467747774877497750775177527753775477557756775777587759776077617762776377647765776677677768776977707771777277737774777577767777777877797780778177827783778477857786778777887789779077917792779377947795779677977798779978007801780278037804780578067807780878097810781178127813781478157816781778187819782078217822782378247825782678277828782978307831783278337834783578367837783878397840784178427843784478457846784778487849785078517852785378547855785678577858785978607861786278637864786578667867786878697870787178727873787478757876787778787879788078817882788378847885788678877888788978907891789278937894789578967897789878997900790179027903790479057906790779087909791079117912791379147915791679177918791979207921792279237924792579267927792879297930793179327933793479357936793779387939794079417942794379447945794679477948794979507951795279537954795579567957795879597960796179627963796479657966796779687969797079717972797379747975797679777978797979807981798279837984798579867987798879897990799179927993799479957996799779987999800080018002800380048005800680078008800980108011801280138014801580168017801880198020802180228023802480258026802780288029803080318032803380348035803680378038803980408041804280438044804580468047804880498050805180528053805480558056805780588059806080618062806380648065806680678068806980708071807280738074807580768077807880798080808180828083808480858086808780888089809080918092809380948095809680978098809981008101810281038104810581068107810881098110811181128113811481158116811781188119812081218122812381248125812681278128812981308131813281338134813581368137813881398140814181428143814481458146814781488149815081518152815381548155815681578158815981608161816281638164816581668167816881698170817181728173817481758176817781788179818081818182818381848185818681878188818981908191819281938194819581968197819881998200820182028203820482058206820782088209821082118212821382148215821682178218821982208221822282238224822582268227822882298230823182328233823482358236823782388239824082418242824382448245824682478248824982508251825282538254825582568257825882598260826182628263826482658266826782688269827082718272827382748275827682778278827982808281828282838284828582868287828882898290829182928293829482958296829782988299830083018302830383048305830683078308830983108311831283138314831583168317831883198320832183228323832483258326832783288329833083318332833383348335833683378338833983408341834283438344834583468347834883498350835183528353835483558356835783588359836083618362836383648365836683678368836983708371837283738374837583768377837883798380838183828383838483858386838783888389839083918392839383948395839683978398839984008401840284038404840584068407840884098410841184128413841484158416841784188419842084218422842384248425842684278428842984308431843284338434843584368437843884398440844184428443844484458446844784488449845084518452845384548455845684578458845984608461846284638464846584668467846884698470847184728473847484758476847784788479848084818482848384848485848684878488848984908491849284938494849584968497849884998500850185028503850485058506850785088509851085118512851385148515851685178518851985208521852285238524852585268527852885298530853185328533853485358536853785388539854085418542854385448545854685478548854985508551855285538554855585568557855885598560856185628563856485658566856785688569857085718572857385748575857685778578857985808581858285838584858585868587858885898590859185928593859485958596859785988599860086018602860386048605860686078608860986108611861286138614861586168617861886198620862186228623862486258626862786288629863086318632863386348635863686378638863986408641864286438644864586468647864886498650865186528653865486558656865786588659866086618662866386648665866686678668866986708671867286738674867586768677867886798680868186828683868486858686868786888689869086918692869386948695869686978698869987008701870287038704870587068707870887098710871187128713871487158716871787188719872087218722872387248725872687278728872987308731873287338734873587368737873887398740874187428743874487458746874787488749875087518752875387548755875687578758875987608761876287638764876587668767876887698770877187728773877487758776877787788779878087818782878387848785878687878788878987908791879287938794879587968797879887998800880188028803880488058806880788088809881088118812881388148815881688178818881988208821882288238824882588268827882888298830883188328833883488358836883788388839884088418842884388448845884688478848884988508851885288538854885588568857885888598860886188628863886488658866886788688869887088718872887388748875887688778878887988808881888288838884888588868887888888898890889188928893889488958896889788988899890089018902890389048905890689078908890989108911891289138914891589168917891889198920892189228923892489258926892789288929893089318932893389348935893689378938893989408941894289438944894589468947894889498950895189528953895489558956895789588959896089618962896389648965896689678968896989708971897289738974897589768977897889798980898189828983898489858986898789888989899089918992899389948995899689978998899990009001900290039004900590069007900890099010901190129013901490159016901790189019902090219022902390249025902690279028902990309031903290339034903590369037903890399040904190429043904490459046904790489049905090519052905390549055905690579058905990609061906290639064906590669067906890699070907190729073907490759076907790789079908090819082908390849085908690879088908990909091909290939094909590969097909890999100910191029103910491059106910791089109911091119112911391149115911691179118911991209121912291239124912591269127912891299130913191329133913491359136913791389139914091419142914391449145914691479148914991509151915291539154915591569157915891599160916191629163916491659166916791689169917091719172917391749175917691779178917991809181918291839184918591869187918891899190919191929193919491959196919791989199920092019202920392049205920692079208920992109211921292139214921592169217921892199220922192229223922492259226922792289229923092319232923392349235923692379238923992409241924292439244924592469247924892499250925192529253925492559256925792589259926092619262926392649265926692679268926992709271927292739274927592769277927892799280928192829283928492859286928792889289929092919292929392949295929692979298929993009301930293039304930593069307930893099310931193129313931493159316931793189319932093219322932393249325932693279328932993309331933293339334933593369337933893399340934193429343934493459346934793489349935093519352935393549355935693579358935993609361936293639364936593669367936893699370937193729373937493759376937793789379938093819382938393849385938693879388938993909391939293939394939593969397939893999400940194029403940494059406940794089409941094119412941394149415941694179418941994209421942294239424942594269427942894299430943194329433943494359436943794389439944094419442944394449445944694479448944994509451945294539454945594569457945894599460946194629463946494659466946794689469947094719472947394749475947694779478947994809481948294839484948594869487948894899490949194929493949494959496949794989499950095019502950395049505950695079508950995109511951295139514951595169517951895199520952195229523952495259526952795289529953095319532953395349535953695379538953995409541954295439544954595469547954895499550955195529553955495559556955795589559956095619562956395649565956695679568956995709571957295739574957595769577957895799580958195829583958495859586958795889589959095919592959395949595959695979598959996009601960296039604960596069607960896099610961196129613961496159616961796189619962096219622962396249625962696279628962996309631963296339634963596369637963896399640964196429643964496459646964796489649965096519652965396549655965696579658965996609661966296639664966596669667966896699670967196729673967496759676967796789679968096819682968396849685968696879688968996909691969296939694969596969697969896999700970197029703970497059706970797089709971097119712971397149715971697179718971997209721972297239724972597269727972897299730973197329733973497359736973797389739974097419742974397449745974697479748974997509751975297539754975597569757975897599760976197629763976497659766976797689769977097719772977397749775977697779778977997809781978297839784978597869787978897899790979197929793979497959796979797989799980098019802980398049805980698079808980998109811981298139814981598169817981898199820982198229823982498259826982798289829983098319832983398349835983698379838983998409841984298439844984598469847984898499850985198529853985498559856985798589859986098619862986398649865986698679868986998709871987298739874987598769877987898799880988198829883988498859886988798889889989098919892989398949895989698979898989999009901990299039904990599069907990899099910991199129913991499159916991799189919992099219922992399249925992699279928992999309931993299339934993599369937993899399940994199429943994499459946994799489949995099519952995399549955995699579958995999609961996299639964996599669967996899699970997199729973997499759976997799789979998099819982998399849985998699879988998999909991999299939994999599969997999899991000010001100021000310004100051000610007100081000910010100111001210013100141001510016100171001810019100201002110022100231002410025100261002710028100291003010031100321003310034100351003610037100381003910040100411004210043100441004510046100471004810049100501005110052100531005410055100561005710058100591006010061100621006310064100651006610067100681006910070100711007210073100741007510076100771007810079100801008110082100831008410085100861008710088100891009010091100921009310094100951009610097100981009910100101011010210103101041010510106101071010810109101101011110112101131011410115101161011710118101191012010121101221012310124101251012610127101281012910130101311013210133101341013510136101371013810139101401014110142101431014410145101461014710148101491015010151101521015310154101551015610157101581015910160101611016210163101641016510166101671016810169101701017110172101731017410175101761017710178101791018010181101821018310184101851018610187101881018910190101911019210193101941019510196101971019810199102001020110202102031020410205102061020710208102091021010211102121021310214102151021610217102181021910220102211022210223102241022510226102271022810229102301023110232102331023410235102361023710238102391024010241102421024310244102451024610247102481024910250102511025210253102541025510256102571025810259102601026110262102631026410265102661026710268102691027010271102721027310274102751027610277102781027910280102811028210283102841028510286102871028810289102901029110292102931029410295102961029710298102991030010301103021030310304103051030610307103081030910310103111031210313103141031510316103171031810319103201032110322103231032410325103261032710328103291033010331103321033310334103351033610337103381033910340103411034210343103441034510346103471034810349103501035110352103531035410355103561035710358103591036010361103621036310364103651036610367103681036910370103711037210373103741037510376103771037810379103801038110382103831038410385103861038710388103891039010391103921039310394103951039610397103981039910400104011040210403104041040510406104071040810409104101041110412104131041410415104161041710418104191042010421104221042310424104251042610427104281042910430104311043210433104341043510436104371043810439104401044110442104431044410445104461044710448104491045010451104521045310454104551045610457104581045910460104611046210463104641046510466104671046810469104701047110472104731047410475104761047710478104791048010481104821048310484104851048610487104881048910490104911049210493104941049510496104971049810499105001050110502105031050410505105061050710508105091051010511105121051310514105151051610517105181051910520105211052210523105241052510526105271052810529105301053110532105331053410535105361053710538105391054010541105421054310544105451054610547105481054910550105511055210553105541055510556105571055810559105601056110562105631056410565105661056710568105691057010571105721057310574105751057610577105781057910580105811058210583105841058510586105871058810589105901059110592105931059410595105961059710598105991060010601106021060310604106051060610607106081060910610106111061210613106141061510616106171061810619106201062110622106231062410625106261062710628106291063010631106321063310634106351063610637106381063910640106411064210643106441064510646106471064810649106501065110652106531065410655106561065710658106591066010661
  1. <?php
  2. /* Copyright (C) 2006-2015 Laurent Destailleur <eldy@users.sourceforge.net>
  3. * Copyright (C) 2005-2013 Regis Houssin <regis.houssin@inodbox.com>
  4. * Copyright (C) 2010-2020 Juanjo Menent <jmenent@2byte.es>
  5. * Copyright (C) 2012-2013 Christophe Battarel <christophe.battarel@altairis.fr>
  6. * Copyright (C) 2011-2022 Philippe Grand <philippe.grand@atoo-net.com>
  7. * Copyright (C) 2012-2015 Marcos García <marcosgdf@gmail.com>
  8. * Copyright (C) 2012-2015 Raphaël Doursenaud <rdoursenaud@gpcsolutions.fr>
  9. * Copyright (C) 2012 Cedric Salvador <csalvador@gpcsolutions.fr>
  10. * Copyright (C) 2015-2022 Alexandre Spangaro <aspangaro@open-dsi.fr>
  11. * Copyright (C) 2016 Bahfir abbes <dolipar@dolipar.org>
  12. * Copyright (C) 2017 ATM Consulting <support@atm-consulting.fr>
  13. * Copyright (C) 2017-2019 Nicolas ZABOURI <info@inovea-conseil.com>
  14. * Copyright (C) 2017 Rui Strecht <rui.strecht@aliartalentos.com>
  15. * Copyright (C) 2018-2023 Frédéric France <frederic.france@netlogic.fr>
  16. * Copyright (C) 2018 Josep Lluís Amador <joseplluis@lliuretic.cat>
  17. * Copyright (C) 2023 Gauthier VERDOL <gauthier.verdol@atm-consulting.fr>
  18. * Copyright (C) 2021 Grégory Blémand <gregory.blemand@atm-consulting.fr>
  19. * Copyright (C) 2023 Lenin Rivas <lenin.rivas777@gmail.com>
  20. *
  21. * This program is free software; you can redistribute it and/or modify
  22. * it under the terms of the GNU General Public License as published by
  23. * the Free Software Foundation; either version 3 of the License, or
  24. * (at your option) any later version.
  25. *
  26. * This program is distributed in the hope that it will be useful,
  27. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  28. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  29. * GNU General Public License for more details.
  30. *
  31. * You should have received a copy of the GNU General Public License
  32. * along with this program. If not, see <https://www.gnu.org/licenses/>.
  33. */
  34. /**
  35. * \file htdocs/core/class/commonobject.class.php
  36. * \ingroup core
  37. * \brief File of parent class of all other business classes (invoices, contracts, proposals, orders, ...)
  38. */
  39. /**
  40. * Parent class of all other business classes (invoices, contracts, proposals, orders, ...)
  41. */
  42. abstract class CommonObject
  43. {
  44. const TRIGGER_PREFIX = ''; // to be overriden in child class implementations, i.e. 'BILL', 'TASK', 'PROPAL', etc.
  45. /**
  46. * @var string ID of module.
  47. */
  48. public $module;
  49. /**
  50. * @var DoliDb Database handler (result of a new DoliDB)
  51. */
  52. public $db;
  53. /**
  54. * @var int The object identifier
  55. */
  56. public $id;
  57. /**
  58. * @var int The environment ID when using a multicompany module
  59. */
  60. public $entity;
  61. /**
  62. * @var string Error string
  63. * @see $errors
  64. */
  65. public $error;
  66. /**
  67. * @var string Error string that is hidden but can be used to store complementatry technical code.
  68. */
  69. public $errorhidden;
  70. /**
  71. * @var string[] Array of error strings
  72. */
  73. public $errors = array();
  74. /**
  75. * @var array To store error results of ->validateField()
  76. */
  77. private $validateFieldsErrors = array();
  78. /**
  79. * @var string ID to identify managed object
  80. */
  81. public $element;
  82. /**
  83. * @var string Fieldname with ID of parent key if this field has a parent
  84. */
  85. public $fk_element;
  86. /**
  87. * @var string Name to use for 'features' parameter to check module permissions user->rights->feature with restrictedArea().
  88. * Undefined means same value than $element. Can be use to force a check on another element for example for class of line, we mention here the parent element.
  89. */
  90. public $element_for_permission;
  91. /**
  92. * @var string Name of table without prefix where object is stored
  93. */
  94. public $table_element;
  95. /**
  96. * @var string Name of subtable line
  97. */
  98. public $table_element_line = '';
  99. /**
  100. * @var int 0=No test on entity, 1=Test with field entity, 'field@table'=Test with link by field@table
  101. */
  102. public $ismultientitymanaged;
  103. /**
  104. * @var string Key value used to track if data is coming from import wizard
  105. */
  106. public $import_key;
  107. /**
  108. * @var mixed Contains data to manage extrafields
  109. */
  110. public $array_options = array();
  111. /**
  112. * @var array Array with all fields and their property. Do not use it as a static var. It may be modified by constructor.
  113. */
  114. public $fields = array();
  115. /**
  116. * @var mixed Array to store alternative languages values of object
  117. */
  118. public $array_languages = null; // Value is array() when load already tried
  119. /**
  120. * @var array To store result of ->liste_contact()
  121. */
  122. public $contacts_ids;
  123. /**
  124. * @var mixed Array of linked objects, set and used when calling ->create() to be able to create links during the creation of object
  125. */
  126. public $linked_objects;
  127. /**
  128. * @var int[][] Array of linked objects ids. Loaded by ->fetchObjectLinked
  129. */
  130. public $linkedObjectsIds;
  131. /**
  132. * @var mixed Array of linked objects. Loaded by ->fetchObjectLinked
  133. */
  134. public $linkedObjects;
  135. /**
  136. * @var boolean[] Array of boolean with object id as key and value as true if linkedObjects full loaded for object id. Loaded by ->fetchObjectLinked. Important for pdf generation time reduction.
  137. */
  138. private $linkedObjectsFullLoaded = array();
  139. /**
  140. * @var CommonObject To store a cloned copy of object before to edit it and keep track of old properties
  141. */
  142. public $oldcopy;
  143. /**
  144. * @var string To store old value of a modified ref
  145. */
  146. public $oldref;
  147. /**
  148. * @var string Column name of the ref field.
  149. */
  150. protected $table_ref_field = '';
  151. /**
  152. * @var integer 0=Default, 1=View may be restricted to sales representative only if no permission to see all or to company of external user if external user
  153. */
  154. public $restrictiononfksoc = 0;
  155. // Following vars are used by some objects only. We keep this property here in CommonObject to be able to provide common method using them.
  156. /**
  157. * @var array<string,mixed> Can be used to pass information when only object is provided to method
  158. */
  159. public $context = array();
  160. // Properties set and used by Agenda trigger
  161. public $actionmsg;
  162. public $actionmsg2;
  163. /**
  164. * @var string Contains canvas name if record is an alternative canvas record
  165. */
  166. public $canvas;
  167. /**
  168. * @var Project The related project object
  169. * @see fetch_projet()
  170. */
  171. public $project;
  172. /**
  173. * @var int The related project ID
  174. * @see setProject(), project
  175. */
  176. public $fk_project;
  177. /**
  178. * @var Project The related project object
  179. * @deprecated
  180. * @see project
  181. */
  182. public $projet;
  183. /**
  184. * @deprecated
  185. * @see $fk_project
  186. */
  187. public $fk_projet;
  188. /**
  189. * @var Contact A related contact object
  190. * @see fetch_contact()
  191. */
  192. public $contact;
  193. /**
  194. * @var int The related contact ID
  195. * @see fetch_contact()
  196. */
  197. public $contact_id;
  198. /**
  199. * @var Societe A related thirdparty object
  200. * @see fetch_thirdparty()
  201. */
  202. public $thirdparty;
  203. /**
  204. * @var User A related user
  205. * @see fetch_user()
  206. */
  207. public $user;
  208. /**
  209. * @var string The type of originating object ('commande', 'facture', ...). Note: on some object this field is called $origin_type
  210. * @see fetch_origin()
  211. */
  212. public $origin;
  213. /**
  214. * @var int The id of originating object
  215. * @see fetch_origin()
  216. */
  217. public $origin_id;
  218. /**
  219. * @var Object Origin object. This is set by fetch_origin() from this->origin and this->origin_id.
  220. */
  221. public $origin_object;
  222. // TODO Remove this. Has been replaced with ->origin_object.
  223. // This is set by fetch_origin() from this->origin and this->origin_id
  224. /** @deprecated */
  225. public $expedition;
  226. /** @deprecated */
  227. public $livraison;
  228. /** @deprecated */
  229. public $commandeFournisseur;
  230. /**
  231. * @var string The object's reference
  232. */
  233. public $ref;
  234. /**
  235. * @var string An external reference for the object
  236. */
  237. public $ref_ext;
  238. /**
  239. * @var string The object's previous reference
  240. */
  241. public $ref_previous;
  242. /**
  243. * @var string The object's next reference
  244. */
  245. public $ref_next;
  246. /**
  247. * @var string Ref to store on object to save the new ref to use for example when making a validate() of an object
  248. */
  249. public $newref;
  250. /**
  251. * @var int The object's status. Prefer use of status.
  252. * @deprecated
  253. * @see setStatut()
  254. */
  255. public $statut;
  256. /**
  257. * @var int The object's status
  258. * @see setStatut()
  259. */
  260. public $status;
  261. /**
  262. * @var string
  263. * @see getFullAddress()
  264. */
  265. public $country;
  266. /**
  267. * @var int
  268. * @see getFullAddress(), country
  269. */
  270. public $country_id;
  271. /**
  272. * @var string The ISO country code on 2 chars.
  273. * @see getFullAddress(), isInEEC(), country
  274. */
  275. public $country_code;
  276. /**
  277. * @var string
  278. * @see getFullAddress()
  279. */
  280. public $state;
  281. /**
  282. * @var int
  283. * @see getFullAddress(), state
  284. */
  285. public $state_id;
  286. /**
  287. * @var string
  288. * @see getFullAddress(), $state
  289. */
  290. public $state_code;
  291. /**
  292. * @var int
  293. * @see getFullAddress(), $region_code, $region
  294. */
  295. public $region_id;
  296. /**
  297. * @var string
  298. * @see getFullAddress(), $region_id, $region
  299. */
  300. public $region_code;
  301. /**
  302. * @var string
  303. * @see getFullAddress(), $region_id, $region_code
  304. */
  305. public $region;
  306. /**
  307. * @var int
  308. * @see fetch_barcode()
  309. */
  310. public $barcode_type;
  311. /**
  312. * @var string
  313. * @see fetch_barcode(), barcode_type
  314. */
  315. public $barcode_type_code;
  316. /**
  317. * @var string
  318. * @see fetch_barcode(), barcode_type
  319. */
  320. public $barcode_type_label;
  321. /**
  322. * @var string
  323. * @see fetch_barcode(), barcode_type
  324. */
  325. public $barcode_type_coder;
  326. /**
  327. * @var int Payment method ID (cheque, cash, ...)
  328. * @see setPaymentMethods()
  329. */
  330. public $mode_reglement_id;
  331. /**
  332. * @var int Payment terms ID
  333. * @see setPaymentTerms()
  334. */
  335. public $cond_reglement_id;
  336. /**
  337. * @var int Demand reason ID
  338. */
  339. public $demand_reason_id;
  340. /**
  341. * @var int Transport mode ID (For module intracomm report)
  342. * @see setTransportMode()
  343. */
  344. public $transport_mode_id;
  345. /**
  346. * @var int Payment terms ID
  347. * @deprecated Kept for compatibility
  348. * @see cond_reglement_id;
  349. */
  350. public $cond_reglement;
  351. /**
  352. * @var int Delivery address ID
  353. * @see setDeliveryAddress()
  354. * @deprecated
  355. */
  356. public $fk_delivery_address;
  357. /**
  358. * @var int Shipping method ID
  359. * @see setShippingMethod()
  360. */
  361. public $shipping_method_id;
  362. /**
  363. * @var string Shipping method label
  364. * @see setShippingMethod()
  365. */
  366. public $shipping_method;
  367. // Multicurrency
  368. /**
  369. * @var int ID
  370. */
  371. public $fk_multicurrency;
  372. /**
  373. * @var string Multicurrency code
  374. */
  375. public $multicurrency_code;
  376. /**
  377. * @var float Multicurrency rate
  378. */
  379. public $multicurrency_tx;
  380. /**
  381. * @var float Multicurrency total without tax
  382. */
  383. public $multicurrency_total_ht;
  384. /**
  385. * @var float Multicurrency total vat
  386. */
  387. public $multicurrency_total_tva;
  388. /**
  389. * @var float Multicurrency total with tax
  390. */
  391. public $multicurrency_total_ttc;
  392. /**
  393. * @var string
  394. * @see SetDocModel()
  395. */
  396. public $model_pdf;
  397. /**
  398. * @var string
  399. * @deprecated
  400. * @see $model_pdf
  401. */
  402. public $modelpdf;
  403. /**
  404. * @var string
  405. * Contains relative path of last generated main file
  406. */
  407. public $last_main_doc;
  408. /**
  409. * @var int Bank account ID sometimes, ID of record into llx_bank sometimes
  410. * @deprecated
  411. * @see $fk_account
  412. */
  413. public $fk_bank;
  414. /**
  415. * @var int Bank account ID
  416. * @see SetBankAccount()
  417. */
  418. public $fk_account;
  419. /**
  420. * @var string Public note
  421. * @see update_note()
  422. */
  423. public $note_public;
  424. /**
  425. * @var string Private note
  426. * @see update_note()
  427. */
  428. public $note_private;
  429. /**
  430. * @deprecated
  431. * @see $note_private
  432. */
  433. public $note;
  434. /**
  435. * @var float Total amount before taxes
  436. * @see update_price()
  437. */
  438. public $total_ht;
  439. /**
  440. * @var float Total VAT amount
  441. * @see update_price()
  442. */
  443. public $total_tva;
  444. /**
  445. * @var float Total local tax 1 amount
  446. * @see update_price()
  447. */
  448. public $total_localtax1;
  449. /**
  450. * @var float Total local tax 2 amount
  451. * @see update_price()
  452. */
  453. public $total_localtax2;
  454. /**
  455. * @var float Total amount with taxes
  456. * @see update_price()
  457. */
  458. public $total_ttc;
  459. /**
  460. * @var CommonObjectLine[]
  461. */
  462. public $lines;
  463. /**
  464. * @var mixed Contains comments
  465. * @see fetchComments()
  466. */
  467. public $comments = array();
  468. /**
  469. * @var string The name
  470. */
  471. public $name;
  472. /**
  473. * @var string The lastname
  474. */
  475. public $lastname;
  476. /**
  477. * @var string The firstname
  478. */
  479. public $firstname;
  480. /**
  481. * @var string The civility code, not an integer
  482. */
  483. public $civility_id;
  484. // Dates
  485. /**
  486. * @var integer|string date_creation
  487. */
  488. public $date_creation;
  489. /**
  490. * @var integer|string $date_validation;
  491. */
  492. public $date_validation; // Date validation
  493. /**
  494. * @var integer|string $date_modification;
  495. */
  496. public $date_modification; // Date last change (tms field)
  497. /**
  498. * @var integer|string $date_modification;
  499. * @deprecated Use date_modification
  500. */
  501. public $date_update;
  502. /**
  503. * @var integer|string $date_cloture;
  504. */
  505. public $date_cloture; // Date closing (tms field)
  506. /**
  507. * @var User User author/creation
  508. * @deprecated Store only id in user_creation_id
  509. */
  510. public $user_author;
  511. /**
  512. * @var User User author/creation
  513. * @deprecated
  514. */
  515. public $user_creation;
  516. /**
  517. * @var int User id author/creation
  518. */
  519. public $user_creation_id;
  520. /**
  521. * @var User User of validation
  522. * @deprecated
  523. */
  524. public $user_valid;
  525. /**
  526. * @var User User of validation
  527. * @deprecated
  528. */
  529. public $user_validation;
  530. /**
  531. * @var int User id of validation
  532. */
  533. public $user_validation_id;
  534. /**
  535. * @var int User id closing object
  536. */
  537. public $user_closing_id;
  538. /**
  539. * @var User User last modifier
  540. * @deprecated
  541. */
  542. public $user_modification;
  543. /**
  544. * @var int User id last modifier
  545. */
  546. public $user_modification_id;
  547. public $next_prev_filter;
  548. /**
  549. * @var int 1 if object is specimen
  550. */
  551. public $specimen = 0;
  552. /**
  553. * @var int Id of contact to send object (used by the trigger of module Agenda)
  554. */
  555. public $sendtoid;
  556. /**
  557. * @var float Amount already paid from getSommePaiement() (used to show correct status)
  558. * @deprecated Duplicate of $totalpaid
  559. */
  560. public $alreadypaid;
  561. /**
  562. * @var float Amount already paid from getSommePaiement() (used to show correct status)
  563. */
  564. public $totalpaid;
  565. /**
  566. * @var array Array with label of status
  567. */
  568. public $labelStatus = array();
  569. /**
  570. * @var array Array with short label of status
  571. */
  572. public $labelStatusShort = array();
  573. /**
  574. * @var array Array to store list of tpl
  575. */
  576. public $tpl;
  577. /**
  578. * @var int show photo on popup
  579. */
  580. public $showphoto_on_popup;
  581. /**
  582. * @var array nb used in load_stateboard
  583. */
  584. public $nb = array();
  585. /**
  586. * @var int used for the return of show_photos()
  587. */
  588. public $nbphoto;
  589. /**
  590. * @var string output
  591. */
  592. public $output;
  593. /**
  594. * @var array extra parameters
  595. */
  596. public $extraparams = array();
  597. /**
  598. * @var array List of child tables. To test if we can delete object.
  599. */
  600. protected $childtables = array();
  601. /**
  602. * @var array List of child tables. To know object to delete on cascade.
  603. * If name is like '@ClassName:FilePathClass:ParentFkFieldName', it will
  604. * call method deleteByParentField(parentId, ParentFkFieldName) to fetch and delete child object.
  605. */
  606. protected $childtablesoncascade = array();
  607. /**
  608. * @var Product Populate by fetch_product()
  609. */
  610. public $product;
  611. /**
  612. * @var int Populate by setPaymentTerms()
  613. */
  614. public $cond_reglement_supplier_id;
  615. /**
  616. * @var string Populate by setPaymentTerms()
  617. */
  618. public $deposit_percent;
  619. /**
  620. * @var string Populate by setRetainedWarrantyPaymentTerms()
  621. */
  622. public $retained_warranty_fk_cond_reglement;
  623. /**
  624. * @var int Populate by setWarehouse()
  625. */
  626. public $warehouse_id;
  627. // No constructor as it is an abstract class
  628. /**
  629. * Check an object id/ref exists
  630. * If you don't need/want to instantiate object and just need to know if object exists, use this method instead of fetch
  631. *
  632. * @param string $element String of element ('product', 'facture', ...)
  633. * @param int $id Id of object
  634. * @param string $ref Ref of object to check
  635. * @param string $ref_ext Ref ext of object to check
  636. * @return int Return integer <0 if KO, 0 if OK but not found, >0 if OK and exists
  637. */
  638. public static function isExistingObject($element, $id, $ref = '', $ref_ext = '')
  639. {
  640. global $db, $conf;
  641. $sql = "SELECT rowid, ref, ref_ext";
  642. $sql .= " FROM ".$db->prefix().$element;
  643. $sql .= " WHERE entity IN (".getEntity($element).")";
  644. if ($id > 0) {
  645. $sql .= " AND rowid = ".((int) $id);
  646. } elseif ($ref) {
  647. $sql .= " AND ref = '".$db->escape($ref)."'";
  648. } elseif ($ref_ext) {
  649. $sql .= " AND ref_ext = '".$db->escape($ref_ext)."'";
  650. } else {
  651. $error = 'ErrorWrongParameters';
  652. dol_print_error(get_class()."::isExistingObject ".$error, LOG_ERR);
  653. return -1;
  654. }
  655. if ($ref || $ref_ext) { // Because the same ref can exists in 2 different entities, we force the current one in priority
  656. $sql .= " AND entity = ".((int) $conf->entity);
  657. }
  658. dol_syslog(get_class()."::isExistingObject", LOG_DEBUG);
  659. $resql = $db->query($sql);
  660. if ($resql) {
  661. $num = $db->num_rows($resql);
  662. if ($num > 0) {
  663. return 1;
  664. } else {
  665. return 0;
  666. }
  667. }
  668. return -1;
  669. }
  670. /**
  671. * setErrorsFromObject
  672. *
  673. * @param CommonObject $object commonobject
  674. * @return void
  675. */
  676. public function setErrorsFromObject($object)
  677. {
  678. if (!empty($object->error)) {
  679. $this->error = $object->error;
  680. }
  681. if (!empty($object->errors)) {
  682. $this->errors = array_merge($this->errors, $object->errors);
  683. }
  684. }
  685. /**
  686. * Return array of datas to show into a tooltip. This method must be implemented in each object class.
  687. *
  688. * @since v18
  689. * @param array $params params to construct tooltip data
  690. * @return array
  691. */
  692. public function getTooltipContentArray($params)
  693. {
  694. return [];
  695. }
  696. /**
  697. * getTooltipContent
  698. *
  699. * @param array $params params
  700. * @since v18
  701. * @return string
  702. */
  703. public function getTooltipContent($params)
  704. {
  705. global $action, $extrafields, $langs, $hookmanager;
  706. // If there is too much extrafields, we do not include them into tooltip
  707. $MAX_EXTRAFIELDS_TO_SHOW_IN_TOOLTIP = getDolGlobalInt('MAX_EXTRAFIELDS_TO_SHOW_IN_TOOLTIP', 3);
  708. $datas = $this->getTooltipContentArray($params);
  709. $count = 0;
  710. // Add extrafields
  711. if (!empty($extrafields->attributes[$this->table_element]['label'])) {
  712. $datas['opendivextra'] = '<div class="centpercent wordbreak divtooltipextra">';
  713. foreach ($extrafields->attributes[$this->table_element]['label'] as $key => $val) {
  714. if ($extrafields->attributes[$this->table_element]['type'][$key] == 'separate') {
  715. continue;
  716. }
  717. if ($count >= abs($MAX_EXTRAFIELDS_TO_SHOW_IN_TOOLTIP)) {
  718. $datas['more_extrafields'] = '<br>...';
  719. break;
  720. }
  721. $enabled = 1;
  722. if ($enabled && isset($extrafields->attributes[$this->table_element]['enabled'][$key])) {
  723. $enabled = dol_eval($extrafields->attributes[$this->table_element]['enabled'][$key], 1, 1, '2');
  724. }
  725. if ($enabled && isset($extrafields->attributes[$this->table_element]['list'][$key])) {
  726. $enabled = dol_eval($extrafields->attributes[$this->table_element]['list'][$key], 1, 1, '2');
  727. }
  728. $perms = 1;
  729. if ($perms && isset($extrafields->attributes[$this->table_element]['perms'][$key])) {
  730. $perms = dol_eval($extrafields->attributes[$this->table_element]['perms'][$key], 1, 1, '2');
  731. }
  732. if (empty($enabled)) {
  733. continue; // 0 = Never visible field
  734. }
  735. if (abs($enabled) != 1 && abs($enabled) != 3 && abs($enabled) != 5 && abs($enabled) != 4) {
  736. continue; // <> -1 and <> 1 and <> 3 = not visible on forms, only on list <> 4 = not visible at the creation
  737. }
  738. if (empty($perms)) {
  739. continue; // 0 = Not visible
  740. }
  741. if (!empty($extrafields->attributes[$this->table_element]['langfile'][$key])) {
  742. $langs->load($extrafields->attributes[$this->table_element]['langfile'][$key]);
  743. }
  744. $labelextra = $langs->trans((string) $extrafields->attributes[$this->table_element]['label'][$key]);
  745. if ($extrafields->attributes[$this->table_element]['type'][$key] == 'separate') {
  746. $datas[$key]= '<br><b><u>'. $labelextra . '</u></b>';
  747. } else {
  748. $value = (empty($this->array_options['options_' . $key]) ? '' : $this->array_options['options_' . $key]);
  749. $datas[$key]= '<br><b>'. $labelextra . ':</b> ' . $extrafields->showOutputField($key, $value, '', $this->table_element);
  750. $count++;
  751. }
  752. }
  753. $datas['closedivextra'] = '</div>';
  754. }
  755. $hookmanager->initHooks(array($this->element . 'dao'));
  756. $parameters = array(
  757. 'tooltipcontentarray' => &$datas,
  758. 'params' => $params,
  759. );
  760. // Note that $action and $object may have been modified by some hooks
  761. $hookmanager->executeHooks('getTooltipContent', $parameters, $this, $action);
  762. //var_dump($datas);
  763. $label = implode($datas);
  764. return $label;
  765. }
  766. /**
  767. * Method to output saved errors
  768. *
  769. * @return string String with errors
  770. */
  771. public function errorsToString()
  772. {
  773. return $this->error.(is_array($this->errors) ? (($this->error != '' ? ', ' : '').join(', ', $this->errors)) : '');
  774. }
  775. /**
  776. * Return customer ref for screen output.
  777. *
  778. * @param string $objref Customer ref
  779. * @return string Customer ref formated
  780. */
  781. public function getFormatedCustomerRef($objref)
  782. {
  783. global $hookmanager;
  784. $parameters = array('objref'=>$objref);
  785. $action = '';
  786. $reshook = $hookmanager->executeHooks('getFormatedCustomerRef', $parameters, $this, $action); // Note that $action and $object may have been modified by some hooks
  787. if ($reshook > 0) {
  788. return $hookmanager->resArray['objref'];
  789. }
  790. return $objref.(isset($hookmanager->resArray['objref']) ? $hookmanager->resArray['objref'] : '');
  791. }
  792. /**
  793. * Return supplier ref for screen output.
  794. *
  795. * @param string $objref Supplier ref
  796. * @return string Supplier ref formated
  797. */
  798. public function getFormatedSupplierRef($objref)
  799. {
  800. global $hookmanager;
  801. $parameters = array('objref'=>$objref);
  802. $action = '';
  803. $reshook = $hookmanager->executeHooks('getFormatedSupplierRef', $parameters, $this, $action); // Note that $action and $object may have been modified by some hooks
  804. if ($reshook > 0) {
  805. return $hookmanager->resArray['objref'];
  806. }
  807. return $objref.(isset($hookmanager->resArray['objref']) ? $hookmanager->resArray['objref'] : '');
  808. }
  809. /**
  810. * Return full address of contact
  811. *
  812. * @param int $withcountry 1=Add country into address string
  813. * @param string $sep Separator to use to build string
  814. * @param int $withregion 1=Add region into address string
  815. * @param string $extralangcode User extralanguages as value
  816. * @return string Full address string
  817. */
  818. public function getFullAddress($withcountry = 0, $sep = "\n", $withregion = 0, $extralangcode = '')
  819. {
  820. if ($withcountry && $this->country_id && (empty($this->country_code) || empty($this->country))) {
  821. require_once DOL_DOCUMENT_ROOT.'/core/lib/company.lib.php';
  822. $tmparray = getCountry($this->country_id, 'all');
  823. $this->country_code = $tmparray['code'];
  824. $this->country = $tmparray['label'];
  825. }
  826. if ($withregion && $this->state_id && (empty($this->state_code) || empty($this->state) || empty($this->region) || empty($this->region_code))) {
  827. require_once DOL_DOCUMENT_ROOT.'/core/lib/company.lib.php';
  828. $tmparray = getState($this->state_id, 'all', 0, 1);
  829. $this->state_code = $tmparray['code'];
  830. $this->state = $tmparray['label'];
  831. $this->region_code = $tmparray['region_code'];
  832. $this->region = $tmparray['region'];
  833. }
  834. return dol_format_address($this, $withcountry, $sep, '', 0, $extralangcode);
  835. }
  836. /**
  837. * Return the link of last main doc file for direct public download.
  838. *
  839. * @param string $modulepart Module related to document
  840. * @param int $initsharekey Init the share key if it was not yet defined
  841. * @param int $relativelink 0=Return full external link, 1=Return link relative to root of file
  842. * @return string Link or empty string if there is no download link
  843. */
  844. public function getLastMainDocLink($modulepart, $initsharekey = 0, $relativelink = 0)
  845. {
  846. global $user, $dolibarr_main_url_root;
  847. if (empty($this->last_main_doc)) {
  848. return ''; // No way to known which document name to use
  849. }
  850. include_once DOL_DOCUMENT_ROOT.'/ecm/class/ecmfiles.class.php';
  851. $ecmfile = new EcmFiles($this->db);
  852. $result = $ecmfile->fetch(0, '', $this->last_main_doc);
  853. if ($result < 0) {
  854. $this->error = $ecmfile->error;
  855. $this->errors = $ecmfile->errors;
  856. return -1;
  857. }
  858. if (empty($ecmfile->id)) {
  859. // Add entry into index
  860. if ($initsharekey) {
  861. require_once DOL_DOCUMENT_ROOT.'/core/lib/security2.lib.php';
  862. // TODO We can't, we dont' have full path of file, only last_main_doc and ->element, so we must first rebuild full path $destfull
  863. /*
  864. $ecmfile->filepath = $rel_dir;
  865. $ecmfile->filename = $filename;
  866. $ecmfile->label = md5_file(dol_osencode($destfull)); // hash of file content
  867. $ecmfile->fullpath_orig = '';
  868. $ecmfile->gen_or_uploaded = 'generated';
  869. $ecmfile->description = ''; // indexed content
  870. $ecmfile->keywords = ''; // keyword content
  871. $ecmfile->share = getRandomPassword(true);
  872. $result = $ecmfile->create($user);
  873. if ($result < 0)
  874. {
  875. $this->error = $ecmfile->error;
  876. $this->errors = $ecmfile->errors;
  877. }
  878. */
  879. } else {
  880. return '';
  881. }
  882. } elseif (empty($ecmfile->share)) {
  883. // Add entry into index
  884. if ($initsharekey) {
  885. require_once DOL_DOCUMENT_ROOT.'/core/lib/security2.lib.php';
  886. $ecmfile->share = getRandomPassword(true);
  887. $ecmfile->update($user);
  888. } else {
  889. return '';
  890. }
  891. }
  892. // Define $urlwithroot
  893. $urlwithouturlroot = preg_replace('/'.preg_quote(DOL_URL_ROOT, '/').'$/i', '', trim($dolibarr_main_url_root));
  894. // This is to use external domain name found into config file
  895. //if (DOL_URL_ROOT && ! preg_match('/\/$/', $urlwithouturlroot) && ! preg_match('/^\//', DOL_URL_ROOT)) $urlwithroot=$urlwithouturlroot.'/'.DOL_URL_ROOT;
  896. //else
  897. $urlwithroot = $urlwithouturlroot.DOL_URL_ROOT;
  898. //$urlwithroot=DOL_MAIN_URL_ROOT; // This is to use same domain name than current
  899. $forcedownload = 0;
  900. $paramlink = '';
  901. //if (!empty($modulepart)) $paramlink.=($paramlink?'&':'').'modulepart='.$modulepart; // For sharing with hash (so public files), modulepart is not required.
  902. //if (!empty($ecmfile->entity)) $paramlink.='&entity='.$ecmfile->entity; // For sharing with hash (so public files), entity is not required.
  903. //$paramlink.=($paramlink?'&':'').'file='.urlencode($filepath); // No need of name of file for public link, we will use the hash
  904. if (!empty($ecmfile->share)) {
  905. $paramlink .= ($paramlink ? '&' : '').'hashp='.$ecmfile->share; // Hash for public share
  906. }
  907. if ($forcedownload) {
  908. $paramlink .= ($paramlink ? '&' : '').'attachment=1';
  909. }
  910. if ($relativelink) {
  911. $linktoreturn = 'document.php'.($paramlink ? '?'.$paramlink : '');
  912. } else {
  913. $linktoreturn = $urlwithroot.'/document.php'.($paramlink ? '?'.$paramlink : '');
  914. }
  915. // Here $ecmfile->share is defined
  916. return $linktoreturn;
  917. }
  918. // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
  919. /**
  920. * Add a link between element $this->element and a contact
  921. *
  922. * @param int $fk_socpeople Id of thirdparty contact (if source = 'external') or id of user (if souce = 'internal') to link
  923. * @param int|string $type_contact Type of contact (code or id). Must be id or code found into table llx_c_type_contact. For example: SALESREPFOLL
  924. * @param string $source external=Contact extern (llx_socpeople), internal=Contact intern (llx_user)
  925. * @param int $notrigger Disable all triggers
  926. * @return int Return integer <0 if KO, 0 if already added or code not valid, >0 if OK
  927. */
  928. public function add_contact($fk_socpeople, $type_contact, $source = 'external', $notrigger = 0)
  929. {
  930. // phpcs:enable
  931. global $user, $langs;
  932. dol_syslog(get_class($this)."::add_contact $fk_socpeople, $type_contact, $source, $notrigger");
  933. // Check parameters
  934. if ($fk_socpeople <= 0) {
  935. $langs->load("errors");
  936. $this->error = $langs->trans("ErrorWrongValueForParameterX", "1");
  937. dol_syslog(get_class($this)."::add_contact ".$this->error, LOG_ERR);
  938. return -1;
  939. }
  940. if (!$type_contact) {
  941. $langs->load("errors");
  942. $this->error = $langs->trans("ErrorWrongValueForParameterX", "2");
  943. dol_syslog(get_class($this)."::add_contact ".$this->error, LOG_ERR);
  944. return -2;
  945. }
  946. $id_type_contact = 0;
  947. if (is_numeric($type_contact)) {
  948. $id_type_contact = $type_contact;
  949. } else {
  950. // We look for id type_contact
  951. $sql = "SELECT tc.rowid";
  952. $sql .= " FROM ".$this->db->prefix()."c_type_contact as tc";
  953. $sql .= " WHERE tc.element='".$this->db->escape($this->element)."'";
  954. $sql .= " AND tc.source='".$this->db->escape($source)."'";
  955. $sql .= " AND tc.code='".$this->db->escape($type_contact)."' AND tc.active=1";
  956. //print $sql;
  957. $resql = $this->db->query($sql);
  958. if ($resql) {
  959. $obj = $this->db->fetch_object($resql);
  960. if ($obj) {
  961. $id_type_contact = $obj->rowid;
  962. }
  963. }
  964. }
  965. if ($id_type_contact == 0) {
  966. dol_syslog("CODE_NOT_VALID_FOR_THIS_ELEMENT: Code type of contact '".$type_contact."' does not exists or is not active for element ".$this->element.", we can ignore it");
  967. return 0;
  968. }
  969. $datecreate = dol_now();
  970. // Socpeople must have already been added by some trigger, then we have to check it to avoid DB_ERROR_RECORD_ALREADY_EXISTS error
  971. $TListeContacts = $this->liste_contact(-1, $source);
  972. $already_added = false;
  973. if (is_array($TListeContacts) && !empty($TListeContacts)) {
  974. foreach ($TListeContacts as $array_contact) {
  975. if ($array_contact['status'] == 4 && $array_contact['id'] == $fk_socpeople && $array_contact['fk_c_type_contact'] == $id_type_contact) {
  976. $already_added = true;
  977. break;
  978. }
  979. }
  980. }
  981. if (!$already_added) {
  982. $this->db->begin();
  983. // Insert into database
  984. $sql = "INSERT INTO ".$this->db->prefix()."element_contact";
  985. $sql .= " (element_id, fk_socpeople, datecreate, statut, fk_c_type_contact) ";
  986. $sql .= " VALUES (".$this->id.", ".((int) $fk_socpeople)." , ";
  987. $sql .= "'".$this->db->idate($datecreate)."'";
  988. $sql .= ", 4, ".((int) $id_type_contact);
  989. $sql .= ")";
  990. $resql = $this->db->query($sql);
  991. if ($resql) {
  992. if (!$notrigger) {
  993. $result = $this->call_trigger(strtoupper($this->element).'_ADD_CONTACT', $user);
  994. if ($result < 0) {
  995. $this->db->rollback();
  996. return -1;
  997. }
  998. }
  999. $this->db->commit();
  1000. return 1;
  1001. } else {
  1002. if ($this->db->errno() == 'DB_ERROR_RECORD_ALREADY_EXISTS') {
  1003. $this->error = $this->db->errno();
  1004. $this->db->rollback();
  1005. return -2;
  1006. } else {
  1007. $this->error = $this->db->lasterror();
  1008. $this->db->rollback();
  1009. return -1;
  1010. }
  1011. }
  1012. } else {
  1013. return 0;
  1014. }
  1015. }
  1016. // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
  1017. /**
  1018. * Copy contact from one element to current
  1019. *
  1020. * @param CommonObject $objFrom Source element
  1021. * @param string $source Nature of contact ('internal' or 'external')
  1022. * @return int >0 if OK, <0 if KO
  1023. */
  1024. public function copy_linked_contact($objFrom, $source = 'internal')
  1025. {
  1026. // phpcs:enable
  1027. $contacts = $objFrom->liste_contact(-1, $source);
  1028. foreach ($contacts as $contact) {
  1029. if ($this->add_contact($contact['id'], $contact['fk_c_type_contact'], $contact['source']) < 0) {
  1030. return -1;
  1031. }
  1032. }
  1033. return 1;
  1034. }
  1035. // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
  1036. /**
  1037. * Update a link to contact line
  1038. *
  1039. * @param int $rowid Id of line contact-element
  1040. * @param int $statut New status of link
  1041. * @param int $type_contact_id Id of contact type (not modified if 0)
  1042. * @param int $fk_socpeople Id of soc_people to update (not modified if 0)
  1043. * @return int Return integer <0 if KO, >= 0 if OK
  1044. */
  1045. public function update_contact($rowid, $statut, $type_contact_id = 0, $fk_socpeople = 0)
  1046. {
  1047. // phpcs:enable
  1048. // Insert into database
  1049. $sql = "UPDATE ".$this->db->prefix()."element_contact set";
  1050. $sql .= " statut = ".$statut;
  1051. if ($type_contact_id) {
  1052. $sql .= ", fk_c_type_contact = ".((int) $type_contact_id);
  1053. }
  1054. if ($fk_socpeople) {
  1055. $sql .= ", fk_socpeople = ".((int) $fk_socpeople);
  1056. }
  1057. $sql .= " where rowid = ".((int) $rowid);
  1058. $resql = $this->db->query($sql);
  1059. if ($resql) {
  1060. return 0;
  1061. } else {
  1062. $this->error = $this->db->lasterror();
  1063. return -1;
  1064. }
  1065. }
  1066. // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
  1067. /**
  1068. * Delete a link to contact line
  1069. *
  1070. * @param int $rowid Id of contact link line to delete
  1071. * @param int $notrigger Disable all triggers
  1072. * @return int >0 if OK, <0 if KO
  1073. */
  1074. public function delete_contact($rowid, $notrigger = 0)
  1075. {
  1076. // phpcs:enable
  1077. global $user;
  1078. $error = 0;
  1079. $this->db->begin();
  1080. if (!$error && empty($notrigger)) {
  1081. // Call trigger
  1082. $this->context['contact_id'] = ((int) $rowid);
  1083. $result = $this->call_trigger(strtoupper($this->element).'_DELETE_CONTACT', $user);
  1084. if ($result < 0) {
  1085. $error++;
  1086. }
  1087. // End call triggers
  1088. }
  1089. if (!$error) {
  1090. dol_syslog(get_class($this)."::delete_contact", LOG_DEBUG);
  1091. $sql = "DELETE FROM ".MAIN_DB_PREFIX."element_contact";
  1092. $sql .= " WHERE rowid = ".((int) $rowid);
  1093. $result = $this->db->query($sql);
  1094. if (!$result) {
  1095. $error++;
  1096. $this->errors[] = $this->db->lasterror();
  1097. }
  1098. }
  1099. if (!$error) {
  1100. $this->db->commit();
  1101. return 1;
  1102. } else {
  1103. $this->error = $this->db->lasterror();
  1104. $this->db->rollback();
  1105. return -1;
  1106. }
  1107. }
  1108. // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
  1109. /**
  1110. * Delete all links between an object $this and all its contacts in llx_element_contact
  1111. *
  1112. * @param string $source '' or 'internal' or 'external'
  1113. * @param string $code Type of contact (code or id)
  1114. * @return int Return integer <0 if KO, 0=Nothing done, >0 if OK
  1115. */
  1116. public function delete_linked_contact($source = '', $code = '')
  1117. {
  1118. // phpcs:enable
  1119. $listId = '';
  1120. $temp = array();
  1121. $typeContact = $this->liste_type_contact($source, '', 0, 0, $code);
  1122. if (!empty($typeContact)) {
  1123. foreach ($typeContact as $key => $value) {
  1124. array_push($temp, $key);
  1125. }
  1126. $listId = implode(",", $temp);
  1127. }
  1128. // If $listId is empty, we have not criteria on fk_c_type_contact so we will delete record on element_id for
  1129. // any type or record instead of only the ones of the current object. So we do nothing in such a case.
  1130. if (empty($listId)) {
  1131. return 0;
  1132. }
  1133. $sql = "DELETE FROM ".$this->db->prefix()."element_contact";
  1134. $sql .= " WHERE element_id = ".((int) $this->id);
  1135. $sql .= " AND fk_c_type_contact IN (".$this->db->sanitize($listId).")";
  1136. dol_syslog(get_class($this)."::delete_linked_contact", LOG_DEBUG);
  1137. if ($this->db->query($sql)) {
  1138. return 1;
  1139. } else {
  1140. $this->error = $this->db->lasterror();
  1141. return -1;
  1142. }
  1143. }
  1144. // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
  1145. /**
  1146. * Get array of all contacts for an object
  1147. *
  1148. * @param int $statusoflink Status of links to get (-1=all). Not used.
  1149. * @param string $source Source of contact: 'external' or 'thirdparty' (llx_socpeople) or 'internal' (llx_user)
  1150. * @param int $list 0:Returned array contains all properties, 1:Return array contains just id
  1151. * @param string $code Filter on this code of contact type ('SHIPPING', 'BILLING', ...)
  1152. * @param int $status Status of user or company
  1153. * @param array $arrayoftcids Array with ID of type of contacts. If we provide this, we can make a ec.fk_c_type_contact in ($arrayoftcids) to avoid link on tc table. TODO Not implemented.
  1154. * @return array|int Array of contacts, -1 if error
  1155. */
  1156. public function liste_contact($statusoflink = -1, $source = 'external', $list = 0, $code = '', $status = -1, $arrayoftcids = array())
  1157. {
  1158. // phpcs:enable
  1159. global $langs;
  1160. $tab = array();
  1161. $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
  1162. if ($source == 'internal') {
  1163. $sql .= ", '-1' as socid, t.statut as statuscontact, t.login, t.photo";
  1164. }
  1165. if ($source == 'external' || $source == 'thirdparty') {
  1166. $sql .= ", t.fk_soc as socid, t.statut as statuscontact";
  1167. }
  1168. $sql .= ", t.civility as civility, t.lastname as lastname, t.firstname, t.email";
  1169. $sql .= ", tc.source, tc.element, tc.code, tc.libelle as type_label";
  1170. $sql .= " FROM ".$this->db->prefix()."c_type_contact tc,";
  1171. $sql .= " ".$this->db->prefix()."element_contact ec";
  1172. if ($source == 'internal') { // internal contact (user)
  1173. $sql .= " LEFT JOIN ".$this->db->prefix()."user t on ec.fk_socpeople = t.rowid";
  1174. }
  1175. if ($source == 'external' || $source == 'thirdparty') { // external contact (socpeople)
  1176. $sql .= " LEFT JOIN ".$this->db->prefix()."socpeople t on ec.fk_socpeople = t.rowid";
  1177. }
  1178. $sql .= " WHERE ec.element_id = ".((int) $this->id);
  1179. $sql .= " AND ec.fk_c_type_contact = tc.rowid";
  1180. $sql .= " AND tc.element = '".$this->db->escape($this->element)."'";
  1181. if ($code) {
  1182. $sql .= " AND tc.code = '".$this->db->escape($code)."'";
  1183. }
  1184. if ($source == 'internal') {
  1185. $sql .= " AND tc.source = 'internal'";
  1186. if ($status >= 0) {
  1187. $sql .= " AND t.statut = ".((int) $status);
  1188. }
  1189. }
  1190. if ($source == 'external' || $source == 'thirdparty') {
  1191. $sql .= " AND tc.source = 'external'";
  1192. if ($status >= 0) {
  1193. $sql .= " AND t.statut = ".((int) $status); // t is llx_socpeople
  1194. }
  1195. }
  1196. $sql .= " AND tc.active = 1";
  1197. if ($statusoflink >= 0) {
  1198. $sql .= " AND ec.statut = ".((int) $statusoflink);
  1199. }
  1200. $sql .= " ORDER BY t.lastname ASC";
  1201. dol_syslog(get_class($this)."::liste_contact", LOG_DEBUG);
  1202. $resql = $this->db->query($sql);
  1203. if ($resql) {
  1204. $num = $this->db->num_rows($resql);
  1205. $i = 0;
  1206. while ($i < $num) {
  1207. $obj = $this->db->fetch_object($resql);
  1208. if (!$list) {
  1209. $transkey = "TypeContact_".$obj->element."_".$obj->source."_".$obj->code;
  1210. $libelle_type = ($langs->trans($transkey) != $transkey ? $langs->trans($transkey) : $obj->type_label);
  1211. $tab[$i] = array(
  1212. 'parentId' => $this->id,
  1213. 'source' => $obj->source,
  1214. 'socid' => $obj->socid,
  1215. 'id' => $obj->id,
  1216. 'nom' => $obj->lastname, // For backward compatibility
  1217. 'civility' => $obj->civility,
  1218. 'lastname' => $obj->lastname,
  1219. 'firstname' => $obj->firstname,
  1220. 'email'=>$obj->email,
  1221. 'login'=> (empty($obj->login) ? '' : $obj->login),
  1222. 'photo' => (empty($obj->photo) ? '' : $obj->photo),
  1223. 'statuscontact' => $obj->statuscontact,
  1224. 'rowid' => $obj->rowid,
  1225. 'code' => $obj->code,
  1226. 'libelle' => $libelle_type,
  1227. 'status' => $obj->statuslink,
  1228. 'fk_c_type_contact' => $obj->fk_c_type_contact
  1229. );
  1230. } else {
  1231. $tab[$i] = $obj->id;
  1232. }
  1233. $i++;
  1234. }
  1235. return $tab;
  1236. } else {
  1237. $this->error = $this->db->lasterror();
  1238. dol_print_error($this->db);
  1239. return -1;
  1240. }
  1241. }
  1242. /**
  1243. * Update status of a contact linked to object
  1244. *
  1245. * @param int $rowid Id of link between object and contact
  1246. * @return int Return integer <0 if KO, >=0 if OK
  1247. */
  1248. public function swapContactStatus($rowid)
  1249. {
  1250. $sql = "SELECT ec.datecreate, ec.statut, ec.fk_socpeople, ec.fk_c_type_contact,";
  1251. $sql .= " tc.code, tc.libelle as type_label";
  1252. $sql .= " FROM (".$this->db->prefix()."element_contact as ec, ".$this->db->prefix()."c_type_contact as tc)";
  1253. $sql .= " WHERE ec.rowid =".((int) $rowid);
  1254. $sql .= " AND ec.fk_c_type_contact=tc.rowid";
  1255. $sql .= " AND tc.element = '".$this->db->escape($this->element)."'";
  1256. dol_syslog(get_class($this)."::swapContactStatus", LOG_DEBUG);
  1257. $resql = $this->db->query($sql);
  1258. if ($resql) {
  1259. $obj = $this->db->fetch_object($resql);
  1260. $newstatut = ($obj->statut == 4) ? 5 : 4;
  1261. $result = $this->update_contact($rowid, $newstatut);
  1262. $this->db->free($resql);
  1263. return $result;
  1264. } else {
  1265. $this->error = $this->db->error();
  1266. dol_print_error($this->db);
  1267. return -1;
  1268. }
  1269. }
  1270. // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
  1271. /**
  1272. * Return array with list of possible values for type of contacts
  1273. *
  1274. * @param string $source 'internal', 'external' or 'all'
  1275. * @param string $order Sort order by : 'position', 'code', 'rowid'...
  1276. * @param int $option 0=Return array id->label, 1=Return array code->label
  1277. * @param int $activeonly 0=all status of contact, 1=only the active
  1278. * @param string $code Type of contact (Example: 'CUSTOMER', 'SERVICE')
  1279. * @return array Array list of type of contacts (id->label if option=0, code->label if option=1)
  1280. */
  1281. public function liste_type_contact($source = 'internal', $order = 'position', $option = 0, $activeonly = 0, $code = '')
  1282. {
  1283. // phpcs:enable
  1284. global $langs;
  1285. if (empty($order)) {
  1286. $order = 'position';
  1287. }
  1288. if ($order == 'position') {
  1289. $order .= ',code';
  1290. }
  1291. $tab = array();
  1292. $sql = "SELECT DISTINCT tc.rowid, tc.code, tc.libelle as type_label, tc.position";
  1293. $sql .= " FROM ".$this->db->prefix()."c_type_contact as tc";
  1294. $sql .= " WHERE tc.element='".$this->db->escape($this->element)."'";
  1295. if ($activeonly == 1) {
  1296. $sql .= " AND tc.active=1"; // only the active types
  1297. }
  1298. if (!empty($source) && $source != 'all') {
  1299. $sql .= " AND tc.source='".$this->db->escape($source)."'";
  1300. }
  1301. if (!empty($code)) {
  1302. $sql .= " AND tc.code='".$this->db->escape($code)."'";
  1303. }
  1304. $sql .= $this->db->order($order, 'ASC');
  1305. //print "sql=".$sql;
  1306. $resql = $this->db->query($sql);
  1307. if ($resql) {
  1308. $num = $this->db->num_rows($resql);
  1309. $i = 0;
  1310. while ($i < $num) {
  1311. $obj = $this->db->fetch_object($resql);
  1312. $transkey = "TypeContact_".$this->element."_".$source."_".$obj->code;
  1313. $libelle_type = ($langs->trans($transkey) != $transkey ? $langs->trans($transkey) : $obj->type_label);
  1314. if (empty($option)) {
  1315. $tab[$obj->rowid] = $libelle_type;
  1316. } else {
  1317. $tab[$obj->code] = $libelle_type;
  1318. }
  1319. $i++;
  1320. }
  1321. return $tab;
  1322. } else {
  1323. $this->error = $this->db->lasterror();
  1324. //dol_print_error($this->db);
  1325. return null;
  1326. }
  1327. }
  1328. /**
  1329. * Return array with list of possible values for type of contacts
  1330. *
  1331. * @param string $source 'internal', 'external' or 'all'
  1332. * @param int $option 0=Return array id->label, 1=Return array code->label
  1333. * @param int $activeonly 0=all status of contact, 1=only the active
  1334. * @param string $code Type of contact (Example: 'CUSTOMER', 'SERVICE')
  1335. * @param string $element Filter on 1 element type
  1336. * @param string $excludeelement Exclude 1 element type. Example: 'agenda'
  1337. * @return array Array list of type of contacts (id->label if option=0, code->label if option=1)
  1338. */
  1339. public function listeTypeContacts($source = 'internal', $option = 0, $activeonly = 0, $code = '', $element = '', $excludeelement = '')
  1340. {
  1341. global $langs, $conf;
  1342. $langs->loadLangs(array('bills', 'contracts', 'interventions', 'orders', 'projects', 'propal', 'ticket', 'agenda'));
  1343. $tab = array();
  1344. $sql = "SELECT DISTINCT tc.rowid, tc.code, tc.libelle as type_label, tc.position, tc.element";
  1345. $sql .= " FROM ".$this->db->prefix()."c_type_contact as tc";
  1346. $sqlWhere = array();
  1347. if (!empty($element)) {
  1348. $sqlWhere[] = " tc.element='".$this->db->escape($element)."'";
  1349. }
  1350. if (!empty($excludeelement)) {
  1351. $sqlWhere[] = " tc.element <> '".$this->db->escape($excludeelement)."'";
  1352. }
  1353. if ($activeonly == 1) {
  1354. $sqlWhere[] = " tc.active=1"; // only the active types
  1355. }
  1356. if (!empty($source) && $source != 'all') {
  1357. $sqlWhere[] = " tc.source='".$this->db->escape($source)."'";
  1358. }
  1359. if (!empty($code)) {
  1360. $sqlWhere[] = " tc.code='".$this->db->escape($code)."'";
  1361. }
  1362. if (count($sqlWhere) > 0) {
  1363. $sql .= " WHERE ".implode(' AND ', $sqlWhere);
  1364. }
  1365. $sql .= $this->db->order('tc.element, tc.position', 'ASC');
  1366. dol_syslog(__METHOD__, LOG_DEBUG);
  1367. $resql = $this->db->query($sql);
  1368. if ($resql) {
  1369. $num = $this->db->num_rows($resql);
  1370. if ($num > 0) {
  1371. $langs->loadLangs(array("propal", "orders", "bills", "suppliers", "contracts", "supplier_proposal"));
  1372. while ($obj = $this->db->fetch_object($resql)) {
  1373. $modulename = $obj->element;
  1374. if (strpos($obj->element, 'project') !== false) {
  1375. $modulename = 'projet';
  1376. } elseif ($obj->element == 'contrat') {
  1377. $element = 'contract';
  1378. } elseif ($obj->element == 'action') {
  1379. $modulename = 'agenda';
  1380. } elseif (strpos($obj->element, 'supplier') !== false && $obj->element != 'supplier_proposal') {
  1381. $modulename = 'fournisseur';
  1382. } elseif (strpos($obj->element, 'supplier') !== false && $obj->element != 'supplier_proposal') {
  1383. $modulename = 'fournisseur';
  1384. }
  1385. if (!empty($conf->{$modulename}->enabled)) {
  1386. $libelle_element = $langs->trans('ContactDefault_'.$obj->element);
  1387. $tmpelement = $obj->element;
  1388. $transkey = "TypeContact_".$tmpelement."_".$source."_".$obj->code;
  1389. $libelle_type = ($langs->trans($transkey) != $transkey ? $langs->trans($transkey) : $obj->type_label);
  1390. if (empty($option)) {
  1391. $tab[$obj->rowid] = $libelle_element.' - '.$libelle_type;
  1392. } else {
  1393. $tab[$obj->rowid] = $libelle_element.' - '.$libelle_type;
  1394. }
  1395. }
  1396. }
  1397. }
  1398. return $tab;
  1399. } else {
  1400. $this->error = $this->db->lasterror();
  1401. return null;
  1402. }
  1403. }
  1404. /**
  1405. * Return id of contacts for a source and a contact code.
  1406. * Example: contact client de facturation ('external', 'BILLING')
  1407. * Example: contact client de livraison ('external', 'SHIPPING')
  1408. * Example: contact interne suivi paiement ('internal', 'SALESREPFOLL')
  1409. *
  1410. * @param string $source 'external' or 'internal'
  1411. * @param string $code 'BILLING', 'SHIPPING', 'SALESREPFOLL', ...
  1412. * @param int $status limited to a certain status
  1413. * @return array List of id for such contacts
  1414. */
  1415. public function getIdContact($source, $code, $status = 0)
  1416. {
  1417. global $conf;
  1418. $result = array();
  1419. $i = 0;
  1420. //cas particulier pour les expeditions
  1421. if ($this->element == 'shipping' && $this->origin_id != 0) {
  1422. $id = $this->origin_id;
  1423. $element = 'commande';
  1424. } elseif ($this->element == 'reception' && $this->origin_id != 0) {
  1425. $id = $this->origin_id;
  1426. $element = 'order_supplier';
  1427. } else {
  1428. $id = $this->id;
  1429. $element = $this->element;
  1430. }
  1431. $sql = "SELECT ec.fk_socpeople";
  1432. $sql .= " FROM ".$this->db->prefix()."element_contact as ec,";
  1433. if ($source == 'internal') {
  1434. $sql .= " ".$this->db->prefix()."user as c,";
  1435. }
  1436. if ($source == 'external') {
  1437. $sql .= " ".$this->db->prefix()."socpeople as c,";
  1438. }
  1439. $sql .= " ".$this->db->prefix()."c_type_contact as tc";
  1440. $sql .= " WHERE ec.element_id = ".((int) $id);
  1441. $sql .= " AND ec.fk_socpeople = c.rowid";
  1442. if ($source == 'internal') {
  1443. $sql .= " AND c.entity IN (".getEntity('user').")";
  1444. }
  1445. if ($source == 'external') {
  1446. $sql .= " AND c.entity IN (".getEntity('societe').")";
  1447. }
  1448. $sql .= " AND ec.fk_c_type_contact = tc.rowid";
  1449. $sql .= " AND tc.element = '".$this->db->escape($element)."'";
  1450. $sql .= " AND tc.source = '".$this->db->escape($source)."'";
  1451. if ($code) {
  1452. $sql .= " AND tc.code = '".$this->db->escape($code)."'";
  1453. }
  1454. $sql .= " AND tc.active = 1";
  1455. if ($status) {
  1456. $sql .= " AND ec.statut = ".((int) $status);
  1457. }
  1458. dol_syslog(get_class($this)."::getIdContact", LOG_DEBUG);
  1459. $resql = $this->db->query($sql);
  1460. if ($resql) {
  1461. while ($obj = $this->db->fetch_object($resql)) {
  1462. $result[$i] = $obj->fk_socpeople;
  1463. $i++;
  1464. }
  1465. } else {
  1466. $this->error = $this->db->error();
  1467. return null;
  1468. }
  1469. return $result;
  1470. }
  1471. // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
  1472. /**
  1473. * Load object contact with id=$this->contact_id into $this->contact
  1474. *
  1475. * @param int $contactid Id du contact. Use this->contact_id if empty.
  1476. * @return int Return integer <0 if KO, >0 if OK
  1477. */
  1478. public function fetch_contact($contactid = null)
  1479. {
  1480. // phpcs:enable
  1481. if (empty($contactid)) {
  1482. $contactid = $this->contact_id;
  1483. }
  1484. if (empty($contactid)) {
  1485. return 0;
  1486. }
  1487. require_once DOL_DOCUMENT_ROOT.'/contact/class/contact.class.php';
  1488. $contact = new Contact($this->db);
  1489. $result = $contact->fetch($contactid);
  1490. $this->contact = $contact;
  1491. return $result;
  1492. }
  1493. // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
  1494. /**
  1495. * Load the third party of object, from id $this->socid or $this->fk_soc, into this->thirdparty
  1496. *
  1497. * @param int $force_thirdparty_id Force thirdparty id
  1498. * @return int Return integer <0 if KO, >0 if OK
  1499. */
  1500. public function fetch_thirdparty($force_thirdparty_id = 0)
  1501. {
  1502. // phpcs:enable
  1503. if (empty($this->socid) && empty($this->fk_soc) && empty($force_thirdparty_id)) {
  1504. return 0;
  1505. }
  1506. require_once DOL_DOCUMENT_ROOT.'/societe/class/societe.class.php';
  1507. $idtofetch = isset($this->socid) ? $this->socid : (isset($this->fk_soc) ? $this->fk_soc : 0);
  1508. if ($force_thirdparty_id) {
  1509. $idtofetch = $force_thirdparty_id;
  1510. }
  1511. if ($idtofetch) {
  1512. $thirdparty = new Societe($this->db);
  1513. $result = $thirdparty->fetch($idtofetch);
  1514. if ($result<0) {
  1515. $this->errors=array_merge($this->errors, $thirdparty->errors);
  1516. }
  1517. $this->thirdparty = $thirdparty;
  1518. // Use first price level if level not defined for third party
  1519. if (getDolGlobalString('PRODUIT_MULTIPRICES') && empty($this->thirdparty->price_level)) {
  1520. $this->thirdparty->price_level = 1;
  1521. }
  1522. return $result;
  1523. } else {
  1524. return -1;
  1525. }
  1526. }
  1527. /**
  1528. * Looks for an object with ref matching the wildcard provided
  1529. * It does only work when $this->table_ref_field is set
  1530. *
  1531. * @param string $ref Wildcard
  1532. * @return int >1 = OK, 0 = Not found or table_ref_field not defined, <0 = KO
  1533. */
  1534. public function fetchOneLike($ref)
  1535. {
  1536. if (!$this->table_ref_field) {
  1537. return 0;
  1538. }
  1539. $sql = "SELECT rowid FROM ".$this->db->prefix().$this->table_element;
  1540. $sql .= " WHERE ".$this->table_ref_field." LIKE '".$this->db->escape($ref)."'"; // no escapeforlike here
  1541. $sql .= " LIMIT 1";
  1542. $query = $this->db->query($sql);
  1543. if (!$this->db->num_rows($query)) {
  1544. return 0;
  1545. }
  1546. $result = $this->db->fetch_object($query);
  1547. if (method_exists($this, 'fetch')) {
  1548. return $this->fetch($result->rowid);
  1549. } else {
  1550. $this->error = 'Fetch method not implemented on '.get_class($this);
  1551. dol_syslog(get_class($this).'::fetchOneLike Error='.$this->error, LOG_ERR);
  1552. array_push($this->errors, $this->error);
  1553. return -1;
  1554. }
  1555. }
  1556. // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
  1557. /**
  1558. * Load data for barcode into properties ->barcode_type*
  1559. * Properties ->barcode_type that is id of barcode. Type is used to find other properties, but
  1560. * if it is not defined, ->element must be defined to know default barcode type.
  1561. *
  1562. * @return int Return integer <0 if KO, 0 if can't guess type of barcode (ISBN, EAN13...), >0 if OK (all barcode properties loaded)
  1563. */
  1564. public function fetch_barcode()
  1565. {
  1566. // phpcs:enable
  1567. global $conf;
  1568. dol_syslog(get_class($this).'::fetch_barcode this->element='.$this->element.' this->barcode_type='.$this->barcode_type);
  1569. $idtype = $this->barcode_type;
  1570. 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
  1571. if ($this->element == 'product' && getDolGlobalString('PRODUIT_DEFAULT_BARCODE_TYPE')) {
  1572. $idtype = $conf->global->PRODUIT_DEFAULT_BARCODE_TYPE;
  1573. } elseif ($this->element == 'societe') {
  1574. $idtype = $conf->global->GENBARCODE_BARCODETYPE_THIRDPARTY;
  1575. } else {
  1576. dol_syslog('Call fetch_barcode with barcode_type not defined and cant be guessed', LOG_WARNING);
  1577. }
  1578. }
  1579. if ($idtype > 0) {
  1580. 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
  1581. $sql = "SELECT rowid, code, libelle as label, coder";
  1582. $sql .= " FROM ".$this->db->prefix()."c_barcode_type";
  1583. $sql .= " WHERE rowid = ".((int) $idtype);
  1584. dol_syslog(get_class($this).'::fetch_barcode', LOG_DEBUG);
  1585. $resql = $this->db->query($sql);
  1586. if ($resql) {
  1587. $obj = $this->db->fetch_object($resql);
  1588. $this->barcode_type = $obj->rowid;
  1589. $this->barcode_type_code = $obj->code;
  1590. $this->barcode_type_label = $obj->label;
  1591. $this->barcode_type_coder = $obj->coder;
  1592. return 1;
  1593. } else {
  1594. dol_print_error($this->db);
  1595. return -1;
  1596. }
  1597. }
  1598. }
  1599. return 0;
  1600. }
  1601. // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
  1602. /**
  1603. * Load the project with id $this->fk_project into this->project
  1604. *
  1605. * @return int Return integer <0 if KO, >=0 if OK
  1606. */
  1607. public function fetch_project()
  1608. {
  1609. // phpcs:enable
  1610. return $this->fetch_projet();
  1611. }
  1612. // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
  1613. /**
  1614. * Load the project with id $this->fk_project into this->project
  1615. *
  1616. * @return int Return integer <0 if KO, >=0 if OK
  1617. */
  1618. public function fetch_projet()
  1619. {
  1620. // phpcs:enable
  1621. include_once DOL_DOCUMENT_ROOT.'/projet/class/project.class.php';
  1622. if (empty($this->fk_project) && !empty($this->fk_projet)) {
  1623. $this->fk_project = $this->fk_projet; // For backward compatibility
  1624. }
  1625. if (empty($this->fk_project)) {
  1626. return 0;
  1627. }
  1628. $project = new Project($this->db);
  1629. $result = $project->fetch($this->fk_project);
  1630. $this->projet = $project; // deprecated
  1631. $this->project = $project;
  1632. return $result;
  1633. }
  1634. // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
  1635. /**
  1636. * Load the product with id $this->fk_product into this->product
  1637. *
  1638. * @return int Return integer <0 if KO, >=0 if OK
  1639. */
  1640. public function fetch_product()
  1641. {
  1642. // phpcs:enable
  1643. include_once DOL_DOCUMENT_ROOT.'/product/class/product.class.php';
  1644. if (empty($this->fk_product)) {
  1645. return 0;
  1646. }
  1647. $product = new Product($this->db);
  1648. $result = $product->fetch($this->fk_product);
  1649. $this->product = $product;
  1650. return $result;
  1651. }
  1652. // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
  1653. /**
  1654. * Load the user with id $userid into this->user
  1655. *
  1656. * @param int $userid Id du contact
  1657. * @return int Return integer <0 if KO, >0 if OK
  1658. */
  1659. public function fetch_user($userid)
  1660. {
  1661. // phpcs:enable
  1662. $user = new User($this->db);
  1663. $result = $user->fetch($userid);
  1664. $this->user = $user;
  1665. return $result;
  1666. }
  1667. // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
  1668. /**
  1669. * Read linked origin object.
  1670. * Set ->origin_object
  1671. * Set also ->expedition or ->livraison or ->commandFournisseur (deprecated)
  1672. *
  1673. * @return void
  1674. */
  1675. public function fetch_origin()
  1676. {
  1677. // phpcs:enable
  1678. if ($this->origin == 'shipping') {
  1679. $this->origin = 'expedition';
  1680. }
  1681. if ($this->origin == 'delivery') {
  1682. $this->origin = 'livraison';
  1683. }
  1684. if ($this->origin == 'order_supplier' || $this->origin == 'supplier_order') {
  1685. $this->origin = 'commandeFournisseur';
  1686. }
  1687. $origin = $this->origin;
  1688. $classname = ucfirst($origin);
  1689. $this->origin_object = new $classname($this->db);
  1690. $this->origin_object->fetch($this->origin_id);
  1691. // TODO Remove this line
  1692. $this->$origin = $this->origin_object;
  1693. }
  1694. /**
  1695. * Load object from specific field
  1696. *
  1697. * @param string $table Table element or element line
  1698. * @param string $field Field selected
  1699. * @param string $key Import key
  1700. * @param string $element Element name
  1701. * @return int Return integer <0 if KO, >0 if OK
  1702. */
  1703. public function fetchObjectFrom($table, $field, $key, $element = null)
  1704. {
  1705. global $conf;
  1706. $result = false;
  1707. $sql = "SELECT rowid FROM ".$this->db->prefix().$table;
  1708. $sql .= " WHERE ".$field." = '".$this->db->escape($key)."'";
  1709. if (!empty($element)) {
  1710. $sql .= " AND entity IN (".getEntity($element).")";
  1711. } else {
  1712. $sql .= " AND entity = ".((int) $conf->entity);
  1713. }
  1714. dol_syslog(get_class($this).'::fetchObjectFrom', LOG_DEBUG);
  1715. $resql = $this->db->query($sql);
  1716. if ($resql) {
  1717. $obj = $this->db->fetch_object($resql);
  1718. // Test for avoid error -1
  1719. if ($obj) {
  1720. if (method_exists($this, 'fetch')) {
  1721. return $this->fetch($obj->rowid);
  1722. } else {
  1723. $this->error = 'fetch() method not implemented on '.get_class($this);
  1724. dol_syslog(get_class($this).'::fetchOneLike Error='.$this->error, LOG_ERR);
  1725. array_push($this->errors, $this->error);
  1726. return -1;
  1727. }
  1728. }
  1729. }
  1730. return $result;
  1731. }
  1732. /**
  1733. * Getter generic. Load value from a specific field
  1734. *
  1735. * @param string $table Table of element or element line
  1736. * @param int $id Element id
  1737. * @param string $field Field selected
  1738. * @return int Return integer <0 if KO, >0 if OK
  1739. */
  1740. public function getValueFrom($table, $id, $field)
  1741. {
  1742. $result = false;
  1743. if (!empty($id) && !empty($field) && !empty($table)) {
  1744. $sql = "SELECT ".$field." FROM ".$this->db->prefix().$table;
  1745. $sql .= " WHERE rowid = ".((int) $id);
  1746. dol_syslog(get_class($this).'::getValueFrom', LOG_DEBUG);
  1747. $resql = $this->db->query($sql);
  1748. if ($resql) {
  1749. $row = $this->db->fetch_row($resql);
  1750. $result = $row[0];
  1751. }
  1752. }
  1753. return $result;
  1754. }
  1755. /**
  1756. * Setter generic. Update a specific field into database.
  1757. * Warning: Trigger is run only if param trigkey is provided.
  1758. *
  1759. * @param string $field Field to update
  1760. * @param mixed $value New value
  1761. * @param string $table To force other table element or element line (should not be used)
  1762. * @param int $id To force other object id (should not be used)
  1763. * @param string $format Data format ('text', 'int', 'date'). 'text' is used if not defined
  1764. * @param string $id_field To force rowid field name. 'rowid' is used if not defined
  1765. * @param User|string $fuser Update the user of last update field with this user. If not provided, current user is used except if value is 'none'
  1766. * @param string $trigkey Trigger key to run (in most cases something like 'XXX_MODIFY')
  1767. * @param string $fk_user_field Name of field to save user id making change
  1768. * @return int Return integer <0 if KO, >0 if OK
  1769. * @see updateExtraField()
  1770. */
  1771. public function setValueFrom($field, $value, $table = '', $id = null, $format = '', $id_field = '', $fuser = null, $trigkey = '', $fk_user_field = 'fk_user_modif')
  1772. {
  1773. global $user;
  1774. if (empty($table)) {
  1775. $table = $this->table_element;
  1776. }
  1777. if (empty($id)) {
  1778. $id = $this->id;
  1779. }
  1780. if (empty($format)) {
  1781. $format = 'text';
  1782. }
  1783. if (empty($id_field)) {
  1784. $id_field = 'rowid';
  1785. }
  1786. // Special case
  1787. if ($table == 'product' && $field == 'note_private') {
  1788. $field = 'note';
  1789. }
  1790. if (in_array($table, array('actioncomm', 'adherent', 'advtargetemailing', 'cronjob', 'establishment'))) {
  1791. $fk_user_field = 'fk_user_mod';
  1792. }
  1793. if (in_array($table, array('prelevement_bons'))) { // TODO Add a field fk_user_modif into llx_prelevement_bons
  1794. $fk_user_field = '';
  1795. }
  1796. if ($trigkey) {
  1797. $oldvalue = null;
  1798. $sql = "SELECT " . $field;
  1799. $sql .= " FROM " . MAIN_DB_PREFIX . $table;
  1800. $sql .= " WHERE " . $id_field . " = " . ((int) $id);
  1801. $resql = $this->db->query($sql);
  1802. if ($resql) {
  1803. if ($obj = $this->db->fetch_object($resql)) {
  1804. if ($format == 'date') {
  1805. $oldvalue = $this->db->jdate($obj->$field);
  1806. } else {
  1807. $oldvalue = $obj->$field;
  1808. }
  1809. }
  1810. } else {
  1811. $this->error = $this->db->lasterror();
  1812. return -1;
  1813. }
  1814. }
  1815. $error = 0;
  1816. dol_syslog(__METHOD__, LOG_DEBUG);
  1817. $this->db->begin();
  1818. $sql = "UPDATE ".$this->db->prefix().$table." SET ";
  1819. if ($format == 'text') {
  1820. $sql .= $field." = '".$this->db->escape($value)."'";
  1821. } elseif ($format == 'int') {
  1822. $sql .= $field." = ".((int) $value);
  1823. } elseif ($format == 'date') {
  1824. $sql .= $field." = ".($value ? "'".$this->db->idate($value)."'" : "null");
  1825. } elseif ($format == 'dategmt') {
  1826. $sql .= $field." = ".($value ? "'".$this->db->idate($value, 'gmt')."'" : "null");
  1827. }
  1828. if ($fk_user_field) {
  1829. if (!empty($fuser) && is_object($fuser)) {
  1830. $sql .= ", ".$fk_user_field." = ".((int) $fuser->id);
  1831. } elseif (empty($fuser) || $fuser != 'none') {
  1832. $sql .= ", ".$fk_user_field." = ".((int) $user->id);
  1833. }
  1834. }
  1835. $sql .= " WHERE ".$id_field." = ".((int) $id);
  1836. $resql = $this->db->query($sql);
  1837. if ($resql) {
  1838. if ($trigkey) {
  1839. // call trigger with updated object values
  1840. if (method_exists($this, 'fetch')) {
  1841. $result = $this->fetch($id);
  1842. } else {
  1843. $result = $this->fetchCommon($id);
  1844. }
  1845. $this->oldcopy = clone $this;
  1846. if (property_exists($this->oldcopy, $field)) {
  1847. $this->oldcopy->$field = $oldvalue;
  1848. }
  1849. if ($result >= 0) {
  1850. $result = $this->call_trigger($trigkey, (!empty($fuser) && is_object($fuser)) ? $fuser : $user); // This may set this->errors
  1851. }
  1852. if ($result < 0) {
  1853. $error++;
  1854. }
  1855. }
  1856. if (!$error) {
  1857. if (property_exists($this, $field)) {
  1858. $this->$field = $value;
  1859. }
  1860. $this->db->commit();
  1861. return 1;
  1862. } else {
  1863. $this->db->rollback();
  1864. return -2;
  1865. }
  1866. } else {
  1867. if ($this->db->lasterrno() == 'DB_ERROR_RECORD_ALREADY_EXISTS') {
  1868. $this->error = 'DB_ERROR_RECORD_ALREADY_EXISTS';
  1869. } else {
  1870. $this->error = $this->db->lasterror();
  1871. }
  1872. $this->db->rollback();
  1873. return -1;
  1874. }
  1875. }
  1876. // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
  1877. /**
  1878. * Load properties id_previous and id_next by comparing $fieldid with $this->ref
  1879. *
  1880. * @param string $filter Optional SQL filter. Example: "(t.field1 = 'aa' OR t.field2 = 'bb')". Do not allow user input data here.
  1881. * Use SQL and not Universal Search Filter. @TODO Replace this with an USF string after changing all ->next_prev_filter
  1882. * @param string $fieldid Name of field to use for the select MAX and MIN
  1883. * @param int $nodbprefix Do not include DB prefix to forge table name
  1884. * @return int Return integer <0 if KO, >0 if OK
  1885. */
  1886. public function load_previous_next_ref($filter, $fieldid, $nodbprefix = 0)
  1887. {
  1888. // phpcs:enable
  1889. global $conf, $user;
  1890. if (!$this->table_element) {
  1891. dol_print_error('', get_class($this)."::load_previous_next_ref was called on objet with property table_element not defined");
  1892. return -1;
  1893. }
  1894. if ($fieldid == 'none') {
  1895. return 1;
  1896. }
  1897. // For backward compatibility
  1898. if (in_array($this->table_element, array('facture_rec', 'facture_fourn_rec')) && $fieldid == 'title') {
  1899. $fieldid = 'titre';
  1900. }
  1901. // Security on socid
  1902. $socid = 0;
  1903. if ($user->socid > 0) {
  1904. $socid = $user->socid;
  1905. }
  1906. // this->ismultientitymanaged contains
  1907. // 0=No test on entity, 1=Test with field entity, 'field@table'=Test with link by field@table
  1908. $aliastablesociete = 's';
  1909. if ($this->element == 'societe') {
  1910. $aliastablesociete = 'te'; // te as table_element
  1911. }
  1912. $restrictiononfksoc = empty($this->restrictiononfksoc) ? 0 : $this->restrictiononfksoc;
  1913. $sql = "SELECT MAX(te.".$fieldid.")";
  1914. $sql .= " FROM ".(empty($nodbprefix) ? $this->db->prefix() : '').$this->table_element." as te";
  1915. if (isset($this->ismultientitymanaged) && !is_numeric($this->ismultientitymanaged)) {
  1916. $tmparray = explode('@', $this->ismultientitymanaged);
  1917. $sql .= ", ".$this->db->prefix().$tmparray[1]." as ".($tmparray[1] == 'societe' ? 's' : 'parenttable'); // If we need to link to this table to limit select to entity
  1918. } elseif ($restrictiononfksoc == 1 && $this->element != 'societe' && !$user->hasRight('societe', 'client', 'voir') && !$socid) {
  1919. $sql .= ", ".$this->db->prefix()."societe as s"; // If we need to link to societe to limit select to socid
  1920. } elseif ($restrictiononfksoc == 2 && $this->element != 'societe' && !$user->hasRight('societe', 'client', 'voir') && !$socid) {
  1921. $sql .= " LEFT JOIN ".$this->db->prefix()."societe as s ON te.fk_soc = s.rowid"; // If we need to link to societe to limit select to socid
  1922. }
  1923. if ($restrictiononfksoc && !$user->hasRight('societe', 'client', 'voir') && !$socid) {
  1924. $sql .= " LEFT JOIN ".$this->db->prefix()."societe_commerciaux as sc ON ".$aliastablesociete.".rowid = sc.fk_soc";
  1925. }
  1926. if ($fieldid == 'rowid') {
  1927. $sql .= " WHERE te.".$fieldid." < ".((int) $this->id);
  1928. } else {
  1929. $sql .= " WHERE te.".$fieldid." < '".$this->db->escape($this->ref)."'"; // ->ref must always be defined (set to id if field does not exists)
  1930. }
  1931. if ($restrictiononfksoc == 1 && !$user->hasRight('societe', 'client', 'voir') && !$socid) {
  1932. $sql .= " AND sc.fk_user = ".((int) $user->id);
  1933. }
  1934. if ($restrictiononfksoc == 2 && !$user->hasRight('societe', 'client', 'voir') && !$socid) {
  1935. $sql .= " AND (sc.fk_user = ".((int) $user->id).' OR te.fk_soc IS NULL)';
  1936. }
  1937. if (!empty($filter)) {
  1938. if (!preg_match('/^\s*AND/i', $filter)) {
  1939. $sql .= " AND ";
  1940. }
  1941. $sql .= $filter;
  1942. }
  1943. if (isset($this->ismultientitymanaged) && !is_numeric($this->ismultientitymanaged)) {
  1944. $tmparray = explode('@', $this->ismultientitymanaged);
  1945. $sql .= " AND te.".$tmparray[0]." = ".($tmparray[1] == "societe" ? "s" : "parenttable").".rowid"; // If we need to link to this table to limit select to entity
  1946. } elseif ($restrictiononfksoc == 1 && $this->element != 'societe' && !$user->hasRight('societe', 'client', 'voir') && !$socid) {
  1947. $sql .= ' AND te.fk_soc = s.rowid'; // If we need to link to societe to limit select to socid
  1948. }
  1949. if (isset($this->ismultientitymanaged) && $this->ismultientitymanaged == 1) {
  1950. if ($this->element == 'user' && getDolGlobalInt('MULTICOMPANY_TRANSVERSE_MODE')) {
  1951. if (!empty($user->admin) && empty($user->entity) && $conf->entity == 1) {
  1952. $sql .= " AND te.entity IS NOT NULL"; // Show all users
  1953. } else {
  1954. $sql .= " AND te.rowid IN (SELECT ug.fk_user FROM ".$this->db->prefix()."usergroup_user as ug WHERE ug.entity IN (".getEntity('usergroup')."))";
  1955. }
  1956. } else {
  1957. $sql .= ' AND te.entity IN ('.getEntity($this->element).')';
  1958. }
  1959. }
  1960. if (isset($this->ismultientitymanaged) && !is_numeric($this->ismultientitymanaged) && $this->element != 'societe') {
  1961. $tmparray = explode('@', $this->ismultientitymanaged);
  1962. $sql .= ' AND parenttable.entity IN ('.getEntity($tmparray[1]).')';
  1963. }
  1964. if ($restrictiononfksoc == 1 && $socid && $this->element != 'societe') {
  1965. $sql .= ' AND te.fk_soc = '.((int) $socid);
  1966. }
  1967. if ($restrictiononfksoc == 2 && $socid && $this->element != 'societe') {
  1968. $sql .= ' AND (te.fk_soc = '.((int) $socid).' OR te.fk_soc IS NULL)';
  1969. }
  1970. if ($restrictiononfksoc && $socid && $this->element == 'societe') {
  1971. $sql .= ' AND te.rowid = '.((int) $socid);
  1972. }
  1973. //print 'socid='.$socid.' restrictiononfksoc='.$restrictiononfksoc.' ismultientitymanaged = '.$this->ismultientitymanaged.' filter = '.$filter.' -> '.$sql."<br>";
  1974. $result = $this->db->query($sql);
  1975. if (!$result) {
  1976. $this->error = $this->db->lasterror();
  1977. return -1;
  1978. }
  1979. $row = $this->db->fetch_row($result);
  1980. $this->ref_previous = $row[0];
  1981. $sql = "SELECT MIN(te.".$fieldid.")";
  1982. $sql .= " FROM ".(empty($nodbprefix) ? $this->db->prefix() : '').$this->table_element." as te";
  1983. if (isset($this->ismultientitymanaged) && !is_numeric($this->ismultientitymanaged)) {
  1984. $tmparray = explode('@', $this->ismultientitymanaged);
  1985. $sql .= ", ".$this->db->prefix().$tmparray[1]." as ".($tmparray[1] == 'societe' ? 's' : 'parenttable'); // If we need to link to this table to limit select to entity
  1986. } elseif ($restrictiononfksoc == 1 && $this->element != 'societe' && !$user->hasRight('societe', 'client', 'voir') && !$socid) {
  1987. $sql .= ", ".$this->db->prefix()."societe as s"; // If we need to link to societe to limit select to socid
  1988. } elseif ($restrictiononfksoc == 2 && $this->element != 'societe' && !$user->hasRight('societe', 'client', 'voir') && !$socid) {
  1989. $sql .= " LEFT JOIN ".$this->db->prefix()."societe as s ON te.fk_soc = s.rowid"; // If we need to link to societe to limit select to socid
  1990. }
  1991. if ($restrictiononfksoc && !$user->hasRight('societe', 'client', 'voir') && !$socid) {
  1992. $sql .= " LEFT JOIN ".$this->db->prefix()."societe_commerciaux as sc ON ".$aliastablesociete.".rowid = sc.fk_soc";
  1993. }
  1994. if ($fieldid == 'rowid') {
  1995. $sql .= " WHERE te.".$fieldid." > ".((int) $this->id);
  1996. } else {
  1997. $sql .= " WHERE te.".$fieldid." > '".$this->db->escape($this->ref)."'"; // ->ref must always be defined (set to id if field does not exists)
  1998. }
  1999. if ($restrictiononfksoc == 1 && !$user->hasRight('societe', 'client', 'voir') && !$socid) {
  2000. $sql .= " AND sc.fk_user = ".((int) $user->id);
  2001. }
  2002. if ($restrictiononfksoc == 2 && !$user->hasRight('societe', 'client', 'voir') && !$socid) {
  2003. $sql .= " AND (sc.fk_user = ".((int) $user->id).' OR te.fk_soc IS NULL)';
  2004. }
  2005. if (!empty($filter)) {
  2006. if (!preg_match('/^\s*AND/i', $filter)) {
  2007. $sql .= " AND "; // For backward compatibility
  2008. }
  2009. $sql .= $filter;
  2010. }
  2011. if (isset($this->ismultientitymanaged) && !is_numeric($this->ismultientitymanaged)) {
  2012. $tmparray = explode('@', $this->ismultientitymanaged);
  2013. $sql .= " AND te.".$tmparray[0]." = ".($tmparray[1] == "societe" ? "s" : "parenttable").".rowid"; // If we need to link to this table to limit select to entity
  2014. } elseif ($restrictiononfksoc == 1 && $this->element != 'societe' && !$user->hasRight('societe', 'client', 'voir') && !$socid) {
  2015. $sql .= ' AND te.fk_soc = s.rowid'; // If we need to link to societe to limit select to socid
  2016. }
  2017. if (isset($this->ismultientitymanaged) && $this->ismultientitymanaged == 1) {
  2018. if ($this->element == 'user' && getDolGlobalInt('MULTICOMPANY_TRANSVERSE_MODE')) {
  2019. if (!empty($user->admin) && empty($user->entity) && $conf->entity == 1) {
  2020. $sql .= " AND te.entity IS NOT NULL"; // Show all users
  2021. } else {
  2022. $sql .= " AND te.rowid IN (SELECT ug.fk_user FROM ".$this->db->prefix()."usergroup_user as ug WHERE ug.entity IN (".getEntity('usergroup')."))";
  2023. }
  2024. } else {
  2025. $sql .= ' AND te.entity IN ('.getEntity($this->element).')';
  2026. }
  2027. }
  2028. if (isset($this->ismultientitymanaged) && !is_numeric($this->ismultientitymanaged) && $this->element != 'societe') {
  2029. $tmparray = explode('@', $this->ismultientitymanaged);
  2030. $sql .= ' AND parenttable.entity IN ('.getEntity($tmparray[1]).')';
  2031. }
  2032. if ($restrictiononfksoc == 1 && $socid && $this->element != 'societe') {
  2033. $sql .= ' AND te.fk_soc = '.((int) $socid);
  2034. }
  2035. if ($restrictiononfksoc == 2 && $socid && $this->element != 'societe') {
  2036. $sql .= ' AND (te.fk_soc = '.((int) $socid).' OR te.fk_soc IS NULL)';
  2037. }
  2038. if ($restrictiononfksoc && $socid && $this->element == 'societe') {
  2039. $sql .= ' AND te.rowid = '.((int) $socid);
  2040. }
  2041. //print 'socid='.$socid.' restrictiononfksoc='.$restrictiononfksoc.' ismultientitymanaged = '.$this->ismultientitymanaged.' filter = '.$filter.' -> '.$sql."<br>";
  2042. // 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
  2043. $result = $this->db->query($sql);
  2044. if (!$result) {
  2045. $this->error = $this->db->lasterror();
  2046. return -2;
  2047. }
  2048. $row = $this->db->fetch_row($result);
  2049. $this->ref_next = $row[0];
  2050. return 1;
  2051. }
  2052. /**
  2053. * Return list of id of contacts of object
  2054. *
  2055. * @param string $source Source of contact: external (llx_socpeople) or internal (llx_user) or thirdparty (llx_societe)
  2056. * @return array Array of id of contacts (if source=external or internal)
  2057. * Array of id of third parties with at least one contact on object (if source=thirdparty)
  2058. */
  2059. public function getListContactId($source = 'external')
  2060. {
  2061. $contactAlreadySelected = array();
  2062. $tab = $this->liste_contact(-1, $source);
  2063. $num = count($tab);
  2064. $i = 0;
  2065. while ($i < $num) {
  2066. if ($source == 'thirdparty') {
  2067. $contactAlreadySelected[$i] = $tab[$i]['socid'];
  2068. } else {
  2069. $contactAlreadySelected[$i] = $tab[$i]['id'];
  2070. }
  2071. $i++;
  2072. }
  2073. return $contactAlreadySelected;
  2074. }
  2075. /**
  2076. * Link element with a project
  2077. *
  2078. * @param int $projectid Project id to link element to
  2079. * @param int $notrigger Disable the trigger
  2080. * @return int Return integer <0 if KO, >0 if OK
  2081. */
  2082. public function setProject($projectid, $notrigger = 0)
  2083. {
  2084. global $user;
  2085. $error = 0;
  2086. if (!$this->table_element) {
  2087. dol_syslog(get_class($this)."::setProject was called on objet with property table_element not defined", LOG_ERR);
  2088. return -1;
  2089. }
  2090. $sql = "UPDATE ".$this->db->prefix().$this->table_element;
  2091. if (!empty($this->fields['fk_project'])) { // Common case
  2092. if ($projectid) {
  2093. $sql .= " SET fk_project = ".((int) $projectid);
  2094. } else {
  2095. $sql .= " SET fk_project = NULL";
  2096. }
  2097. $sql .= ' WHERE rowid = '.((int) $this->id);
  2098. } elseif ($this->table_element == 'actioncomm') { // Special case for actioncomm
  2099. if ($projectid) {
  2100. $sql .= " SET fk_project = ".((int) $projectid);
  2101. } else {
  2102. $sql .= " SET fk_project = NULL";
  2103. }
  2104. $sql .= ' WHERE id = '.((int) $this->id);
  2105. } else { // Special case for old architecture objects
  2106. if ($projectid) {
  2107. $sql .= ' SET fk_projet = '.((int) $projectid);
  2108. } else {
  2109. $sql .= ' SET fk_projet = NULL';
  2110. }
  2111. $sql .= " WHERE rowid = ".((int) $this->id);
  2112. }
  2113. $this->db->begin();
  2114. dol_syslog(get_class($this)."::setProject", LOG_DEBUG);
  2115. if ($this->db->query($sql)) {
  2116. $this->fk_project = ((int) $projectid);
  2117. } else {
  2118. dol_print_error($this->db);
  2119. $error++;
  2120. }
  2121. // Triggers
  2122. if (!$error && !$notrigger) {
  2123. // Call triggers
  2124. $result = $this->call_trigger(strtoupper($this->element) . '_MODIFY', $user);
  2125. if ($result < 0) {
  2126. $error++;
  2127. } //Do also here what you must do to rollback action if trigger fail
  2128. // End call triggers
  2129. }
  2130. // Commit or rollback
  2131. if ($error) {
  2132. $this->db->rollback();
  2133. return -1;
  2134. } else {
  2135. $this->db->commit();
  2136. return 1;
  2137. }
  2138. }
  2139. /**
  2140. * Change the payments methods
  2141. *
  2142. * @param int $id Id of new payment method
  2143. * @return int >0 if OK, <0 if KO
  2144. */
  2145. public function setPaymentMethods($id)
  2146. {
  2147. global $user;
  2148. $error = 0;
  2149. $notrigger = 0;
  2150. dol_syslog(get_class($this).'::setPaymentMethods('.$id.')');
  2151. if ($this->statut >= 0 || $this->element == 'societe') {
  2152. // TODO uniformize field name
  2153. $fieldname = 'fk_mode_reglement';
  2154. if ($this->element == 'societe') {
  2155. $fieldname = 'mode_reglement';
  2156. }
  2157. if (get_class($this) == 'Fournisseur') {
  2158. $fieldname = 'mode_reglement_supplier';
  2159. }
  2160. if (get_class($this) == 'Tva') {
  2161. $fieldname = 'fk_typepayment';
  2162. }
  2163. if (get_class($this) == 'Salary') {
  2164. $fieldname = 'fk_typepayment';
  2165. }
  2166. $sql = "UPDATE ".$this->db->prefix().$this->table_element;
  2167. $sql .= " SET ".$fieldname." = ".(($id > 0 || $id == '0') ? ((int) $id) : 'NULL');
  2168. $sql .= ' WHERE rowid='.((int) $this->id);
  2169. if ($this->db->query($sql)) {
  2170. $this->mode_reglement_id = $id;
  2171. // for supplier
  2172. if (get_class($this) == 'Fournisseur') {
  2173. $this->mode_reglement_supplier_id = $id;
  2174. }
  2175. // Triggers
  2176. if (!$error && !$notrigger) {
  2177. // Call triggers
  2178. if (get_class($this) == 'Commande') {
  2179. $result = $this->call_trigger('ORDER_MODIFY', $user);
  2180. } else {
  2181. $result = $this->call_trigger(strtoupper(get_class($this)).'_MODIFY', $user);
  2182. }
  2183. if ($result < 0) {
  2184. $error++;
  2185. }
  2186. // End call triggers
  2187. }
  2188. return 1;
  2189. } else {
  2190. dol_syslog(get_class($this).'::setPaymentMethods Error '.$this->db->error());
  2191. $this->error = $this->db->error();
  2192. return -1;
  2193. }
  2194. } else {
  2195. dol_syslog(get_class($this).'::setPaymentMethods, status of the object is incompatible');
  2196. $this->error = 'Status of the object is incompatible '.$this->statut;
  2197. return -2;
  2198. }
  2199. }
  2200. /**
  2201. * Change the multicurrency code
  2202. *
  2203. * @param string $code multicurrency code
  2204. * @return int >0 if OK, <0 if KO
  2205. */
  2206. public function setMulticurrencyCode($code)
  2207. {
  2208. dol_syslog(get_class($this).'::setMulticurrencyCode('.$code.')');
  2209. if ($this->statut >= 0 || $this->element == 'societe') {
  2210. $fieldname = 'multicurrency_code';
  2211. $sql = 'UPDATE '.$this->db->prefix().$this->table_element;
  2212. $sql .= " SET ".$fieldname." = '".$this->db->escape($code)."'";
  2213. $sql .= ' WHERE rowid='.((int) $this->id);
  2214. if ($this->db->query($sql)) {
  2215. $this->multicurrency_code = $code;
  2216. list($fk_multicurrency, $rate) = MultiCurrency::getIdAndTxFromCode($this->db, $code);
  2217. if ($rate) {
  2218. $this->setMulticurrencyRate($rate, 2);
  2219. }
  2220. return 1;
  2221. } else {
  2222. dol_syslog(get_class($this).'::setMulticurrencyCode Error '.$sql.' - '.$this->db->error());
  2223. $this->error = $this->db->error();
  2224. return -1;
  2225. }
  2226. } else {
  2227. dol_syslog(get_class($this).'::setMulticurrencyCode, status of the object is incompatible');
  2228. $this->error = 'Status of the object is incompatible '.$this->statut;
  2229. return -2;
  2230. }
  2231. }
  2232. /**
  2233. * Change the multicurrency rate
  2234. *
  2235. * @param double $rate multicurrency rate
  2236. * @param int $mode mode 1 : amounts in company currency will be recalculated, mode 2 : amounts in foreign currency will be recalculated
  2237. * @return int >0 if OK, <0 if KO
  2238. */
  2239. public function setMulticurrencyRate($rate, $mode = 1)
  2240. {
  2241. dol_syslog(get_class($this).'::setMulticurrencyRate('.$rate.','.$mode.')');
  2242. if ($this->statut >= 0 || $this->element == 'societe') {
  2243. $fieldname = 'multicurrency_tx';
  2244. $sql = 'UPDATE '.$this->db->prefix().$this->table_element;
  2245. $sql .= " SET ".$fieldname." = ".((float) $rate);
  2246. $sql .= ' WHERE rowid='.((int) $this->id);
  2247. if ($this->db->query($sql)) {
  2248. $this->multicurrency_tx = $rate;
  2249. // Update line price
  2250. if (!empty($this->lines)) {
  2251. foreach ($this->lines as &$line) {
  2252. // Amounts in company currency will be recalculated
  2253. if ($mode == 1) {
  2254. $line->subprice = 0;
  2255. }
  2256. // Amounts in foreign currency will be recalculated
  2257. if ($mode == 2) {
  2258. $line->multicurrency_subprice = 0;
  2259. }
  2260. switch ($this->element) {
  2261. case 'propal':
  2262. /** @var Propal $this */
  2263. /** @var PropaleLigne $line */
  2264. $this->updateline(
  2265. $line->id,
  2266. $line->subprice,
  2267. $line->qty,
  2268. $line->remise_percent,
  2269. $line->tva_tx,
  2270. $line->localtax1_tx,
  2271. $line->localtax2_tx,
  2272. ($line->description ? $line->description : $line->desc),
  2273. 'HT',
  2274. $line->info_bits,
  2275. $line->special_code,
  2276. $line->fk_parent_line,
  2277. $line->skip_update_total,
  2278. $line->fk_fournprice,
  2279. $line->pa_ht,
  2280. $line->label,
  2281. $line->product_type,
  2282. $line->date_start,
  2283. $line->date_end,
  2284. $line->array_options,
  2285. $line->fk_unit,
  2286. $line->multicurrency_subprice
  2287. );
  2288. break;
  2289. case 'commande':
  2290. /** @var Commande $this */
  2291. /** @var OrderLine $line */
  2292. $this->updateline(
  2293. $line->id,
  2294. ($line->description ? $line->description : $line->desc),
  2295. $line->subprice,
  2296. $line->qty,
  2297. $line->remise_percent,
  2298. $line->tva_tx,
  2299. $line->localtax1_tx,
  2300. $line->localtax2_tx,
  2301. 'HT',
  2302. $line->info_bits,
  2303. $line->date_start,
  2304. $line->date_end,
  2305. $line->product_type,
  2306. $line->fk_parent_line,
  2307. $line->skip_update_total,
  2308. $line->fk_fournprice,
  2309. $line->pa_ht,
  2310. $line->label,
  2311. $line->special_code,
  2312. $line->array_options,
  2313. $line->fk_unit,
  2314. $line->multicurrency_subprice
  2315. );
  2316. break;
  2317. case 'facture':
  2318. /** @var Facture $this */
  2319. /** @var FactureLigne $line */
  2320. $this->updateline(
  2321. $line->id,
  2322. ($line->description ? $line->description : $line->desc),
  2323. $line->subprice,
  2324. $line->qty,
  2325. $line->remise_percent,
  2326. $line->date_start,
  2327. $line->date_end,
  2328. $line->tva_tx,
  2329. $line->localtax1_tx,
  2330. $line->localtax2_tx,
  2331. 'HT',
  2332. $line->info_bits,
  2333. $line->product_type,
  2334. $line->fk_parent_line,
  2335. $line->skip_update_total,
  2336. $line->fk_fournprice,
  2337. $line->pa_ht,
  2338. $line->label,
  2339. $line->special_code,
  2340. $line->array_options,
  2341. $line->situation_percent,
  2342. $line->fk_unit,
  2343. $line->multicurrency_subprice
  2344. );
  2345. break;
  2346. case 'supplier_proposal':
  2347. /** @var SupplierProposal $this */
  2348. /** @var SupplierProposalLine $line */
  2349. $this->updateline(
  2350. $line->id,
  2351. $line->subprice,
  2352. $line->qty,
  2353. $line->remise_percent,
  2354. $line->tva_tx,
  2355. $line->localtax1_tx,
  2356. $line->localtax2_tx,
  2357. ($line->description ? $line->description : $line->desc),
  2358. 'HT',
  2359. $line->info_bits,
  2360. $line->special_code,
  2361. $line->fk_parent_line,
  2362. $line->skip_update_total,
  2363. $line->fk_fournprice,
  2364. $line->pa_ht,
  2365. $line->label,
  2366. $line->product_type,
  2367. $line->array_options,
  2368. $line->ref_fourn,
  2369. $line->multicurrency_subprice
  2370. );
  2371. break;
  2372. case 'order_supplier':
  2373. /** @var CommandeFournisseur $this */
  2374. /** @var CommandeFournisseurLigne $line */
  2375. $this->updateline(
  2376. $line->id,
  2377. ($line->description ? $line->description : $line->desc),
  2378. $line->subprice,
  2379. $line->qty,
  2380. $line->remise_percent,
  2381. $line->tva_tx,
  2382. $line->localtax1_tx,
  2383. $line->localtax2_tx,
  2384. 'HT',
  2385. $line->info_bits,
  2386. $line->product_type,
  2387. false,
  2388. $line->date_start,
  2389. $line->date_end,
  2390. $line->array_options,
  2391. $line->fk_unit,
  2392. $line->multicurrency_subprice,
  2393. $line->ref_supplier
  2394. );
  2395. break;
  2396. case 'invoice_supplier':
  2397. /** @var FactureFournisseur $this */
  2398. /** @var SupplierInvoiceLine $line */
  2399. $this->updateline(
  2400. $line->id,
  2401. ($line->description ? $line->description : $line->desc),
  2402. $line->subprice,
  2403. $line->tva_tx,
  2404. $line->localtax1_tx,
  2405. $line->localtax2_tx,
  2406. $line->qty,
  2407. 0,
  2408. 'HT',
  2409. $line->info_bits,
  2410. $line->product_type,
  2411. $line->remise_percent,
  2412. false,
  2413. $line->date_start,
  2414. $line->date_end,
  2415. $line->array_options,
  2416. $line->fk_unit,
  2417. $line->multicurrency_subprice,
  2418. $line->ref_supplier
  2419. );
  2420. break;
  2421. default:
  2422. dol_syslog(get_class($this).'::setMulticurrencyRate no updateline defined', LOG_DEBUG);
  2423. break;
  2424. }
  2425. }
  2426. }
  2427. return 1;
  2428. } else {
  2429. dol_syslog(get_class($this).'::setMulticurrencyRate Error '.$sql.' - '.$this->db->error());
  2430. $this->error = $this->db->error();
  2431. return -1;
  2432. }
  2433. } else {
  2434. dol_syslog(get_class($this).'::setMulticurrencyRate, status of the object is incompatible');
  2435. $this->error = 'Status of the object is incompatible '.$this->statut;
  2436. return -2;
  2437. }
  2438. }
  2439. /**
  2440. * Change the payments terms
  2441. *
  2442. * @param int $id Id of new payment terms
  2443. * @param string $deposit_percent % of deposit if needed by payment terms
  2444. * @return int >0 if OK, <0 if KO
  2445. */
  2446. public function setPaymentTerms($id, $deposit_percent = null)
  2447. {
  2448. dol_syslog(get_class($this).'::setPaymentTerms('.$id.', '.var_export($deposit_percent, true).')');
  2449. if ($this->statut >= 0 || $this->element == 'societe') {
  2450. // TODO uniformize field name
  2451. $fieldname = 'fk_cond_reglement';
  2452. if ($this->element == 'societe') {
  2453. $fieldname = 'cond_reglement';
  2454. }
  2455. if (get_class($this) == 'Fournisseur') {
  2456. $fieldname = 'cond_reglement_supplier';
  2457. }
  2458. if (empty($deposit_percent) || $deposit_percent < 0) {
  2459. $deposit_percent = getDictionaryValue('c_payment_term', 'deposit_percent', $id);
  2460. }
  2461. if ($deposit_percent > 100) {
  2462. $deposit_percent = 100;
  2463. }
  2464. $sql = 'UPDATE '.$this->db->prefix().$this->table_element;
  2465. $sql .= " SET ".$fieldname." = ".(($id > 0 || $id == '0') ? ((int) $id) : 'NULL');
  2466. if (in_array($this->table_element, array('propal', 'commande', 'societe'))) {
  2467. $sql .= " , deposit_percent = " . (empty($deposit_percent) ? 'NULL' : "'".$this->db->escape($deposit_percent)."'");
  2468. }
  2469. $sql .= ' WHERE rowid='.((int) $this->id);
  2470. if ($this->db->query($sql)) {
  2471. $this->cond_reglement_id = $id;
  2472. // for supplier
  2473. if (get_class($this) == 'Fournisseur') {
  2474. $this->cond_reglement_supplier_id = $id;
  2475. }
  2476. $this->cond_reglement = $id; // for compatibility
  2477. $this->deposit_percent = $deposit_percent;
  2478. return 1;
  2479. } else {
  2480. dol_syslog(get_class($this).'::setPaymentTerms Error '.$sql.' - '.$this->db->error());
  2481. $this->error = $this->db->error();
  2482. return -1;
  2483. }
  2484. } else {
  2485. dol_syslog(get_class($this).'::setPaymentTerms, status of the object is incompatible');
  2486. $this->error = 'Status of the object is incompatible '.$this->statut;
  2487. return -2;
  2488. }
  2489. }
  2490. /**
  2491. * Change the transport mode methods
  2492. *
  2493. * @param int $id Id of transport mode
  2494. * @return int >0 if OK, <0 if KO
  2495. */
  2496. public function setTransportMode($id)
  2497. {
  2498. dol_syslog(get_class($this).'::setTransportMode('.$id.')');
  2499. if ($this->statut >= 0 || $this->element == 'societe') {
  2500. $fieldname = 'fk_transport_mode';
  2501. if ($this->element == 'societe') {
  2502. $fieldname = 'transport_mode';
  2503. }
  2504. if (get_class($this) == 'Fournisseur') {
  2505. $fieldname = 'transport_mode_supplier';
  2506. }
  2507. $sql = 'UPDATE '.$this->db->prefix().$this->table_element;
  2508. $sql .= " SET ".$fieldname." = ".(($id > 0 || $id == '0') ? ((int) $id) : 'NULL');
  2509. $sql .= ' WHERE rowid='.((int) $this->id);
  2510. if ($this->db->query($sql)) {
  2511. $this->transport_mode_id = $id;
  2512. // for supplier
  2513. if (get_class($this) == 'Fournisseur') {
  2514. $this->transport_mode_supplier_id = $id;
  2515. }
  2516. return 1;
  2517. } else {
  2518. dol_syslog(get_class($this).'::setTransportMode Error '.$sql.' - '.$this->db->error());
  2519. $this->error = $this->db->error();
  2520. return -1;
  2521. }
  2522. } else {
  2523. dol_syslog(get_class($this).'::setTransportMode, status of the object is incompatible');
  2524. $this->error = 'Status of the object is incompatible '.$this->statut;
  2525. return -2;
  2526. }
  2527. }
  2528. /**
  2529. * Change the retained warranty payments terms
  2530. *
  2531. * @param int $id Id of new payment terms
  2532. * @return int >0 if OK, <0 if KO
  2533. */
  2534. public function setRetainedWarrantyPaymentTerms($id)
  2535. {
  2536. dol_syslog(get_class($this).'::setRetainedWarrantyPaymentTerms('.$id.')');
  2537. if ($this->statut >= 0 || $this->element == 'societe') {
  2538. $fieldname = 'retained_warranty_fk_cond_reglement';
  2539. $sql = 'UPDATE '.$this->db->prefix().$this->table_element;
  2540. $sql .= " SET ".$fieldname." = ".((int) $id);
  2541. $sql .= ' WHERE rowid='.((int) $this->id);
  2542. if ($this->db->query($sql)) {
  2543. $this->retained_warranty_fk_cond_reglement = $id;
  2544. return 1;
  2545. } else {
  2546. dol_syslog(get_class($this).'::setRetainedWarrantyPaymentTerms Error '.$sql.' - '.$this->db->error());
  2547. $this->error = $this->db->error();
  2548. return -1;
  2549. }
  2550. } else {
  2551. dol_syslog(get_class($this).'::setRetainedWarrantyPaymentTerms, status of the object is incompatible');
  2552. $this->error = 'Status of the object is incompatible '.$this->statut;
  2553. return -2;
  2554. }
  2555. }
  2556. /**
  2557. * Define delivery address
  2558. * @deprecated
  2559. *
  2560. * @param int $id Address id
  2561. * @return int Return integer <0 si ko, >0 si ok
  2562. */
  2563. public function setDeliveryAddress($id)
  2564. {
  2565. $fieldname = 'fk_delivery_address';
  2566. if ($this->element == 'delivery' || $this->element == 'shipping') {
  2567. $fieldname = 'fk_address';
  2568. }
  2569. $sql = "UPDATE ".$this->db->prefix().$this->table_element." SET ".$fieldname." = ".((int) $id);
  2570. $sql .= " WHERE rowid = ".((int) $this->id)." AND fk_statut = 0";
  2571. if ($this->db->query($sql)) {
  2572. $this->fk_delivery_address = $id;
  2573. return 1;
  2574. } else {
  2575. $this->error = $this->db->error();
  2576. dol_syslog(get_class($this).'::setDeliveryAddress Error '.$this->error);
  2577. return -1;
  2578. }
  2579. }
  2580. /**
  2581. * Change the shipping method
  2582. *
  2583. * @param int $shipping_method_id Id of shipping method
  2584. * @param bool $notrigger false=launch triggers after, true=disable triggers
  2585. * @param User $userused Object user
  2586. *
  2587. * @return int 1 if OK, 0 if KO
  2588. */
  2589. public function setShippingMethod($shipping_method_id, $notrigger = false, $userused = null)
  2590. {
  2591. global $user;
  2592. if (empty($userused)) {
  2593. $userused = $user;
  2594. }
  2595. $error = 0;
  2596. if (!$this->table_element) {
  2597. dol_syslog(get_class($this)."::setShippingMethod was called on objet with property table_element not defined", LOG_ERR);
  2598. return -1;
  2599. }
  2600. $this->db->begin();
  2601. if ($shipping_method_id < 0) {
  2602. $shipping_method_id = 'NULL';
  2603. }
  2604. dol_syslog(get_class($this).'::setShippingMethod('.$shipping_method_id.')');
  2605. $sql = "UPDATE ".$this->db->prefix().$this->table_element;
  2606. $sql .= " SET fk_shipping_method = ".((int) $shipping_method_id);
  2607. $sql .= " WHERE rowid=".((int) $this->id);
  2608. $resql = $this->db->query($sql);
  2609. if (!$resql) {
  2610. dol_syslog(get_class($this).'::setShippingMethod Error ', LOG_DEBUG);
  2611. $this->error = $this->db->lasterror();
  2612. $error++;
  2613. } else {
  2614. if (!$notrigger) {
  2615. // Call trigger
  2616. $this->context = array('shippingmethodupdate'=>1);
  2617. $result = $this->call_trigger(strtoupper(get_class($this)).'_MODIFY', $userused);
  2618. if ($result < 0) {
  2619. $error++;
  2620. }
  2621. // End call trigger
  2622. }
  2623. }
  2624. if ($error) {
  2625. $this->db->rollback();
  2626. return -1;
  2627. } else {
  2628. $this->shipping_method_id = ($shipping_method_id == 'NULL') ? null : $shipping_method_id;
  2629. $this->db->commit();
  2630. return 1;
  2631. }
  2632. }
  2633. /**
  2634. * Change the warehouse
  2635. *
  2636. * @param int $warehouse_id Id of warehouse
  2637. * @return int 1 if OK, 0 if KO
  2638. */
  2639. public function setWarehouse($warehouse_id)
  2640. {
  2641. if (!$this->table_element) {
  2642. dol_syslog(get_class($this)."::setWarehouse was called on objet with property table_element not defined", LOG_ERR);
  2643. return -1;
  2644. }
  2645. if ($warehouse_id < 0) {
  2646. $warehouse_id = 'NULL';
  2647. }
  2648. dol_syslog(get_class($this).'::setWarehouse('.$warehouse_id.')');
  2649. $sql = "UPDATE ".$this->db->prefix().$this->table_element;
  2650. $sql .= " SET fk_warehouse = ".((int) $warehouse_id);
  2651. $sql .= " WHERE rowid=".((int) $this->id);
  2652. if ($this->db->query($sql)) {
  2653. $this->warehouse_id = ($warehouse_id == 'NULL') ? null : $warehouse_id;
  2654. return 1;
  2655. } else {
  2656. dol_syslog(get_class($this).'::setWarehouse Error ', LOG_DEBUG);
  2657. $this->error = $this->db->error();
  2658. return 0;
  2659. }
  2660. }
  2661. /**
  2662. * Set last model used by doc generator
  2663. *
  2664. * @param User $user User object that make change
  2665. * @param string $modelpdf Modele name
  2666. * @return int Return integer <0 if KO, >0 if OK
  2667. */
  2668. public function setDocModel($user, $modelpdf)
  2669. {
  2670. if (!$this->table_element) {
  2671. dol_syslog(get_class($this)."::setDocModel was called on objet with property table_element not defined", LOG_ERR);
  2672. return -1;
  2673. }
  2674. $newmodelpdf = dol_trunc($modelpdf, 255);
  2675. $sql = "UPDATE ".$this->db->prefix().$this->table_element;
  2676. $sql .= " SET model_pdf = '".$this->db->escape($newmodelpdf)."'";
  2677. $sql .= " WHERE rowid = ".((int) $this->id);
  2678. dol_syslog(get_class($this)."::setDocModel", LOG_DEBUG);
  2679. $resql = $this->db->query($sql);
  2680. if ($resql) {
  2681. $this->model_pdf = $modelpdf;
  2682. return 1;
  2683. } else {
  2684. dol_print_error($this->db);
  2685. return 0;
  2686. }
  2687. }
  2688. /**
  2689. * Change the bank account
  2690. *
  2691. * @param int $fk_account Id of bank account
  2692. * @param bool $notrigger false=launch triggers after, true=disable triggers
  2693. * @param User $userused Object user
  2694. * @return int 1 if OK, 0 if KO
  2695. */
  2696. public function setBankAccount($fk_account, $notrigger = false, $userused = null)
  2697. {
  2698. global $user;
  2699. if (empty($userused)) {
  2700. $userused = $user;
  2701. }
  2702. $error = 0;
  2703. if (!$this->table_element) {
  2704. dol_syslog(get_class($this)."::setBankAccount was called on objet with property table_element not defined", LOG_ERR);
  2705. return -1;
  2706. }
  2707. $this->db->begin();
  2708. if ($fk_account < 0) {
  2709. $fk_account = 'NULL';
  2710. }
  2711. dol_syslog(get_class($this).'::setBankAccount('.$fk_account.')');
  2712. $sql = "UPDATE ".$this->db->prefix().$this->table_element;
  2713. $sql .= " SET fk_account = ".((int) $fk_account);
  2714. $sql .= " WHERE rowid=".((int) $this->id);
  2715. $resql = $this->db->query($sql);
  2716. if (!$resql) {
  2717. dol_syslog(get_class($this).'::setBankAccount Error '.$sql.' - '.$this->db->error());
  2718. $this->error = $this->db->lasterror();
  2719. $error++;
  2720. } else {
  2721. if (!$notrigger) {
  2722. // Call trigger
  2723. $this->context['bankaccountupdate'] = 1;
  2724. $triggerName = strtoupper(get_class($this)).'_MODIFY';
  2725. // Special cases
  2726. if ($triggerName == 'FACTUREREC_MODIFY') {
  2727. $triggerName = 'BILLREC_MODIFY';
  2728. }
  2729. $result = $this->call_trigger($triggerName, $userused);
  2730. if ($result < 0) {
  2731. $error++;
  2732. }
  2733. // End call trigger
  2734. }
  2735. }
  2736. if ($error) {
  2737. $this->db->rollback();
  2738. return -1;
  2739. } else {
  2740. $this->fk_account = ($fk_account == 'NULL') ? null : $fk_account;
  2741. $this->db->commit();
  2742. return 1;
  2743. }
  2744. }
  2745. // TODO: Move line related operations to CommonObjectLine?
  2746. // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
  2747. /**
  2748. * Save a new position (field rang) for details lines.
  2749. * You can choose to set position for lines with already a position or lines without any position defined.
  2750. *
  2751. * @param boolean $renum True to renum all already ordered lines, false to renum only not already ordered lines.
  2752. * @param string $rowidorder ASC or DESC
  2753. * @param boolean $fk_parent_line Table with fk_parent_line field or not
  2754. * @return int Return integer <0 if KO, >0 if OK
  2755. */
  2756. public function line_order($renum = false, $rowidorder = 'ASC', $fk_parent_line = true)
  2757. {
  2758. // phpcs:enable
  2759. if (!$this->table_element_line) {
  2760. dol_syslog(get_class($this)."::line_order was called on objet with property table_element_line not defined", LOG_ERR);
  2761. return -1;
  2762. }
  2763. if (!$this->fk_element) {
  2764. dol_syslog(get_class($this)."::line_order was called on objet with property fk_element not defined", LOG_ERR);
  2765. return -1;
  2766. }
  2767. $fieldposition = 'rang'; // @todo Rename 'rang' into 'position'
  2768. if (in_array($this->table_element_line, array('bom_bomline', 'ecm_files', 'emailcollector_emailcollectoraction', 'product_attribute_value'))) {
  2769. $fieldposition = 'position';
  2770. }
  2771. // Count number of lines to reorder (according to choice $renum)
  2772. $nl = 0;
  2773. $sql = "SELECT count(rowid) FROM ".$this->db->prefix().$this->table_element_line;
  2774. $sql .= " WHERE ".$this->fk_element." = ".((int) $this->id);
  2775. if (!$renum) {
  2776. $sql .= " AND " . $fieldposition . " = 0";
  2777. }
  2778. if ($renum) {
  2779. $sql .= " AND " . $fieldposition . " <> 0";
  2780. }
  2781. dol_syslog(get_class($this)."::line_order", LOG_DEBUG);
  2782. $resql = $this->db->query($sql);
  2783. if ($resql) {
  2784. $row = $this->db->fetch_row($resql);
  2785. $nl = $row[0];
  2786. } else {
  2787. dol_print_error($this->db);
  2788. }
  2789. if ($nl > 0) {
  2790. // The goal of this part is to reorder all lines, with all children lines sharing the same counter that parents.
  2791. $rows = array();
  2792. // We first search all lines that are parent lines (for multilevel details lines)
  2793. $sql = "SELECT rowid FROM ".$this->db->prefix().$this->table_element_line;
  2794. $sql .= " WHERE ".$this->fk_element." = ".((int) $this->id);
  2795. if ($fk_parent_line) {
  2796. $sql .= ' AND fk_parent_line IS NULL';
  2797. }
  2798. $sql .= " ORDER BY " . $fieldposition . " ASC, rowid " . $rowidorder;
  2799. dol_syslog(get_class($this)."::line_order search all parent lines", LOG_DEBUG);
  2800. $resql = $this->db->query($sql);
  2801. if ($resql) {
  2802. $i = 0;
  2803. $num = $this->db->num_rows($resql);
  2804. while ($i < $num) {
  2805. $row = $this->db->fetch_row($resql);
  2806. $rows[] = $row[0]; // Add parent line into array rows
  2807. $childrens = $this->getChildrenOfLine($row[0]);
  2808. if (!empty($childrens)) {
  2809. foreach ($childrens as $child) {
  2810. array_push($rows, $child);
  2811. }
  2812. }
  2813. $i++;
  2814. }
  2815. // Now we set a new number for each lines (parent and children with children included into parent tree)
  2816. if (!empty($rows)) {
  2817. foreach ($rows as $key => $row) {
  2818. $this->updateRangOfLine($row, ($key + 1));
  2819. }
  2820. }
  2821. } else {
  2822. dol_print_error($this->db);
  2823. }
  2824. }
  2825. return 1;
  2826. }
  2827. /**
  2828. * Get children of line
  2829. *
  2830. * @param int $id Id of parent line
  2831. * @param int $includealltree 0 = 1st level child, 1 = All level child
  2832. * @return array Array with list of children lines id
  2833. */
  2834. public function getChildrenOfLine($id, $includealltree = 0)
  2835. {
  2836. $fieldposition = 'rang'; // @todo Rename 'rang' into 'position'
  2837. if (in_array($this->table_element_line, array('bom_bomline', 'ecm_files', 'emailcollector_emailcollectoraction', 'product_attribute_value'))) {
  2838. $fieldposition = 'position';
  2839. }
  2840. $rows = array();
  2841. $sql = "SELECT rowid FROM ".$this->db->prefix().$this->table_element_line;
  2842. $sql .= " WHERE ".$this->fk_element." = ".((int) $this->id);
  2843. $sql .= ' AND fk_parent_line = '.((int) $id);
  2844. $sql .= " ORDER BY " . $fieldposition . " ASC";
  2845. dol_syslog(get_class($this)."::getChildrenOfLine search children lines for line ".$id, LOG_DEBUG);
  2846. $resql = $this->db->query($sql);
  2847. if ($resql) {
  2848. if ($this->db->num_rows($resql) > 0) {
  2849. while ($row = $this->db->fetch_row($resql)) {
  2850. $rows[] = $row[0];
  2851. if (!empty($includealltree)) {
  2852. $rows = array_merge($rows, $this->getChildrenOfLine($row[0]), $includealltree);
  2853. }
  2854. }
  2855. }
  2856. }
  2857. return $rows;
  2858. }
  2859. // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
  2860. /**
  2861. * Update a line to have a lower rank
  2862. *
  2863. * @param int $rowid Id of line
  2864. * @param boolean $fk_parent_line Table with fk_parent_line field or not
  2865. * @return void
  2866. */
  2867. public function line_up($rowid, $fk_parent_line = true)
  2868. {
  2869. // phpcs:enable
  2870. $this->line_order(false, 'ASC', $fk_parent_line);
  2871. // Get rang of line
  2872. $rang = $this->getRangOfLine($rowid);
  2873. // Update position of line
  2874. $this->updateLineUp($rowid, $rang);
  2875. }
  2876. // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
  2877. /**
  2878. * Update a line to have a higher rank
  2879. *
  2880. * @param int $rowid Id of line
  2881. * @param boolean $fk_parent_line Table with fk_parent_line field or not
  2882. * @return void
  2883. */
  2884. public function line_down($rowid, $fk_parent_line = true)
  2885. {
  2886. // phpcs:enable
  2887. $this->line_order(false, 'ASC', $fk_parent_line);
  2888. // Get rang of line
  2889. $rang = $this->getRangOfLine($rowid);
  2890. // Get max value for rang
  2891. $max = $this->line_max();
  2892. // Update position of line
  2893. $this->updateLineDown($rowid, $rang, $max);
  2894. }
  2895. /**
  2896. * Update position of line (rang)
  2897. *
  2898. * @param int $rowid Id of line
  2899. * @param int $rang Position
  2900. * @return int Return integer <0 if KO, >0 if OK
  2901. */
  2902. public function updateRangOfLine($rowid, $rang)
  2903. {
  2904. global $hookmanager;
  2905. $fieldposition = 'rang'; // @todo Rename 'rang' into 'position'
  2906. if (in_array($this->table_element_line, array('bom_bomline', 'ecm_files', 'emailcollector_emailcollectoraction', 'product_attribute_value'))) {
  2907. $fieldposition = 'position';
  2908. }
  2909. $sql = "UPDATE ".$this->db->prefix().$this->table_element_line." SET ".$fieldposition." = ".((int) $rang);
  2910. $sql .= ' WHERE rowid = '.((int) $rowid);
  2911. dol_syslog(get_class($this)."::updateRangOfLine", LOG_DEBUG);
  2912. if (!$this->db->query($sql)) {
  2913. dol_print_error($this->db);
  2914. return -1;
  2915. } else {
  2916. $parameters=array('rowid'=>$rowid, 'rang'=>$rang, 'fieldposition' => $fieldposition);
  2917. $action='';
  2918. $reshook = $hookmanager->executeHooks('afterRankOfLineUpdate', $parameters, $this, $action);
  2919. return 1;
  2920. }
  2921. }
  2922. // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
  2923. /**
  2924. * Update position of line with ajax (rang)
  2925. *
  2926. * @param array $rows Array of rows
  2927. * @return void
  2928. */
  2929. public function line_ajaxorder($rows)
  2930. {
  2931. // phpcs:enable
  2932. $num = count($rows);
  2933. for ($i = 0; $i < $num; $i++) {
  2934. $this->updateRangOfLine($rows[$i], ($i + 1));
  2935. }
  2936. }
  2937. /**
  2938. * Update position of line up (rang)
  2939. *
  2940. * @param int $rowid Id of line
  2941. * @param int $rang Position
  2942. * @return void
  2943. */
  2944. public function updateLineUp($rowid, $rang)
  2945. {
  2946. if ($rang > 1) {
  2947. $fieldposition = 'rang';
  2948. if (in_array($this->table_element_line, array('ecm_files', 'emailcollector_emailcollectoraction', 'product_attribute_value'))) {
  2949. $fieldposition = 'position';
  2950. }
  2951. $sql = "UPDATE ".$this->db->prefix().$this->table_element_line." SET ".$fieldposition." = ".((int) $rang);
  2952. $sql .= " WHERE ".$this->fk_element." = ".((int) $this->id);
  2953. $sql .= " AND " . $fieldposition . " = " . ((int) ($rang - 1));
  2954. if ($this->db->query($sql)) {
  2955. $sql = "UPDATE ".$this->db->prefix().$this->table_element_line." SET ".$fieldposition." = ".((int) ($rang - 1));
  2956. $sql .= ' WHERE rowid = '.((int) $rowid);
  2957. if (!$this->db->query($sql)) {
  2958. dol_print_error($this->db);
  2959. }
  2960. } else {
  2961. dol_print_error($this->db);
  2962. }
  2963. }
  2964. }
  2965. /**
  2966. * Update position of line down (rang)
  2967. *
  2968. * @param int $rowid Id of line
  2969. * @param int $rang Position
  2970. * @param int $max Max
  2971. * @return void
  2972. */
  2973. public function updateLineDown($rowid, $rang, $max)
  2974. {
  2975. if ($rang < $max) {
  2976. $fieldposition = 'rang';
  2977. if (in_array($this->table_element_line, array('ecm_files', 'emailcollector_emailcollectoraction', 'product_attribute_value'))) {
  2978. $fieldposition = 'position';
  2979. }
  2980. $sql = "UPDATE ".$this->db->prefix().$this->table_element_line." SET ".$fieldposition." = ".((int) $rang);
  2981. $sql .= " WHERE ".$this->fk_element." = ".((int) $this->id);
  2982. $sql .= " AND " . $fieldposition . " = " . ((int) ($rang + 1));
  2983. if ($this->db->query($sql)) {
  2984. $sql = "UPDATE ".$this->db->prefix().$this->table_element_line." SET ".$fieldposition." = ".((int) ($rang + 1));
  2985. $sql .= ' WHERE rowid = '.((int) $rowid);
  2986. if (!$this->db->query($sql)) {
  2987. dol_print_error($this->db);
  2988. }
  2989. } else {
  2990. dol_print_error($this->db);
  2991. }
  2992. }
  2993. }
  2994. /**
  2995. * Get position of line (rang)
  2996. *
  2997. * @param int $rowid Id of line
  2998. * @return int Value of rang in table of lines
  2999. */
  3000. public function getRangOfLine($rowid)
  3001. {
  3002. $fieldposition = 'rang';
  3003. if (in_array($this->table_element_line, array('ecm_files', 'emailcollector_emailcollectoraction', 'product_attribute_value'))) {
  3004. $fieldposition = 'position';
  3005. }
  3006. $sql = "SELECT " . $fieldposition . " FROM ".$this->db->prefix().$this->table_element_line;
  3007. $sql .= " WHERE rowid = ".((int) $rowid);
  3008. dol_syslog(get_class($this)."::getRangOfLine", LOG_DEBUG);
  3009. $resql = $this->db->query($sql);
  3010. if ($resql) {
  3011. $row = $this->db->fetch_row($resql);
  3012. return $row[0];
  3013. }
  3014. return 0;
  3015. }
  3016. /**
  3017. * Get rowid of the line relative to its position
  3018. *
  3019. * @param int $rang Rang value
  3020. * @return int Rowid of the line
  3021. */
  3022. public function getIdOfLine($rang)
  3023. {
  3024. $fieldposition = 'rang';
  3025. if (in_array($this->table_element_line, array('ecm_files', 'emailcollector_emailcollectoraction', 'product_attribute_value'))) {
  3026. $fieldposition = 'position';
  3027. }
  3028. $sql = "SELECT rowid FROM ".$this->db->prefix().$this->table_element_line;
  3029. $sql .= " WHERE ".$this->fk_element." = ".((int) $this->id);
  3030. $sql .= " AND " . $fieldposition . " = ".((int) $rang);
  3031. $resql = $this->db->query($sql);
  3032. if ($resql) {
  3033. $row = $this->db->fetch_row($resql);
  3034. return $row[0];
  3035. }
  3036. return 0;
  3037. }
  3038. // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
  3039. /**
  3040. * Get max value used for position of line (rang)
  3041. *
  3042. * @param int $fk_parent_line Parent line id
  3043. * @return int Max value of rang in table of lines
  3044. */
  3045. public function line_max($fk_parent_line = 0)
  3046. {
  3047. // phpcs:enable
  3048. $positionfield = 'rang';
  3049. if (in_array($this->table_element, array('bom_bom', 'product_attribute'))) {
  3050. $positionfield = 'position';
  3051. }
  3052. // Search the last rang with fk_parent_line
  3053. if ($fk_parent_line) {
  3054. $sql = "SELECT max(".$positionfield.") FROM ".$this->db->prefix().$this->table_element_line;
  3055. $sql .= " WHERE ".$this->fk_element." = ".((int) $this->id);
  3056. $sql .= " AND fk_parent_line = ".((int) $fk_parent_line);
  3057. dol_syslog(get_class($this)."::line_max", LOG_DEBUG);
  3058. $resql = $this->db->query($sql);
  3059. if ($resql) {
  3060. $row = $this->db->fetch_row($resql);
  3061. if (!empty($row[0])) {
  3062. return $row[0];
  3063. } else {
  3064. return $this->getRangOfLine($fk_parent_line);
  3065. }
  3066. }
  3067. } else {
  3068. // If not, search the last rang of element
  3069. $sql = "SELECT max(".$positionfield.") FROM ".$this->db->prefix().$this->table_element_line;
  3070. $sql .= " WHERE ".$this->fk_element." = ".((int) $this->id);
  3071. dol_syslog(get_class($this)."::line_max", LOG_DEBUG);
  3072. $resql = $this->db->query($sql);
  3073. if ($resql) {
  3074. $row = $this->db->fetch_row($resql);
  3075. return $row[0];
  3076. }
  3077. }
  3078. return 0;
  3079. }
  3080. // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
  3081. /**
  3082. * Update external ref of element
  3083. *
  3084. * @param string $ref_ext Update field ref_ext
  3085. * @return int Return integer <0 if KO, >0 if OK
  3086. */
  3087. public function update_ref_ext($ref_ext)
  3088. {
  3089. // phpcs:enable
  3090. if (!$this->table_element) {
  3091. dol_syslog(get_class($this)."::update_ref_ext was called on objet with property table_element not defined", LOG_ERR);
  3092. return -1;
  3093. }
  3094. $sql = "UPDATE ".$this->db->prefix().$this->table_element;
  3095. $sql .= " SET ref_ext = '".$this->db->escape($ref_ext)."'";
  3096. $sql .= " WHERE ".(isset($this->table_rowid) ? $this->table_rowid : 'rowid')." = ".((int) $this->id);
  3097. dol_syslog(get_class($this)."::update_ref_ext", LOG_DEBUG);
  3098. if ($this->db->query($sql)) {
  3099. $this->ref_ext = $ref_ext;
  3100. return 1;
  3101. } else {
  3102. $this->error = $this->db->error();
  3103. return -1;
  3104. }
  3105. }
  3106. // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
  3107. /**
  3108. * Update note of element
  3109. *
  3110. * @param string $note New value for note
  3111. * @param string $suffix '', '_public' or '_private'
  3112. * @param int $notrigger 1=Does not execute triggers, 0=execute triggers
  3113. * @return int Return integer <0 if KO, >0 if OK
  3114. */
  3115. public function update_note($note, $suffix = '', $notrigger = 0)
  3116. {
  3117. // phpcs:enable
  3118. global $user;
  3119. if (!$this->table_element) {
  3120. $this->error = 'update_note was called on objet with property table_element not defined';
  3121. dol_syslog(get_class($this)."::update_note was called on objet with property table_element not defined", LOG_ERR);
  3122. return -1;
  3123. }
  3124. if (!in_array($suffix, array('', '_public', '_private'))) {
  3125. $this->error = 'update_note Parameter suffix must be empty, \'_private\' or \'_public\'';
  3126. dol_syslog(get_class($this)."::update_note Parameter suffix must be empty, '_private' or '_public'", LOG_ERR);
  3127. return -2;
  3128. }
  3129. $newsuffix = $suffix;
  3130. // Special cas
  3131. if ($this->table_element == 'product' && $newsuffix == '_private') {
  3132. $newsuffix = '';
  3133. }
  3134. if (in_array($this->table_element, array('actioncomm', 'adherent', 'advtargetemailing', 'cronjob', 'establishment'))) {
  3135. $fieldusermod = "fk_user_mod";
  3136. } elseif ($this->table_element == 'ecm_files') {
  3137. $fieldusermod = "fk_user_m";
  3138. } else {
  3139. $fieldusermod = "fk_user_modif";
  3140. }
  3141. $sql = "UPDATE ".$this->db->prefix().$this->table_element;
  3142. $sql .= " SET note".$newsuffix." = ".(!empty($note) ? ("'".$this->db->escape($note)."'") : "NULL");
  3143. $sql .= ", ".$fieldusermod." = ".((int) $user->id);
  3144. $sql .= " WHERE rowid = ".((int) $this->id);
  3145. dol_syslog(get_class($this)."::update_note", LOG_DEBUG);
  3146. if ($this->db->query($sql)) {
  3147. if ($suffix == '_public') {
  3148. $this->note_public = $note;
  3149. } elseif ($suffix == '_private') {
  3150. $this->note_private = $note;
  3151. } else {
  3152. $this->note = $note; // deprecated
  3153. $this->note_private = $note;
  3154. }
  3155. if (empty($notrigger)) {
  3156. switch ($this->element) {
  3157. case 'societe':
  3158. $trigger_name = 'COMPANY_MODIFY';
  3159. break;
  3160. case 'commande':
  3161. $trigger_name = 'ORDER_MODIFY';
  3162. break;
  3163. case 'facture':
  3164. $trigger_name = 'BILL_MODIFY';
  3165. break;
  3166. case 'invoice_supplier':
  3167. $trigger_name = 'BILL_SUPPLIER_MODIFY';
  3168. break;
  3169. case 'facturerec':
  3170. $trigger_name = 'BILLREC_MODIFIY';
  3171. break;
  3172. case 'expensereport':
  3173. $trigger_name = 'EXPENSE_REPORT_MODIFY';
  3174. break;
  3175. default:
  3176. $trigger_name = strtoupper($this->element) . '_MODIFY';
  3177. }
  3178. $ret = $this->call_trigger($trigger_name, $user);
  3179. if ($ret < 0) {
  3180. return -1;
  3181. }
  3182. }
  3183. return 1;
  3184. } else {
  3185. $this->error = $this->db->lasterror();
  3186. return -1;
  3187. }
  3188. }
  3189. // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
  3190. /**
  3191. * Update public note (kept for backward compatibility)
  3192. *
  3193. * @param string $note New value for note
  3194. * @return int Return integer <0 if KO, >0 if OK
  3195. * @deprecated
  3196. * @see update_note()
  3197. */
  3198. public function update_note_public($note)
  3199. {
  3200. // phpcs:enable
  3201. return $this->update_note($note, '_public');
  3202. }
  3203. // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
  3204. /**
  3205. * Update total_ht, total_ttc, total_vat, total_localtax1, total_localtax2 for an object (sum of lines).
  3206. * Must be called at end of methods addline or updateline.
  3207. *
  3208. * @param int $exclspec >0 = Exclude special product (product_type=9)
  3209. * @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
  3210. * @param int $nodatabaseupdate 1=Do not update database total fields of the main object. Update only properties in memory. Can be used to save SQL when this method is called several times, so we can do it only once at end.
  3211. * @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 (used to analyze lines to check corrupted data).
  3212. * @return int Return integer <0 if KO, >0 if OK
  3213. */
  3214. public function update_price($exclspec = 0, $roundingadjust = 'none', $nodatabaseupdate = 0, $seller = null)
  3215. {
  3216. // phpcs:enable
  3217. global $conf, $hookmanager, $action;
  3218. $parameters = array('exclspec' => $exclspec, 'roundingadjust' => $roundingadjust, 'nodatabaseupdate' => $nodatabaseupdate, 'seller' => $seller);
  3219. $reshook = $hookmanager->executeHooks('updateTotalPrice', $parameters, $this, $action); // Note that $action and $object may have been modified by some hooks
  3220. if ($reshook > 0) {
  3221. return 1; // replacement code
  3222. } elseif ($reshook < 0) {
  3223. return -1; // failure
  3224. } // reshook = 0 => execute normal code
  3225. // Some external module want no update price after a trigger because they have another method to calculate the total (ex: with an extrafield)
  3226. $MODULE = "";
  3227. if ($this->element == 'propal') {
  3228. $MODULE = "MODULE_DISALLOW_UPDATE_PRICE_PROPOSAL";
  3229. } elseif ($this->element == 'commande' || $this->element == 'order') {
  3230. $MODULE = "MODULE_DISALLOW_UPDATE_PRICE_ORDER";
  3231. } elseif ($this->element == 'facture' || $this->element == 'invoice') {
  3232. $MODULE = "MODULE_DISALLOW_UPDATE_PRICE_INVOICE";
  3233. } elseif ($this->element == 'facture_fourn' || $this->element == 'supplier_invoice' || $this->element == 'invoice_supplier' || $this->element == 'invoice_supplier_rec') {
  3234. $MODULE = "MODULE_DISALLOW_UPDATE_PRICE_SUPPLIER_INVOICE";
  3235. } elseif ($this->element == 'order_supplier' || $this->element == 'supplier_order') {
  3236. $MODULE = "MODULE_DISALLOW_UPDATE_PRICE_SUPPLIER_ORDER";
  3237. } elseif ($this->element == 'supplier_proposal') {
  3238. $MODULE = "MODULE_DISALLOW_UPDATE_PRICE_SUPPLIER_PROPOSAL";
  3239. }
  3240. if (!empty($MODULE)) {
  3241. if (getDolGlobalString($MODULE)) {
  3242. $modsactivated = explode(',', getDolGlobalString($MODULE));
  3243. foreach ($modsactivated as $mod) {
  3244. if (isModEnabled($mod)) {
  3245. return 1; // update was disabled by specific setup
  3246. }
  3247. }
  3248. }
  3249. }
  3250. include_once DOL_DOCUMENT_ROOT.'/core/lib/price.lib.php';
  3251. $forcedroundingmode = $roundingadjust;
  3252. if ($forcedroundingmode == 'auto' && isset($conf->global->MAIN_ROUNDOFTOTAL_NOT_TOTALOFROUND)) {
  3253. $forcedroundingmode = getDolGlobalString('MAIN_ROUNDOFTOTAL_NOT_TOTALOFROUND');
  3254. } elseif ($forcedroundingmode == 'auto') {
  3255. $forcedroundingmode = '0';
  3256. }
  3257. $error = 0;
  3258. $multicurrency_tx = !empty($this->multicurrency_tx) ? $this->multicurrency_tx : 1;
  3259. // Define constants to find lines to sum (field name int the table_element_line not into table_element)
  3260. $fieldtva = 'total_tva';
  3261. $fieldlocaltax1 = 'total_localtax1';
  3262. $fieldlocaltax2 = 'total_localtax2';
  3263. $fieldup = 'subprice';
  3264. if ($this->element == 'facture_fourn' || $this->element == 'invoice_supplier') {
  3265. $fieldtva = 'tva';
  3266. $fieldup = 'pu_ht';
  3267. }
  3268. if ($this->element == 'invoice_supplier_rec') {
  3269. $fieldup = 'pu_ht';
  3270. }
  3271. if ($this->element == 'expensereport') {
  3272. $fieldup = 'value_unit';
  3273. }
  3274. $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,";
  3275. $sql .= ' tva_tx as vatrate, localtax1_tx, localtax2_tx, localtax1_type, localtax2_type, info_bits, product_type';
  3276. if ($this->table_element_line == 'facturedet') {
  3277. $sql .= ', situation_percent';
  3278. }
  3279. $sql .= ', multicurrency_total_ht, multicurrency_total_tva, multicurrency_total_ttc';
  3280. $sql .= " FROM ".$this->db->prefix().$this->table_element_line;
  3281. $sql .= " WHERE ".$this->fk_element." = ".((int) $this->id);
  3282. if ($exclspec) {
  3283. $product_field = 'product_type';
  3284. if ($this->table_element_line == 'contratdet') {
  3285. $product_field = ''; // contratdet table has no product_type field
  3286. }
  3287. if ($product_field) {
  3288. $sql .= " AND ".$product_field." <> 9";
  3289. }
  3290. }
  3291. $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
  3292. dol_syslog(get_class($this)."::update_price", LOG_DEBUG);
  3293. $resql = $this->db->query($sql);
  3294. if ($resql) {
  3295. $this->total_ht = 0;
  3296. $this->total_tva = 0;
  3297. $this->total_localtax1 = 0;
  3298. $this->total_localtax2 = 0;
  3299. $this->total_ttc = 0;
  3300. $total_ht_by_vats = array();
  3301. $total_tva_by_vats = array();
  3302. $total_ttc_by_vats = array();
  3303. $this->multicurrency_total_ht = 0;
  3304. $this->multicurrency_total_tva = 0;
  3305. $this->multicurrency_total_ttc = 0;
  3306. $this->db->begin();
  3307. $num = $this->db->num_rows($resql);
  3308. $i = 0;
  3309. while ($i < $num) {
  3310. $obj = $this->db->fetch_object($resql);
  3311. // Note: There is no check on detail line and no check on total, if $forcedroundingmode = 'none'
  3312. $parameters = array('fk_element' => $obj->rowid);
  3313. $reshook = $hookmanager->executeHooks('changeRoundingMode', $parameters, $this, $action); // Note that $action and $object may have been modified by some hooks
  3314. if (empty($reshook) && $forcedroundingmode == '0') { // Check if data on line are consistent. This may solve lines that were not consistent because set with $forcedroundingmode='auto'
  3315. // This part of code is to fix data. We should not call it too often.
  3316. $localtax_array = array($obj->localtax1_type, $obj->localtax1_tx, $obj->localtax2_type, $obj->localtax2_tx);
  3317. $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);
  3318. $diff_when_using_price_ht = price2num($tmpcal[1] - $obj->total_tva, 'MT', 1); // If price was set with tax price and unit price HT has a low number of digits, then we may have a diff on recalculation from unit price HT.
  3319. $diff_on_current_total = price2num($obj->total_ttc - $obj->total_ht - $obj->total_tva - $obj->total_localtax1 - $obj->total_localtax2, 'MT', 1);
  3320. //var_dump($obj->total_ht.' '.$obj->total_tva.' '.$obj->total_localtax1.' '.$obj->total_localtax2.' => '.$obj->total_ttc);
  3321. //var_dump($diff_when_using_price_ht.' '.$diff_on_current_total);
  3322. if ($diff_on_current_total) {
  3323. // This should not happen, we should always have in table: total_ttc = total_ht + total_vat + total_localtax1 + total_localtax2
  3324. $sqlfix = "UPDATE ".$this->db->prefix().$this->table_element_line." SET ".$fieldtva." = ".price2num((float) $tmpcal[1]).", total_ttc = ".price2num((float) $tmpcal[2])." WHERE rowid = ".((int) $obj->rowid);
  3325. dol_syslog('We found unconsistent data into detailed line (diff_on_current_total = '.$diff_on_current_total.') for line rowid = '.$obj->rowid." (ht=".$obj->total_ht." vat=".$obj->total_tva." tax1=".$obj->total_localtax1." tax2=".$obj->total_localtax2." ttc=".$obj->total_ttc."). We fix the total_vat and total_ttc of line by running sqlfix = ".$sqlfix, LOG_WARNING);
  3326. $resqlfix = $this->db->query($sqlfix);
  3327. if (!$resqlfix) {
  3328. dol_print_error($this->db, 'Failed to update line');
  3329. }
  3330. $obj->total_tva = $tmpcal[1];
  3331. $obj->total_ttc = $tmpcal[2];
  3332. } elseif ($diff_when_using_price_ht && $roundingadjust == '0') {
  3333. // After calculation from HT, total is consistent but we have found a difference between VAT part in calculation and into database and
  3334. // we ask to force the use of rounding on line (like done on calculation) so we force update of line
  3335. $sqlfix = "UPDATE ".$this->db->prefix().$this->table_element_line." SET ".$fieldtva." = ".price2num((float) $tmpcal[1]).", total_ttc = ".price2num((float) $tmpcal[2])." WHERE rowid = ".((int) $obj->rowid);
  3336. dol_syslog('We found a line with different rounding data into detailed line (diff_when_using_price_ht = '.$diff_when_using_price_ht.' and diff_on_current_total = '.$diff_on_current_total.') 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);
  3337. $resqlfix = $this->db->query($sqlfix);
  3338. if (!$resqlfix) {
  3339. dol_print_error($this->db, 'Failed to update line');
  3340. }
  3341. $obj->total_tva = $tmpcal[1];
  3342. $obj->total_ttc = $tmpcal[2];
  3343. }
  3344. }
  3345. $this->total_ht += $obj->total_ht; // The field visible at end of line detail
  3346. $this->total_tva += $obj->total_tva;
  3347. $this->total_localtax1 += $obj->total_localtax1;
  3348. $this->total_localtax2 += $obj->total_localtax2;
  3349. $this->total_ttc += $obj->total_ttc;
  3350. $this->multicurrency_total_ht += $obj->multicurrency_total_ht; // The field visible at end of line detail
  3351. $this->multicurrency_total_tva += $obj->multicurrency_total_tva;
  3352. $this->multicurrency_total_ttc += $obj->multicurrency_total_ttc;
  3353. if (!isset($total_ht_by_vats[$obj->vatrate])) {
  3354. $total_ht_by_vats[$obj->vatrate] = 0;
  3355. }
  3356. if (!isset($total_tva_by_vats[$obj->vatrate])) {
  3357. $total_tva_by_vats[$obj->vatrate] = 0;
  3358. }
  3359. if (!isset($total_ttc_by_vats[$obj->vatrate])) {
  3360. $total_ttc_by_vats[$obj->vatrate] = 0;
  3361. }
  3362. $total_ht_by_vats[$obj->vatrate] += $obj->total_ht;
  3363. $total_tva_by_vats[$obj->vatrate] += $obj->total_tva;
  3364. $total_ttc_by_vats[$obj->vatrate] += $obj->total_ttc;
  3365. if ($forcedroundingmode == '1') { // Check if we need adjustement onto line for vat. TODO This works on the company currency but not on foreign currency
  3366. $tmpvat = price2num($total_ht_by_vats[$obj->vatrate] * $obj->vatrate / 100, 'MT', 1);
  3367. $diff = price2num($total_tva_by_vats[$obj->vatrate] - $tmpvat, 'MT', 1);
  3368. //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";
  3369. if ($diff) {
  3370. if (abs($diff) > (10 * pow(10, -1 * getDolGlobalInt('MAIN_MAX_DECIMALS_TOT', 0)))) {
  3371. // If error is more than 10 times the accurancy of rounding. This should not happen.
  3372. $errmsg = 'A rounding difference was detected into TOTAL but is too high to be corrected. Some data in your lines may be corrupted. Try to edit each line manually to fix this before restarting.';
  3373. dol_syslog($errmsg, LOG_WARNING);
  3374. $this->error = $errmsg;
  3375. $error++;
  3376. break;
  3377. }
  3378. $sqlfix = "UPDATE ".$this->db->prefix().$this->table_element_line." SET ".$fieldtva." = ".price2num($obj->total_tva - $diff).", total_ttc = ".price2num($obj->total_ttc - $diff)." WHERE rowid = ".((int) $obj->rowid);
  3379. 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);
  3380. $resqlfix = $this->db->query($sqlfix);
  3381. if (!$resqlfix) {
  3382. dol_print_error($this->db, 'Failed to update line');
  3383. }
  3384. $this->total_tva = (float) price2num($this->total_tva - $diff, '', 1);
  3385. $this->total_ttc = (float) price2num($this->total_ttc - $diff, '', 1);
  3386. $total_tva_by_vats[$obj->vatrate] = (float) price2num($total_tva_by_vats[$obj->vatrate] - $diff, '', 1);
  3387. $total_ttc_by_vats[$obj->vatrate] = (float) price2num($total_ttc_by_vats[$obj->vatrate] - $diff, '', 1);
  3388. }
  3389. }
  3390. $i++;
  3391. }
  3392. // Add revenue stamp to total
  3393. $this->total_ttc += isset($this->revenuestamp) ? $this->revenuestamp : 0;
  3394. $this->multicurrency_total_ttc += isset($this->revenuestamp) ? ($this->revenuestamp * $multicurrency_tx) : 0;
  3395. // Situations totals
  3396. if (!empty($this->situation_cycle_ref) && !empty($this->situation_counter) && $this->situation_counter > 1 && method_exists($this, 'get_prev_sits')) {
  3397. include_once DOL_DOCUMENT_ROOT.'/compta/facture/class/facture.class.php';
  3398. if ($this->type != Facture::TYPE_CREDIT_NOTE) { // @phpstan-ignore-line
  3399. $prev_sits = $this->get_prev_sits();
  3400. foreach ($prev_sits as $sit) { // $sit is an object Facture loaded with a fetch.
  3401. $this->total_ht -= $sit->total_ht;
  3402. $this->total_tva -= $sit->total_tva;
  3403. $this->total_localtax1 -= $sit->total_localtax1;
  3404. $this->total_localtax2 -= $sit->total_localtax2;
  3405. $this->total_ttc -= $sit->total_ttc;
  3406. $this->multicurrency_total_ht -= $sit->multicurrency_total_ht;
  3407. $this->multicurrency_total_tva -= $sit->multicurrency_total_tva;
  3408. $this->multicurrency_total_ttc -= $sit->multicurrency_total_ttc;
  3409. }
  3410. }
  3411. }
  3412. // Clean total
  3413. $this->total_ht = (float) price2num($this->total_ht);
  3414. $this->total_tva = (float) price2num($this->total_tva);
  3415. $this->total_localtax1 = (float) price2num($this->total_localtax1);
  3416. $this->total_localtax2 = (float) price2num($this->total_localtax2);
  3417. $this->total_ttc = (float) price2num($this->total_ttc);
  3418. $this->db->free($resql);
  3419. // Now update global fields total_ht, total_ttc, total_tva, total_localtax1, total_localtax2, multicurrency_total_* of main object
  3420. $fieldht = 'total_ht';
  3421. $fieldtva = 'tva';
  3422. $fieldlocaltax1 = 'localtax1';
  3423. $fieldlocaltax2 = 'localtax2';
  3424. $fieldttc = 'total_ttc';
  3425. // Specific code for backward compatibility with old field names
  3426. if (in_array($this->element, array('propal', 'commande', 'facture', 'facturerec', 'supplier_proposal', 'order_supplier', 'facture_fourn', 'invoice_supplier', 'invoice_supplier_rec', 'expensereport'))) {
  3427. $fieldtva = 'total_tva';
  3428. }
  3429. if (!$error && empty($nodatabaseupdate)) {
  3430. $sql = "UPDATE ".$this->db->prefix().$this->table_element.' SET';
  3431. $sql .= " ".$fieldht." = ".((float) price2num($this->total_ht, 'MT', 1)).",";
  3432. $sql .= " ".$fieldtva." = ".((float) price2num($this->total_tva, 'MT', 1)).",";
  3433. $sql .= " ".$fieldlocaltax1." = ".((float) price2num($this->total_localtax1, 'MT', 1)).",";
  3434. $sql .= " ".$fieldlocaltax2." = ".((float) price2num($this->total_localtax2, 'MT', 1)).",";
  3435. $sql .= " ".$fieldttc." = ".((float) price2num($this->total_ttc, 'MT', 1));
  3436. $sql .= ", multicurrency_total_ht = ".((float) price2num($this->multicurrency_total_ht, 'MT', 1));
  3437. $sql .= ", multicurrency_total_tva = ".((float) price2num($this->multicurrency_total_tva, 'MT', 1));
  3438. $sql .= ", multicurrency_total_ttc = ".((float) price2num($this->multicurrency_total_ttc, 'MT', 1));
  3439. $sql .= " WHERE rowid = ".((int) $this->id);
  3440. dol_syslog(get_class($this)."::update_price", LOG_DEBUG);
  3441. $resql = $this->db->query($sql);
  3442. if (!$resql) {
  3443. $error++;
  3444. $this->error = $this->db->lasterror();
  3445. $this->errors[] = $this->db->lasterror();
  3446. }
  3447. }
  3448. if (!$error) {
  3449. $this->db->commit();
  3450. return 1;
  3451. } else {
  3452. $this->db->rollback();
  3453. return -1;
  3454. }
  3455. } else {
  3456. dol_print_error($this->db, 'Bad request in update_price');
  3457. return -1;
  3458. }
  3459. }
  3460. // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
  3461. /**
  3462. * Add an object link into llx_element_element.
  3463. *
  3464. * @param string $origin Linked element type
  3465. * @param int $origin_id Linked element id
  3466. * @param User $f_user User that create
  3467. * @param int $notrigger 1=Does not execute triggers, 0=execute triggers
  3468. * @return int Return integer <=0 if KO, >0 if OK
  3469. * @see fetchObjectLinked(), updateObjectLinked(), deleteObjectLinked()
  3470. */
  3471. public function add_object_linked($origin = null, $origin_id = null, $f_user = null, $notrigger = 0)
  3472. {
  3473. // phpcs:enable
  3474. global $user, $hookmanager, $action;
  3475. $origin = (!empty($origin) ? $origin : $this->origin);
  3476. $origin_id = (!empty($origin_id) ? $origin_id : $this->origin_id);
  3477. $f_user = isset($f_user) ? $f_user : $user;
  3478. // Special case
  3479. if ($origin == 'order') {
  3480. $origin = 'commande';
  3481. }
  3482. if ($origin == 'invoice') {
  3483. $origin = 'facture';
  3484. }
  3485. if ($origin == 'invoice_template') {
  3486. $origin = 'facturerec';
  3487. }
  3488. if ($origin == 'supplierorder') {
  3489. $origin = 'order_supplier';
  3490. }
  3491. // Elements of the core modules which have `$module` property but may to which we don't want to prefix module part to the element name for finding the linked object in llx_element_element.
  3492. // It's because an entry for this element may be exist in llx_element_element before this modification (version <=14.2) and ave named only with their element name in fk_source or fk_target.
  3493. $coremodule = array('knowledgemanagement', 'partnership', 'workstation', 'ticket', 'recruitment', 'eventorganization', 'asset');
  3494. // Add module part to target type if object has $module property and isn't in core modules.
  3495. $targettype = ((!empty($this->module) && ! in_array($this->module, $coremodule)) ? $this->module.'_' : '').$this->element;
  3496. $parameters = array('targettype'=>$targettype);
  3497. // Hook for explicitly set the targettype if it must be differtent than $this->element
  3498. $reshook = $hookmanager->executeHooks('setLinkedObjectSourceTargetType', $parameters, $this, $action); // Note that $action and $object may have been modified by some hooks
  3499. if ($reshook > 0) {
  3500. if (!empty($hookmanager->resArray['targettype'])) {
  3501. $targettype = $hookmanager->resArray['targettype'];
  3502. }
  3503. }
  3504. $this->db->begin();
  3505. $error = 0;
  3506. $sql = "INSERT INTO " . $this->db->prefix() . "element_element (";
  3507. $sql .= "fk_source";
  3508. $sql .= ", sourcetype";
  3509. $sql .= ", fk_target";
  3510. $sql .= ", targettype";
  3511. $sql .= ") VALUES (";
  3512. $sql .= ((int) $origin_id);
  3513. $sql .= ", '" . $this->db->escape($origin) . "'";
  3514. $sql .= ", " . ((int) $this->id);
  3515. $sql .= ", '" . $this->db->escape($targettype) . "'";
  3516. $sql .= ")";
  3517. dol_syslog(get_class($this) . "::add_object_linked", LOG_DEBUG);
  3518. if ($this->db->query($sql)) {
  3519. if (!$notrigger) {
  3520. // Call trigger
  3521. $this->context['link_origin'] = $origin;
  3522. $this->context['link_origin_id'] = $origin_id;
  3523. $result = $this->call_trigger('OBJECT_LINK_INSERT', $f_user);
  3524. if ($result < 0) {
  3525. $error++;
  3526. }
  3527. // End call triggers
  3528. }
  3529. } else {
  3530. $this->error = $this->db->lasterror();
  3531. $error++;
  3532. }
  3533. if (!$error) {
  3534. $this->db->commit();
  3535. return 1;
  3536. } else {
  3537. $this->db->rollback();
  3538. return 0;
  3539. }
  3540. }
  3541. /**
  3542. * Fetch array of objects linked to current object (object of enabled modules only). Links are loaded into
  3543. * this->linkedObjectsIds array +
  3544. * this->linkedObjects array if $loadalsoobjects = 1 or $loadalsoobjects = type
  3545. * Possible usage for parameters:
  3546. * - all parameters empty -> we look all link to current object (current object can be source or target)
  3547. * - source id+type -> will get list of targets linked to source
  3548. * - target id+type -> will get list of sources linked to target
  3549. * - source id+type + target type -> will get list of targets of the type linked to source
  3550. * - target id+type + source type -> will get list of sources of the type linked to target
  3551. *
  3552. * @param int $sourceid Object source id (if not defined, $this->id)
  3553. * @param string $sourcetype Object source type (if not defined, $this->element)
  3554. * @param int $targetid Object target id (if not defined, $this->id)
  3555. * @param string $targettype Object target type (if not defined, $this->element)
  3556. * @param string $clause 'OR' or 'AND' clause used when both source id and target id are provided
  3557. * @param int $alsosametype 0=Return only links to object that differs from source type. 1=Include also link to objects of same type.
  3558. * @param string $orderby SQL 'ORDER BY' clause
  3559. * @param int|string $loadalsoobjects Load also the array $this->linkedObjects. Use 0 to not load (increase performances), Use 1 to load all, Use value of type ('facture', 'facturerec', ...) to load only a type of object.
  3560. * @return int Return integer <0 if KO, >0 if OK
  3561. * @see add_object_linked(), updateObjectLinked(), deleteObjectLinked()
  3562. */
  3563. public function fetchObjectLinked($sourceid = null, $sourcetype = '', $targetid = null, $targettype = '', $clause = 'OR', $alsosametype = 1, $orderby = 'sourcetype', $loadalsoobjects = 1)
  3564. {
  3565. global $conf, $hookmanager, $action;
  3566. // Important for pdf generation time reduction
  3567. // This boolean is true if $this->linkedObjects has already been loaded with all objects linked without filter
  3568. // If you need to force the reload, you can call clearObjectLinkedCache() before calling fetchObjectLinked()
  3569. if ($this->id > 0 && !empty($this->linkedObjectsFullLoaded[$this->id])) {
  3570. return 1;
  3571. }
  3572. $this->linkedObjectsIds = array();
  3573. $this->linkedObjects = array();
  3574. $justsource = false;
  3575. $justtarget = false;
  3576. $withtargettype = false;
  3577. $withsourcetype = false;
  3578. $parameters = array('sourcetype'=>$sourcetype, 'sourceid'=>$sourceid, 'targettype'=>$targettype, 'targetid'=>$targetid);
  3579. // Hook for explicitly set the targettype if it must be differtent than $this->element
  3580. $reshook = $hookmanager->executeHooks('setLinkedObjectSourceTargetType', $parameters, $this, $action); // Note that $action and $object may have been modified by some hooks
  3581. if ($reshook > 0) {
  3582. if (!empty($hookmanager->resArray['sourcetype'])) {
  3583. $sourcetype = $hookmanager->resArray['sourcetype'];
  3584. }
  3585. if (!empty($hookmanager->resArray['sourceid'])) {
  3586. $sourceid = $hookmanager->resArray['sourceid'];
  3587. }
  3588. if (!empty($hookmanager->resArray['targettype'])) {
  3589. $targettype = $hookmanager->resArray['targettype'];
  3590. }
  3591. if (!empty($hookmanager->resArray['targetid'])) {
  3592. $targetid = $hookmanager->resArray['targetid'];
  3593. }
  3594. }
  3595. if (!empty($sourceid) && !empty($sourcetype) && empty($targetid)) {
  3596. $justsource = true; // the source (id and type) is a search criteria
  3597. if (!empty($targettype)) {
  3598. $withtargettype = true;
  3599. }
  3600. }
  3601. if (!empty($targetid) && !empty($targettype) && empty($sourceid)) {
  3602. $justtarget = true; // the target (id and type) is a search criteria
  3603. if (!empty($sourcetype)) {
  3604. $withsourcetype = true;
  3605. }
  3606. }
  3607. $sourceid = (!empty($sourceid) ? $sourceid : $this->id);
  3608. $targetid = (!empty($targetid) ? $targetid : $this->id);
  3609. $sourcetype = (!empty($sourcetype) ? $sourcetype : $this->element);
  3610. $targettype = (!empty($targettype) ? $targettype : $this->element);
  3611. /*if (empty($sourceid) && empty($targetid))
  3612. {
  3613. dol_syslog('Bad usage of function. No source nor target id defined (nor as parameter nor as object id)', LOG_ERR);
  3614. return -1;
  3615. }*/
  3616. // Links between objects are stored in table element_element
  3617. $sql = "SELECT rowid, fk_source, sourcetype, fk_target, targettype";
  3618. $sql .= " FROM ".$this->db->prefix()."element_element";
  3619. $sql .= " WHERE ";
  3620. if ($justsource || $justtarget) {
  3621. if ($justsource) {
  3622. $sql .= "fk_source = ".((int) $sourceid)." AND sourcetype = '".$this->db->escape($sourcetype)."'";
  3623. if ($withtargettype) {
  3624. $sql .= " AND targettype = '".$this->db->escape($targettype)."'";
  3625. }
  3626. } elseif ($justtarget) {
  3627. $sql .= "fk_target = ".((int) $targetid)." AND targettype = '".$this->db->escape($targettype)."'";
  3628. if ($withsourcetype) {
  3629. $sql .= " AND sourcetype = '".$this->db->escape($sourcetype)."'";
  3630. }
  3631. }
  3632. } else {
  3633. $sql .= "(fk_source = ".((int) $sourceid)." AND sourcetype = '".$this->db->escape($sourcetype)."')";
  3634. $sql .= " ".$clause." (fk_target = ".((int) $targetid)." AND targettype = '".$this->db->escape($targettype)."')";
  3635. if ($loadalsoobjects && $this->id > 0 && $sourceid == $this->id && $sourcetype == $this->element && $targetid == $this->id && $targettype == $this->element && $clause == 'OR') {
  3636. $this->linkedObjectsFullLoaded[$this->id] = true;
  3637. }
  3638. }
  3639. $sql .= " ORDER BY ".$orderby;
  3640. dol_syslog(get_class($this)."::fetchObjectLink", LOG_DEBUG);
  3641. $resql = $this->db->query($sql);
  3642. if ($resql) {
  3643. $num = $this->db->num_rows($resql);
  3644. $i = 0;
  3645. while ($i < $num) {
  3646. $obj = $this->db->fetch_object($resql);
  3647. if ($justsource || $justtarget) {
  3648. if ($justsource) {
  3649. $this->linkedObjectsIds[$obj->targettype][$obj->rowid] = $obj->fk_target;
  3650. } elseif ($justtarget) {
  3651. $this->linkedObjectsIds[$obj->sourcetype][$obj->rowid] = $obj->fk_source;
  3652. }
  3653. } else {
  3654. if ($obj->fk_source == $sourceid && $obj->sourcetype == $sourcetype) {
  3655. $this->linkedObjectsIds[$obj->targettype][$obj->rowid] = $obj->fk_target;
  3656. }
  3657. if ($obj->fk_target == $targetid && $obj->targettype == $targettype) {
  3658. $this->linkedObjectsIds[$obj->sourcetype][$obj->rowid] = $obj->fk_source;
  3659. }
  3660. }
  3661. $i++;
  3662. }
  3663. if (!empty($this->linkedObjectsIds)) {
  3664. $tmparray = $this->linkedObjectsIds;
  3665. foreach ($tmparray as $objecttype => $objectids) { // $objecttype is a module name ('facture', 'mymodule', ...) or a module name with a suffix ('project_task', 'mymodule_myobj', ...)
  3666. // Parse element/subelement (ex: project_task, cabinetmed_consultation, ...)
  3667. $module = $element = $subelement = $objecttype;
  3668. $regs = array();
  3669. if ($objecttype != 'supplier_proposal' && $objecttype != 'order_supplier' && $objecttype != 'invoice_supplier'
  3670. && preg_match('/^([^_]+)_([^_]+)/i', $objecttype, $regs)) {
  3671. $module = $element = $regs[1];
  3672. $subelement = $regs[2];
  3673. }
  3674. $classpath = $element.'/class';
  3675. // To work with non standard classpath or module name
  3676. if ($objecttype == 'facture') {
  3677. $classpath = 'compta/facture/class';
  3678. } elseif ($objecttype == 'facturerec') {
  3679. $classpath = 'compta/facture/class';
  3680. $module = 'facture';
  3681. } elseif ($objecttype == 'propal') {
  3682. $classpath = 'comm/propal/class';
  3683. } elseif ($objecttype == 'supplier_proposal') {
  3684. $classpath = 'supplier_proposal/class';
  3685. } elseif ($objecttype == 'shipping') {
  3686. $classpath = 'expedition/class';
  3687. $subelement = 'expedition';
  3688. $module = 'expedition';
  3689. } elseif ($objecttype == 'delivery') {
  3690. $classpath = 'delivery/class';
  3691. $subelement = 'delivery';
  3692. $module = 'delivery_note';
  3693. } elseif ($objecttype == 'invoice_supplier' || $objecttype == 'order_supplier') {
  3694. $classpath = 'fourn/class';
  3695. $module = 'fournisseur';
  3696. } elseif ($objecttype == 'fichinter') {
  3697. $classpath = 'fichinter/class';
  3698. $subelement = 'fichinter';
  3699. $module = 'ficheinter';
  3700. } elseif ($objecttype == 'subscription') {
  3701. $classpath = 'adherents/class';
  3702. $module = 'adherent';
  3703. } elseif ($objecttype == 'contact') {
  3704. $module = 'societe';
  3705. }
  3706. // Set classfile
  3707. $classfile = strtolower($subelement);
  3708. $classname = ucfirst($subelement);
  3709. if ($objecttype == 'order') {
  3710. $classfile = 'commande';
  3711. $classname = 'Commande';
  3712. } elseif ($objecttype == 'invoice_supplier') {
  3713. $classfile = 'fournisseur.facture';
  3714. $classname = 'FactureFournisseur';
  3715. } elseif ($objecttype == 'order_supplier') {
  3716. $classfile = 'fournisseur.commande';
  3717. $classname = 'CommandeFournisseur';
  3718. } elseif ($objecttype == 'supplier_proposal') {
  3719. $classfile = 'supplier_proposal';
  3720. $classname = 'SupplierProposal';
  3721. } elseif ($objecttype == 'facturerec') {
  3722. $classfile = 'facture-rec';
  3723. $classname = 'FactureRec';
  3724. } elseif ($objecttype == 'subscription') {
  3725. $classfile = 'subscription';
  3726. $classname = 'Subscription';
  3727. } elseif ($objecttype == 'project' || $objecttype == 'projet') {
  3728. $classpath = 'projet/class';
  3729. $classfile = 'project';
  3730. $classname = 'Project';
  3731. } elseif ($objecttype == 'conferenceorboothattendee') {
  3732. $classpath = 'eventorganization/class';
  3733. $classfile = 'conferenceorboothattendee';
  3734. $classname = 'ConferenceOrBoothAttendee';
  3735. $module = 'eventorganization';
  3736. } elseif ($objecttype == 'conferenceorbooth') {
  3737. $classpath = 'eventorganization/class';
  3738. $classfile = 'conferenceorbooth';
  3739. $classname = 'ConferenceOrBooth';
  3740. $module = 'eventorganization';
  3741. } elseif ($objecttype == 'mo') {
  3742. $classpath = 'mrp/class';
  3743. $classfile = 'mo';
  3744. $classname = 'Mo';
  3745. $module = 'mrp';
  3746. }
  3747. // Here $module, $classfile and $classname are set, we can use them.
  3748. if (isModEnabled($module) && (($element != $this->element) || $alsosametype)) {
  3749. if ($loadalsoobjects && (is_numeric($loadalsoobjects) || ($loadalsoobjects === $objecttype))) {
  3750. dol_include_once('/'.$classpath.'/'.$classfile.'.class.php');
  3751. //print '/'.$classpath.'/'.$classfile.'.class.php '.class_exists($classname);
  3752. if (class_exists($classname)) {
  3753. foreach ($objectids as $i => $objectid) { // $i is rowid into llx_element_element
  3754. $object = new $classname($this->db);
  3755. $ret = $object->fetch($objectid);
  3756. if ($ret >= 0) {
  3757. $this->linkedObjects[$objecttype][$i] = $object;
  3758. }
  3759. }
  3760. }
  3761. }
  3762. } else {
  3763. unset($this->linkedObjectsIds[$objecttype]);
  3764. }
  3765. }
  3766. }
  3767. return 1;
  3768. } else {
  3769. dol_print_error($this->db);
  3770. return -1;
  3771. }
  3772. }
  3773. /**
  3774. * Clear the cache saying that all linked object were already loaded. So next fetchObjectLinked will reload all links.
  3775. *
  3776. * @return int Return integer <0 if KO, >0 if OK
  3777. * @see fetchObjectLinked()
  3778. */
  3779. public function clearObjectLinkedCache()
  3780. {
  3781. if ($this->id > 0 && !empty($this->linkedObjectsFullLoaded[$this->id])) {
  3782. unset($this->linkedObjectsFullLoaded[$this->id]);
  3783. }
  3784. return 1;
  3785. }
  3786. /**
  3787. * Update object linked of a current object
  3788. *
  3789. * @param int $sourceid Object source id
  3790. * @param string $sourcetype Object source type
  3791. * @param int $targetid Object target id
  3792. * @param string $targettype Object target type
  3793. * @param User $f_user User that create
  3794. * @param int $notrigger 1=Does not execute triggers, 0= execute triggers
  3795. * @return int >0 if OK, <0 if KO
  3796. * @see add_object_linked(), fetObjectLinked(), deleteObjectLinked()
  3797. */
  3798. public function updateObjectLinked($sourceid = null, $sourcetype = '', $targetid = null, $targettype = '', $f_user = null, $notrigger = 0)
  3799. {
  3800. global $user;
  3801. $updatesource = false;
  3802. $updatetarget = false;
  3803. $f_user = isset($f_user) ? $f_user : $user;
  3804. if (!empty($sourceid) && !empty($sourcetype) && empty($targetid) && empty($targettype)) {
  3805. $updatesource = true;
  3806. } elseif (empty($sourceid) && empty($sourcetype) && !empty($targetid) && !empty($targettype)) {
  3807. $updatetarget = true;
  3808. }
  3809. $this->db->begin();
  3810. $error = 0;
  3811. $sql = "UPDATE " . $this->db->prefix() . "element_element SET ";
  3812. if ($updatesource) {
  3813. $sql .= "fk_source = " . ((int) $sourceid);
  3814. $sql .= ", sourcetype = '" . $this->db->escape($sourcetype) . "'";
  3815. $sql .= " WHERE fk_target = " . ((int) $this->id);
  3816. $sql .= " AND targettype = '" . $this->db->escape($this->element) . "'";
  3817. } elseif ($updatetarget) {
  3818. $sql .= "fk_target = " . ((int) $targetid);
  3819. $sql .= ", targettype = '" . $this->db->escape($targettype) . "'";
  3820. $sql .= " WHERE fk_source = " . ((int) $this->id);
  3821. $sql .= " AND sourcetype = '" . $this->db->escape($this->element) . "'";
  3822. }
  3823. dol_syslog(get_class($this) . "::updateObjectLinked", LOG_DEBUG);
  3824. if ($this->db->query($sql)) {
  3825. if (!$notrigger) {
  3826. // Call trigger
  3827. $this->context['link_source_id'] = $sourceid;
  3828. $this->context['link_source_type'] = $sourcetype;
  3829. $this->context['link_target_id'] = $targetid;
  3830. $this->context['link_target_type'] = $targettype;
  3831. $result = $this->call_trigger('OBJECT_LINK_MODIFY', $f_user);
  3832. if ($result < 0) {
  3833. $error++;
  3834. }
  3835. // End call triggers
  3836. }
  3837. } else {
  3838. $this->error = $this->db->lasterror();
  3839. $error++;
  3840. }
  3841. if (!$error) {
  3842. $this->db->commit();
  3843. return 1;
  3844. } else {
  3845. $this->db->rollback();
  3846. return -1;
  3847. }
  3848. }
  3849. /**
  3850. * Delete all links between an object $this
  3851. *
  3852. * @param int $sourceid Object source id
  3853. * @param string $sourcetype Object source type
  3854. * @param int $targetid Object target id
  3855. * @param string $targettype Object target type
  3856. * @param int $rowid Row id of line to delete. If defined, other parameters are not used.
  3857. * @param User $f_user User that create
  3858. * @param int $notrigger 1=Does not execute triggers, 0= execute triggers
  3859. * @return int >0 if OK, <0 if KO
  3860. * @see add_object_linked(), updateObjectLinked(), fetchObjectLinked()
  3861. */
  3862. public function deleteObjectLinked($sourceid = null, $sourcetype = '', $targetid = null, $targettype = '', $rowid = 0, $f_user = null, $notrigger = 0)
  3863. {
  3864. global $user;
  3865. $deletesource = false;
  3866. $deletetarget = false;
  3867. $f_user = isset($f_user) ? $f_user : $user;
  3868. if (!empty($sourceid) && !empty($sourcetype) && empty($targetid) && empty($targettype)) {
  3869. $deletesource = true;
  3870. } elseif (empty($sourceid) && empty($sourcetype) && !empty($targetid) && !empty($targettype)) {
  3871. $deletetarget = true;
  3872. }
  3873. $sourceid = (!empty($sourceid) ? $sourceid : $this->id);
  3874. $sourcetype = (!empty($sourcetype) ? $sourcetype : $this->element);
  3875. $targetid = (!empty($targetid) ? $targetid : $this->id);
  3876. $targettype = (!empty($targettype) ? $targettype : $this->element);
  3877. $this->db->begin();
  3878. $error = 0;
  3879. if (!$notrigger) {
  3880. // Call trigger
  3881. $this->context['link_id'] = $rowid;
  3882. $this->context['link_source_id'] = $sourceid;
  3883. $this->context['link_source_type'] = $sourcetype;
  3884. $this->context['link_target_id'] = $targetid;
  3885. $this->context['link_target_type'] = $targettype;
  3886. $result = $this->call_trigger('OBJECT_LINK_DELETE', $f_user);
  3887. if ($result < 0) {
  3888. $error++;
  3889. }
  3890. // End call triggers
  3891. }
  3892. if (!$error) {
  3893. $sql = "DELETE FROM " . $this->db->prefix() . "element_element";
  3894. $sql .= " WHERE";
  3895. if ($rowid > 0) {
  3896. $sql .= " rowid = " . ((int) $rowid);
  3897. } else {
  3898. if ($deletesource) {
  3899. $sql .= " fk_source = " . ((int) $sourceid) . " AND sourcetype = '" . $this->db->escape($sourcetype) . "'";
  3900. $sql .= " AND fk_target = " . ((int) $this->id) . " AND targettype = '" . $this->db->escape($this->element) . "'";
  3901. } elseif ($deletetarget) {
  3902. $sql .= " fk_target = " . ((int) $targetid) . " AND targettype = '" . $this->db->escape($targettype) . "'";
  3903. $sql .= " AND fk_source = " . ((int) $this->id) . " AND sourcetype = '" . $this->db->escape($this->element) . "'";
  3904. } else {
  3905. $sql .= " (fk_source = " . ((int) $this->id) . " AND sourcetype = '" . $this->db->escape($this->element) . "')";
  3906. $sql .= " OR";
  3907. $sql .= " (fk_target = " . ((int) $this->id) . " AND targettype = '" . $this->db->escape($this->element) . "')";
  3908. }
  3909. }
  3910. dol_syslog(get_class($this) . "::deleteObjectLinked", LOG_DEBUG);
  3911. if (!$this->db->query($sql)) {
  3912. $this->error = $this->db->lasterror();
  3913. $this->errors[] = $this->error;
  3914. $error++;
  3915. }
  3916. }
  3917. if (!$error) {
  3918. $this->db->commit();
  3919. return 1;
  3920. } else {
  3921. $this->db->rollback();
  3922. return 0;
  3923. }
  3924. }
  3925. /**
  3926. * Function used to get an array with all items linked to an object id in association table
  3927. *
  3928. * @param int $fk_object_where id of object we need to get linked items
  3929. * @param string $field_select name of field we need to get a list
  3930. * @param string $field_where name of field of object we need to get linked items
  3931. * @param string $table_element name of association table
  3932. * @return array|int Array of record, -1 if empty
  3933. */
  3934. public static function getAllItemsLinkedByObjectID($fk_object_where, $field_select, $field_where, $table_element)
  3935. {
  3936. if (empty($fk_object_where) || empty($field_where) || empty($table_element)) {
  3937. return -1;
  3938. }
  3939. if (!preg_match('/^[_a-zA-Z0-9]+$/', $field_select)) {
  3940. dol_syslog('Invalid value $field_select for parameter '.$field_select.' in call to getAllItemsLinkedByObjectID(). Must be a single field name.', LOG_ERR);
  3941. }
  3942. global $db;
  3943. $sql = "SELECT ".$field_select." FROM ".$db->prefix().$table_element." WHERE ".$field_where." = ".((int) $fk_object_where);
  3944. $resql = $db->query($sql);
  3945. $TRes = array();
  3946. if (!empty($resql)) {
  3947. while ($res = $db->fetch_object($resql)) {
  3948. $TRes[] = $res->{$field_select};
  3949. }
  3950. }
  3951. return $TRes;
  3952. }
  3953. /**
  3954. * Count items linked to an object id in association table
  3955. *
  3956. * @param int $fk_object_where id of object we need to get linked items
  3957. * @param string $field_where name of field of object we need to get linked items
  3958. * @param string $table_element name of association table
  3959. * @return array|int Array of record, -1 if empty
  3960. */
  3961. public static function getCountOfItemsLinkedByObjectID($fk_object_where, $field_where, $table_element)
  3962. {
  3963. if (empty($fk_object_where) || empty($field_where) || empty($table_element)) {
  3964. return -1;
  3965. }
  3966. global $db;
  3967. $sql = "SELECT COUNT(*) as nb FROM ".$db->prefix().$table_element." WHERE ".$field_where." = ".((int) $fk_object_where);
  3968. $resql = $db->query($sql);
  3969. $n = 0;
  3970. if ($resql) {
  3971. $res = $db->fetch_object($resql);
  3972. if ($res) {
  3973. $n = $res->nb;
  3974. }
  3975. }
  3976. return $n;
  3977. }
  3978. /**
  3979. * Function used to remove all items linked to an object id in association table
  3980. *
  3981. * @param int $fk_object_where id of object we need to remove linked items
  3982. * @param string $field_where name of field of object we need to delete linked items
  3983. * @param string $table_element name of association table
  3984. * @return int Return integer <0 if KO, 0 if nothing done, >0 if OK and something done
  3985. */
  3986. public static function deleteAllItemsLinkedByObjectID($fk_object_where, $field_where, $table_element)
  3987. {
  3988. if (empty($fk_object_where) || empty($field_where) || empty($table_element)) {
  3989. return -1;
  3990. }
  3991. global $db;
  3992. $sql = "DELETE FROM ".$db->prefix().$table_element." WHERE ".$field_where." = ".((int) $fk_object_where);
  3993. $resql = $db->query($sql);
  3994. if (empty($resql)) {
  3995. return 0;
  3996. }
  3997. return 1;
  3998. }
  3999. /**
  4000. * Set status of an object.
  4001. *
  4002. * @param int $status Status to set
  4003. * @param int $elementId Id of element to force (use this->id by default if null)
  4004. * @param string $elementType Type of element to force (use this->table_element by default)
  4005. * @param string $trigkey Trigger key to use for trigger. Use '' means automatic but it is not recommended and is deprecated.
  4006. * @param string $fieldstatus Name of status field in this->table_element
  4007. * @return int Return integer <0 if KO, >0 if OK
  4008. */
  4009. public function setStatut($status, $elementId = null, $elementType = '', $trigkey = '', $fieldstatus = 'fk_statut')
  4010. {
  4011. global $user, $langs, $conf;
  4012. $savElementId = $elementId; // To be used later to know if we were using the method using the id of this or not.
  4013. $elementId = (!empty($elementId) ? $elementId : $this->id);
  4014. $elementTable = (!empty($elementType) ? $elementType : $this->table_element);
  4015. $this->db->begin();
  4016. if ($elementTable == 'facture_rec') {
  4017. $fieldstatus = "suspended";
  4018. }
  4019. if ($elementTable == 'mailing') {
  4020. $fieldstatus = "statut";
  4021. }
  4022. if ($elementTable == 'cronjob') {
  4023. $fieldstatus = "status";
  4024. }
  4025. if ($elementTable == 'user') {
  4026. $fieldstatus = "statut";
  4027. }
  4028. if ($elementTable == 'expensereport') {
  4029. $fieldstatus = "fk_statut";
  4030. }
  4031. if ($elementTable == 'commande_fournisseur_dispatch') {
  4032. $fieldstatus = "status";
  4033. }
  4034. if ($elementTable == 'prelevement_bons') {
  4035. $fieldstatus = "statut";
  4036. }
  4037. if (isset($this->fields) && is_array($this->fields) && array_key_exists('status', $this->fields)) {
  4038. $fieldstatus = 'status';
  4039. }
  4040. $sql = "UPDATE ".$this->db->prefix().$elementTable;
  4041. $sql .= " SET ".$fieldstatus." = ".((int) $status);
  4042. // If status = 1 = validated, update also fk_user_valid
  4043. // TODO Replace the test on $elementTable by doing a test on existence of the field in $this->fields
  4044. if ($status == 1 && in_array($elementTable, array('expensereport', 'inventory'))) {
  4045. $sql .= ", fk_user_valid = ".((int) $user->id);
  4046. }
  4047. if ($status == 1 && in_array($elementTable, array('expensereport'))) {
  4048. $sql .= ", date_valid = '".$this->db->idate(dol_now())."'";
  4049. }
  4050. if ($status == 1 && in_array($elementTable, array('inventory'))) {
  4051. $sql .= ", date_validation = '".$this->db->idate(dol_now())."'";
  4052. }
  4053. $sql .= " WHERE rowid = ".((int) $elementId);
  4054. $sql .= " AND ".$fieldstatus." <> ".((int) $status); // We avoid update if status already correct
  4055. dol_syslog(get_class($this)."::setStatut", LOG_DEBUG);
  4056. $resql = $this->db->query($sql);
  4057. if ($resql) {
  4058. $error = 0;
  4059. $nb_rows_affected = $this->db->affected_rows($resql); // should be 1 or 0 if status was already correct
  4060. if ($nb_rows_affected > 0) {
  4061. if (empty($trigkey)) {
  4062. // Try to guess trigkey (for backward compatibility, now we should have trigkey defined into the call of setStatus)
  4063. if ($this->element == 'supplier_proposal' && $status == 2) {
  4064. $trigkey = 'SUPPLIER_PROPOSAL_SIGN'; // 2 = SupplierProposal::STATUS_SIGNED. Can't use constant into this generic class
  4065. }
  4066. if ($this->element == 'supplier_proposal' && $status == 3) {
  4067. $trigkey = 'SUPPLIER_PROPOSAL_REFUSE'; // 3 = SupplierProposal::STATUS_REFUSED. Can't use constant into this generic class
  4068. }
  4069. if ($this->element == 'supplier_proposal' && $status == 4) {
  4070. $trigkey = 'SUPPLIER_PROPOSAL_CLOSE'; // 4 = SupplierProposal::STATUS_CLOSED. Can't use constant into this generic class
  4071. }
  4072. if ($this->element == 'fichinter' && $status == 3) {
  4073. $trigkey = 'FICHINTER_CLASSIFY_DONE';
  4074. }
  4075. if ($this->element == 'fichinter' && $status == 2) {
  4076. $trigkey = 'FICHINTER_CLASSIFY_BILLED';
  4077. }
  4078. if ($this->element == 'fichinter' && $status == 1) {
  4079. $trigkey = 'FICHINTER_CLASSIFY_UNBILLED';
  4080. }
  4081. }
  4082. if ($trigkey) {
  4083. // Call trigger
  4084. $result = $this->call_trigger($trigkey, $user);
  4085. if ($result < 0) {
  4086. $error++;
  4087. }
  4088. // End call triggers
  4089. }
  4090. } else {
  4091. // The status was probably already good. We do nothing more, no triggers.
  4092. }
  4093. if (!$error) {
  4094. $this->db->commit();
  4095. if (empty($savElementId)) {
  4096. // If the element we update is $this (so $elementId was provided as null)
  4097. if ($fieldstatus == 'tosell') {
  4098. $this->status = $status;
  4099. } elseif ($fieldstatus == 'tobuy') {
  4100. $this->status_buy = $status; // @phpstan-ignore-line
  4101. } else {
  4102. $this->statut = $status;
  4103. $this->status = $status;
  4104. }
  4105. }
  4106. return 1;
  4107. } else {
  4108. $this->db->rollback();
  4109. dol_syslog(get_class($this)."::setStatut ".$this->error, LOG_ERR);
  4110. return -1;
  4111. }
  4112. } else {
  4113. $this->error = $this->db->lasterror();
  4114. $this->db->rollback();
  4115. return -1;
  4116. }
  4117. }
  4118. /**
  4119. * Load type of canvas of an object if it exists
  4120. *
  4121. * @param int $id Record id
  4122. * @param string $ref Record ref
  4123. * @return int Return integer <0 if KO, 0 if nothing done, >0 if OK
  4124. */
  4125. public function getCanvas($id = 0, $ref = '')
  4126. {
  4127. global $conf;
  4128. if (empty($id) && empty($ref)) {
  4129. return 0;
  4130. }
  4131. if (getDolGlobalString('MAIN_DISABLE_CANVAS')) {
  4132. return 0; // To increase speed. Not enabled by default.
  4133. }
  4134. // Clean parameters
  4135. $ref = trim($ref);
  4136. $sql = "SELECT rowid, canvas";
  4137. $sql .= " FROM ".$this->db->prefix().$this->table_element;
  4138. $sql .= " WHERE entity IN (".getEntity($this->element).")";
  4139. if (!empty($id)) {
  4140. $sql .= " AND rowid = ".((int) $id);
  4141. }
  4142. if (!empty($ref)) {
  4143. $sql .= " AND ref = '".$this->db->escape($ref)."'";
  4144. }
  4145. $resql = $this->db->query($sql);
  4146. if ($resql) {
  4147. $obj = $this->db->fetch_object($resql);
  4148. if ($obj) {
  4149. $this->canvas = $obj->canvas;
  4150. return 1;
  4151. } else {
  4152. return 0;
  4153. }
  4154. } else {
  4155. dol_print_error($this->db);
  4156. return -1;
  4157. }
  4158. }
  4159. /**
  4160. * Get special code of a line
  4161. *
  4162. * @param int $lineid Id of line
  4163. * @return int Special code
  4164. */
  4165. public function getSpecialCode($lineid)
  4166. {
  4167. $sql = "SELECT special_code FROM ".$this->db->prefix().$this->table_element_line;
  4168. $sql .= " WHERE rowid = ".((int) $lineid);
  4169. $resql = $this->db->query($sql);
  4170. if ($resql) {
  4171. $row = $this->db->fetch_row($resql);
  4172. return $row[0];
  4173. }
  4174. return 0;
  4175. }
  4176. /**
  4177. * Function to check if an object is used by others (by children).
  4178. * Check is done into this->childtables. There is no check into llx_element_element.
  4179. *
  4180. * @param int $id Force id of object
  4181. * @param int $entity Force entity to check
  4182. * @return int Return integer <0 if KO, 0 if not used, >0 if already used
  4183. */
  4184. public function isObjectUsed($id = 0, $entity = 0)
  4185. {
  4186. global $langs;
  4187. if (empty($id)) {
  4188. $id = $this->id;
  4189. }
  4190. // Check parameters
  4191. if (!isset($this->childtables) || !is_array($this->childtables) || count($this->childtables) == 0) {
  4192. dol_print_error('Called isObjectUsed on a class with property this->childtables not defined');
  4193. return -1;
  4194. }
  4195. $arraytoscan = $this->childtables; // array('tablename'=>array('fk_element'=>'parentfield'), ...) or array('tablename'=>array('parent'=>table_parent, 'parentkey'=>'nameoffieldforparentfkkey'), ...)
  4196. // For backward compatibility, we check if array is old format array('tablename1', 'tablename2', ...)
  4197. $tmparray = array_keys($this->childtables);
  4198. if (is_numeric($tmparray[0])) {
  4199. $arraytoscan = array_flip($this->childtables);
  4200. }
  4201. // Test if child exists
  4202. $haschild = 0;
  4203. foreach ($arraytoscan as $table => $element) {
  4204. //print $id.'-'.$table.'-'.$elementname.'<br>';
  4205. // Check if element can be deleted
  4206. $sql = "SELECT COUNT(*) as nb";
  4207. $sql.= " FROM ".$this->db->prefix().$table." as c";
  4208. if (!empty($element['parent']) && !empty($element['parentkey'])) {
  4209. $sql.= ", ".$this->db->prefix().$element['parent']." as p";
  4210. }
  4211. if (!empty($element['fk_element'])) {
  4212. $sql.= " WHERE c.".$element['fk_element']." = ".((int) $id);
  4213. } else {
  4214. $sql.= " WHERE c.".$this->fk_element." = ".((int) $id);
  4215. }
  4216. if (!empty($element['parent']) && !empty($element['parentkey'])) {
  4217. $sql.= " AND c.".$element['parentkey']." = p.rowid";
  4218. }
  4219. if (!empty($element['parent']) && !empty($element['parenttypefield']) && !empty($element['parenttypevalue'])) {
  4220. $sql.= " AND c.".$element['parenttypefield']." = '".$this->db->escape($element['parenttypevalue'])."'";
  4221. }
  4222. if (!empty($entity)) {
  4223. if (!empty($element['parent']) && !empty($element['parentkey'])) {
  4224. $sql.= " AND p.entity = ".((int) $entity);
  4225. } else {
  4226. $sql.= " AND c.entity = ".((int) $entity);
  4227. }
  4228. }
  4229. $resql = $this->db->query($sql);
  4230. if ($resql) {
  4231. $obj = $this->db->fetch_object($resql);
  4232. if ($obj->nb > 0) {
  4233. $langs->load("errors");
  4234. //print 'Found into table '.$table.', type '.$langs->transnoentitiesnoconv($elementname).', haschild='.$haschild;
  4235. $haschild += $obj->nb;
  4236. if (is_numeric($element)) { // very old usage array('table1', 'table2', ...)
  4237. $this->errors[] = $langs->transnoentitiesnoconv("ErrorRecordHasAtLeastOneChildOfType", method_exists($this, 'getNomUrl') ? $this->getNomUrl() : $this->ref, $table);
  4238. } elseif (is_string($element)) { // old usage array('table1' => 'TranslateKey1', 'table2' => 'TranslateKey2', ...)
  4239. $this->errors[] = $langs->transnoentitiesnoconv("ErrorRecordHasAtLeastOneChildOfType", method_exists($this, 'getNomUrl') ? $this->getNomUrl() : $this->ref, $langs->transnoentitiesnoconv($element));
  4240. } else { // new usage: $element['name']=Translation key
  4241. $this->errors[] = $langs->transnoentitiesnoconv("ErrorRecordHasAtLeastOneChildOfType", method_exists($this, 'getNomUrl') ? $this->getNomUrl() : $this->ref, $langs->transnoentitiesnoconv($element['name']));
  4242. }
  4243. break; // We found at least one, we stop here
  4244. }
  4245. } else {
  4246. $this->errors[] = $this->db->lasterror();
  4247. return -1;
  4248. }
  4249. }
  4250. if ($haschild > 0) {
  4251. $this->errors[] = "ErrorRecordHasChildren";
  4252. return $haschild;
  4253. } else {
  4254. return 0;
  4255. }
  4256. }
  4257. /**
  4258. * Function to say how many lines object contains
  4259. *
  4260. * @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
  4261. * @return int Return integer <0 if KO, 0 if no predefined products, nb of lines with predefined products if found
  4262. */
  4263. public function hasProductsOrServices($predefined = -1)
  4264. {
  4265. $nb = 0;
  4266. foreach ($this->lines as $key => $val) {
  4267. $qualified = 0;
  4268. if ($predefined == -1) {
  4269. $qualified = 1;
  4270. }
  4271. if ($predefined == 1 && $val->fk_product > 0) {
  4272. $qualified = 1;
  4273. }
  4274. if ($predefined == 0 && $val->fk_product <= 0) {
  4275. $qualified = 1;
  4276. }
  4277. if ($predefined == 2 && $val->fk_product > 0 && $val->product_type == 0) {
  4278. $qualified = 1;
  4279. }
  4280. if ($predefined == 3 && $val->fk_product > 0 && $val->product_type == 1) {
  4281. $qualified = 1;
  4282. }
  4283. if ($qualified) {
  4284. $nb++;
  4285. }
  4286. }
  4287. dol_syslog(get_class($this).'::hasProductsOrServices we found '.$nb.' qualified lines of products/servcies');
  4288. return $nb;
  4289. }
  4290. /**
  4291. * Function that returns the total amount HT of discounts applied for all lines.
  4292. *
  4293. * @return float|string Total amout of discount
  4294. */
  4295. public function getTotalDiscount()
  4296. {
  4297. if (!empty($this->table_element_line)) {
  4298. $total_discount = 0.00;
  4299. $sql = "SELECT subprice as pu_ht, qty, remise_percent, total_ht";
  4300. $sql .= " FROM ".$this->db->prefix().$this->table_element_line;
  4301. $sql .= " WHERE ".$this->fk_element." = ".((int) $this->id);
  4302. dol_syslog(get_class($this).'::getTotalDiscount', LOG_DEBUG);
  4303. $resql = $this->db->query($sql);
  4304. if ($resql) {
  4305. $num = $this->db->num_rows($resql);
  4306. $i = 0;
  4307. while ($i < $num) {
  4308. $obj = $this->db->fetch_object($resql);
  4309. $pu_ht = $obj->pu_ht;
  4310. $qty = $obj->qty;
  4311. $total_ht = $obj->total_ht;
  4312. $total_discount_line = (float) price2num(($pu_ht * $qty) - $total_ht, 'MT');
  4313. $total_discount += $total_discount_line;
  4314. $i++;
  4315. }
  4316. }
  4317. //print $total_discount; exit;
  4318. return price2num($total_discount);
  4319. }
  4320. return null;
  4321. }
  4322. /**
  4323. * Return into unit=0, the calculated total of weight and volume of all lines * qty
  4324. * Calculate by adding weight and volume of each product line, so properties ->volume/volume_units/weight/weight_units must be loaded on line.
  4325. *
  4326. * @return array array('weight'=>...,'volume'=>...)
  4327. */
  4328. public function getTotalWeightVolume()
  4329. {
  4330. $totalWeight = 0;
  4331. $totalVolume = 0;
  4332. // defined for shipment only
  4333. $totalOrdered = '';
  4334. // defined for shipment only
  4335. $totalToShip = '';
  4336. foreach ($this->lines as $line) {
  4337. if (isset($line->qty_asked)) {
  4338. if (empty($totalOrdered)) {
  4339. $totalOrdered = 0; // Avoid warning because $totalOrdered is ''
  4340. }
  4341. $totalOrdered += $line->qty_asked; // defined for shipment only
  4342. }
  4343. if (isset($line->qty_shipped)) {
  4344. if (empty($totalToShip)) {
  4345. $totalToShip = 0; // Avoid warning because $totalToShip is ''
  4346. }
  4347. $totalToShip += $line->qty_shipped; // defined for shipment only
  4348. } elseif ($line->element == 'commandefournisseurdispatch' && isset($line->qty)) {
  4349. if (empty($totalToShip)) {
  4350. $totalToShip = 0;
  4351. }
  4352. $totalToShip += $line->qty; // defined for reception only
  4353. }
  4354. // Define qty, weight, volume, weight_units, volume_units
  4355. if ($this->element == 'shipping') {
  4356. // for shipments
  4357. $qty = $line->qty_shipped ? $line->qty_shipped : 0;
  4358. } else {
  4359. $qty = $line->qty ? $line->qty : 0;
  4360. }
  4361. $weight = !empty($line->weight) ? $line->weight : 0;
  4362. ($weight == 0 && !empty($line->product->weight)) ? $weight = $line->product->weight : 0;
  4363. $volume = !empty($line->volume) ? $line->volume : 0;
  4364. ($volume == 0 && !empty($line->product->volume)) ? $volume = $line->product->volume : 0;
  4365. $weight_units = !empty($line->weight_units) ? $line->weight_units : 0;
  4366. ($weight_units == 0 && !empty($line->product->weight_units)) ? $weight_units = $line->product->weight_units : 0;
  4367. $volume_units = !empty($line->volume_units) ? $line->volume_units : 0;
  4368. ($volume_units == 0 && !empty($line->product->volume_units)) ? $volume_units = $line->product->volume_units : 0;
  4369. $weightUnit = 0;
  4370. $volumeUnit = 0;
  4371. if (!empty($weight_units)) {
  4372. $weightUnit = $weight_units;
  4373. }
  4374. if (!empty($volume_units)) {
  4375. $volumeUnit = $volume_units;
  4376. }
  4377. if (empty($totalWeight)) {
  4378. $totalWeight = 0; // Avoid warning because $totalWeight is ''
  4379. }
  4380. if (empty($totalVolume)) {
  4381. $totalVolume = 0; // Avoid warning because $totalVolume is ''
  4382. }
  4383. //var_dump($line->volume_units);
  4384. if ($weight_units < 50) { // < 50 means a standard unit (power of 10 of official unit), > 50 means an exotic unit (like inch)
  4385. $trueWeightUnit = pow(10, $weightUnit);
  4386. $totalWeight += $weight * $qty * $trueWeightUnit;
  4387. } else {
  4388. if ($weight_units == 99) {
  4389. // conversion 1 Pound = 0.45359237 KG
  4390. $trueWeightUnit = 0.45359237;
  4391. $totalWeight += $weight * $qty * $trueWeightUnit;
  4392. } elseif ($weight_units == 98) {
  4393. // conversion 1 Ounce = 0.0283495 KG
  4394. $trueWeightUnit = 0.0283495;
  4395. $totalWeight += $weight * $qty * $trueWeightUnit;
  4396. } else {
  4397. $totalWeight += $weight * $qty; // This may be wrong if we mix different units
  4398. }
  4399. }
  4400. if ($volume_units < 50) { // >50 means a standard unit (power of 10 of official unit), > 50 means an exotic unit (like inch)
  4401. //print $line->volume."x".$line->volume_units."x".($line->volume_units < 50)."x".$volumeUnit;
  4402. $trueVolumeUnit = pow(10, $volumeUnit);
  4403. //print $line->volume;
  4404. $totalVolume += $volume * $qty * $trueVolumeUnit;
  4405. } else {
  4406. $totalVolume += $volume * $qty; // This may be wrong if we mix different units
  4407. }
  4408. }
  4409. return array('weight'=>$totalWeight, 'volume'=>$totalVolume, 'ordered'=>$totalOrdered, 'toship'=>$totalToShip);
  4410. }
  4411. /**
  4412. * Set extra parameters
  4413. *
  4414. * @return int Return integer <0 if KO, >0 if OK
  4415. */
  4416. public function setExtraParameters()
  4417. {
  4418. $this->db->begin();
  4419. $extraparams = (!empty($this->extraparams) ? json_encode($this->extraparams) : null);
  4420. $sql = "UPDATE ".$this->db->prefix().$this->table_element;
  4421. $sql .= " SET extraparams = ".(!empty($extraparams) ? "'".$this->db->escape($extraparams)."'" : "null");
  4422. $sql .= " WHERE rowid = ".((int) $this->id);
  4423. dol_syslog(get_class($this)."::setExtraParameters", LOG_DEBUG);
  4424. $resql = $this->db->query($sql);
  4425. if (!$resql) {
  4426. $this->error = $this->db->lasterror();
  4427. $this->db->rollback();
  4428. return -1;
  4429. } else {
  4430. $this->db->commit();
  4431. return 1;
  4432. }
  4433. }
  4434. // --------------------
  4435. // TODO: All functions here must be redesigned and moved as they are not business functions but output functions
  4436. // --------------------
  4437. /* This is to show add lines */
  4438. /**
  4439. * Show add free and predefined products/services form
  4440. *
  4441. * @param int $dateSelector 1=Show also date range input fields
  4442. * @param Societe $seller Object thirdparty who sell
  4443. * @param Societe $buyer Object thirdparty who buy
  4444. * @param string $defaulttpldir Directory where to find the template
  4445. * @return void
  4446. */
  4447. public function formAddObjectLine($dateSelector, $seller, $buyer, $defaulttpldir = '/core/tpl')
  4448. {
  4449. global $conf, $user, $langs, $object, $hookmanager, $extrafields, $form;
  4450. // Line extrafield
  4451. if (!is_object($extrafields)) {
  4452. require_once DOL_DOCUMENT_ROOT.'/core/class/extrafields.class.php';
  4453. $extrafields = new ExtraFields($this->db);
  4454. }
  4455. $extrafields->fetch_name_optionals_label($this->table_element_line);
  4456. // Output template part (modules that overwrite templates must declare this into descriptor)
  4457. // Use global variables + $dateSelector + $seller and $buyer
  4458. // Note: This is deprecated. If you need to overwrite the tpl file, use instead the hook 'formAddObjectLine'.
  4459. $dirtpls = array_merge($conf->modules_parts['tpl'], array($defaulttpldir));
  4460. foreach ($dirtpls as $module => $reldir) {
  4461. if (!empty($module)) {
  4462. $tpl = dol_buildpath($reldir.'/objectline_create.tpl.php');
  4463. } else {
  4464. $tpl = DOL_DOCUMENT_ROOT.$reldir.'/objectline_create.tpl.php';
  4465. }
  4466. if (empty($conf->file->strict_mode)) {
  4467. $res = @include $tpl;
  4468. } else {
  4469. $res = include $tpl; // for debug
  4470. }
  4471. if ($res) {
  4472. break;
  4473. }
  4474. }
  4475. }
  4476. /* This is to show array of line of details */
  4477. /**
  4478. * Return HTML table for object lines
  4479. * TODO Move this into an output class file (htmlline.class.php)
  4480. * If lines are into a template, title must also be into a template
  4481. * But for the moment we don't know if it's possible as we keep a method available on overloaded objects.
  4482. *
  4483. * @param string $action Action code
  4484. * @param Societe $seller Object of seller third party
  4485. * @param Societe $buyer Object of buyer third party
  4486. * @param int $selected ID line selected
  4487. * @param int $dateSelector 1=Show also date range input fields
  4488. * @param string $defaulttpldir Directory where to find the template
  4489. * @return void
  4490. */
  4491. public function printObjectLines($action, $seller, $buyer, $selected = 0, $dateSelector = 0, $defaulttpldir = '/core/tpl')
  4492. {
  4493. global $conf, $hookmanager, $langs, $user, $form, $extrafields, $object;
  4494. // TODO We should not use global var for this
  4495. global $inputalsopricewithtax, $usemargins, $disableedit, $disablemove, $disableremove, $outputalsopricetotalwithtax;
  4496. // Define usemargins
  4497. $usemargins = 0;
  4498. if (isModEnabled('margin') && !empty($this->element) && in_array($this->element, array('facture', 'facturerec', 'propal', 'commande'))) {
  4499. $usemargins = 1;
  4500. }
  4501. $num = count($this->lines);
  4502. // Line extrafield
  4503. if (!is_object($extrafields)) {
  4504. require_once DOL_DOCUMENT_ROOT.'/core/class/extrafields.class.php';
  4505. $extrafields = new ExtraFields($this->db);
  4506. }
  4507. $extrafields->fetch_name_optionals_label($this->table_element_line);
  4508. $parameters = array('num'=>$num, 'dateSelector'=>$dateSelector, 'seller'=>$seller, 'buyer'=>$buyer, 'selected'=>$selected, 'table_element_line'=>$this->table_element_line);
  4509. $reshook = $hookmanager->executeHooks('printObjectLineTitle', $parameters, $this, $action); // Note that $action and $object may have been modified by some hooks
  4510. if (empty($reshook)) {
  4511. // Output template part (modules that overwrite templates must declare this into descriptor)
  4512. // Use global variables + $dateSelector + $seller and $buyer
  4513. // Note: This is deprecated. If you need to overwrite the tpl file, use instead the hook.
  4514. $dirtpls = array_merge($conf->modules_parts['tpl'], array($defaulttpldir));
  4515. foreach ($dirtpls as $module => $reldir) {
  4516. $res = 0;
  4517. if (!empty($module)) {
  4518. $tpl = dol_buildpath($reldir.'/objectline_title.tpl.php');
  4519. } else {
  4520. $tpl = DOL_DOCUMENT_ROOT.$reldir.'/objectline_title.tpl.php';
  4521. }
  4522. if (file_exists($tpl)) {
  4523. if (empty($conf->file->strict_mode)) {
  4524. $res = @include $tpl;
  4525. } else {
  4526. $res = include $tpl; // for debug
  4527. }
  4528. }
  4529. if ($res) {
  4530. break;
  4531. }
  4532. }
  4533. }
  4534. $i = 0;
  4535. print "<!-- begin printObjectLines() --><tbody>\n";
  4536. foreach ($this->lines as $line) {
  4537. //Line extrafield
  4538. $line->fetch_optionals();
  4539. //if (is_object($hookmanager) && (($line->product_type == 9 && !empty($line->special_code)) || !empty($line->fk_parent_line)))
  4540. if (is_object($hookmanager)) { // Old code is commented on preceding line.
  4541. if (empty($line->fk_parent_line)) {
  4542. $parameters = array('line'=>$line, 'num'=>$num, 'i'=>$i, 'dateSelector'=>$dateSelector, 'seller'=>$seller, 'buyer'=>$buyer, 'selected'=>$selected, 'table_element_line'=>$line->table_element, 'defaulttpldir'=>$defaulttpldir);
  4543. $reshook = $hookmanager->executeHooks('printObjectLine', $parameters, $this, $action); // Note that $action and $object may have been modified by some hooks
  4544. } else {
  4545. $parameters = array('line'=>$line, 'num'=>$num, 'i'=>$i, 'dateSelector'=>$dateSelector, 'seller'=>$seller, 'buyer'=>$buyer, 'selected'=>$selected, 'table_element_line'=>$line->table_element, 'fk_parent_line'=>$line->fk_parent_line, 'defaulttpldir'=>$defaulttpldir);
  4546. $reshook = $hookmanager->executeHooks('printObjectSubLine', $parameters, $this, $action); // Note that $action and $object may have been modified by some hooks
  4547. }
  4548. }
  4549. if (empty($reshook)) {
  4550. $this->printObjectLine($action, $line, '', $num, $i, $dateSelector, $seller, $buyer, $selected, $extrafields, $defaulttpldir);
  4551. }
  4552. $i++;
  4553. }
  4554. print "</tbody><!-- end printObjectLines() -->\n";
  4555. }
  4556. /**
  4557. * Return HTML content of a detail line
  4558. * TODO Move this into an output class file (htmlline.class.php)
  4559. *
  4560. * @param string $action GET/POST action
  4561. * @param CommonObjectLine $line Selected object line to output
  4562. * @param string $var Not used
  4563. * @param int $num Number of line (0)
  4564. * @param int $i I
  4565. * @param int $dateSelector 1=Show also date range input fields
  4566. * @param Societe $seller Object of seller third party
  4567. * @param Societe $buyer Object of buyer third party
  4568. * @param int $selected ID line selected
  4569. * @param Extrafields $extrafields Object of extrafields
  4570. * @param string $defaulttpldir Directory where to find the template (deprecated)
  4571. * @return void
  4572. */
  4573. public function printObjectLine($action, $line, $var, $num, $i, $dateSelector, $seller, $buyer, $selected = 0, $extrafields = null, $defaulttpldir = '/core/tpl')
  4574. {
  4575. global $conf, $langs, $user, $object, $hookmanager;
  4576. global $form;
  4577. global $object_rights, $disableedit, $disablemove, $disableremove; // TODO We should not use global var for this !
  4578. $object_rights = $this->getRights();
  4579. // var used into tpl
  4580. $text = '';
  4581. $description = '';
  4582. // Line in view mode
  4583. if ($action != 'editline' || $selected != $line->id) {
  4584. // Product
  4585. if (!empty($line->fk_product) && $line->fk_product > 0) {
  4586. $product_static = new Product($this->db);
  4587. $product_static->fetch($line->fk_product);
  4588. $product_static->ref = $line->ref; //can change ref in hook
  4589. $product_static->label = !empty($line->label) ? $line->label : ""; //can change label in hook
  4590. $text = $product_static->getNomUrl(1);
  4591. // Define output language and label
  4592. if (getDolGlobalInt('MAIN_MULTILANGS')) {
  4593. if (property_exists($this, 'socid') && !is_object($this->thirdparty)) {
  4594. dol_print_error('', 'Error: Method printObjectLine was called on an object and object->fetch_thirdparty was not done before');
  4595. return;
  4596. }
  4597. $prod = new Product($this->db);
  4598. $prod->fetch($line->fk_product);
  4599. $outputlangs = $langs;
  4600. $newlang = '';
  4601. if (empty($newlang) && GETPOST('lang_id', 'aZ09')) {
  4602. $newlang = GETPOST('lang_id', 'aZ09');
  4603. }
  4604. if (getDolGlobalString('PRODUIT_TEXTS_IN_THIRDPARTY_LANGUAGE') && empty($newlang) && is_object($this->thirdparty)) {
  4605. $newlang = $this->thirdparty->default_lang; // To use language of customer
  4606. }
  4607. if (!empty($newlang)) {
  4608. $outputlangs = new Translate("", $conf);
  4609. $outputlangs->setDefaultLang($newlang);
  4610. }
  4611. $label = (!empty($prod->multilangs[$outputlangs->defaultlang]["label"])) ? $prod->multilangs[$outputlangs->defaultlang]["label"] : $line->product_label;
  4612. } else {
  4613. $label = $line->product_label;
  4614. }
  4615. $text .= ' - '.(!empty($line->label) ? $line->label : $label);
  4616. $description .= (getDolGlobalInt('PRODUIT_DESC_IN_FORM_ACCORDING_TO_DEVICE') ? '' : (!empty($line->description) ? dol_htmlentitiesbr($line->description) : '')); // Description is what to show on popup. We shown nothing if already into desc.
  4617. }
  4618. $line->pu_ttc = price2num((!empty($line->subprice) ? $line->subprice : 0) * (1 + ((!empty($line->tva_tx) ? $line->tva_tx : 0) / 100)), 'MU');
  4619. // Output template part (modules that overwrite templates must declare this into descriptor)
  4620. // Use global variables + $dateSelector + $seller and $buyer
  4621. // Note: This is deprecated. If you need to overwrite the tpl file, use instead the hook printObjectLine and printObjectSubLine.
  4622. $dirtpls = array_merge($conf->modules_parts['tpl'], array($defaulttpldir));
  4623. foreach ($dirtpls as $module => $reldir) {
  4624. $res = 0;
  4625. if (!empty($module)) {
  4626. $tpl = dol_buildpath($reldir.'/objectline_view.tpl.php');
  4627. } else {
  4628. $tpl = DOL_DOCUMENT_ROOT.$reldir.'/objectline_view.tpl.php';
  4629. }
  4630. //var_dump($tpl);
  4631. if (file_exists($tpl)) {
  4632. if (empty($conf->file->strict_mode)) {
  4633. $res = @include $tpl;
  4634. } else {
  4635. $res = include $tpl; // for debug
  4636. }
  4637. }
  4638. if ($res) {
  4639. break;
  4640. }
  4641. }
  4642. }
  4643. // Line in update mode
  4644. if ($this->statut == 0 && $action == 'editline' && $selected == $line->id) {
  4645. $label = (!empty($line->label) ? $line->label : (($line->fk_product > 0) ? $line->product_label : ''));
  4646. $line->pu_ttc = price2num($line->subprice * (1 + ($line->tva_tx / 100)), 'MU');
  4647. // Output template part (modules that overwrite templates must declare this into descriptor)
  4648. // Use global variables + $dateSelector + $seller and $buyer
  4649. // Note: This is deprecated. If you need to overwrite the tpl file, use instead the hook printObjectLine and printObjectSubLine.
  4650. $dirtpls = array_merge($conf->modules_parts['tpl'], array($defaulttpldir));
  4651. foreach ($dirtpls as $module => $reldir) {
  4652. if (!empty($module)) {
  4653. $tpl = dol_buildpath($reldir.'/objectline_edit.tpl.php');
  4654. } else {
  4655. $tpl = DOL_DOCUMENT_ROOT.$reldir.'/objectline_edit.tpl.php';
  4656. }
  4657. if (empty($conf->file->strict_mode)) {
  4658. $res = @include $tpl;
  4659. } else {
  4660. $res = include $tpl; // for debug
  4661. }
  4662. if ($res) {
  4663. break;
  4664. }
  4665. }
  4666. }
  4667. }
  4668. /* This is to show array of line of details of source object */
  4669. /**
  4670. * Return HTML table table of source object lines
  4671. * TODO Move this and previous function into output html class file (htmlline.class.php).
  4672. * If lines are into a template, title must also be into a template
  4673. * But for the moment we don't know if it's possible, so we keep the method available on overloaded objects.
  4674. *
  4675. * @param string $restrictlist ''=All lines, 'services'=Restrict to services only
  4676. * @param array $selectedLines Array of lines id for selected lines
  4677. * @return void
  4678. */
  4679. public function printOriginLinesList($restrictlist = '', $selectedLines = array())
  4680. {
  4681. global $langs, $hookmanager, $conf, $form, $action;
  4682. print '<tr class="liste_titre">';
  4683. print '<td class="linecolref">'.$langs->trans('Ref').'</td>';
  4684. print '<td class="linecoldescription">'.$langs->trans('Description').'</td>';
  4685. print '<td class="linecolvat right">'.$langs->trans('VATRate').'</td>';
  4686. print '<td class="linecoluht right">'.$langs->trans('PriceUHT').'</td>';
  4687. if (isModEnabled("multicurrency")) {
  4688. print '<td class="linecoluht_currency right">'.$langs->trans('PriceUHTCurrency').'</td>';
  4689. }
  4690. print '<td class="linecolqty right">'.$langs->trans('Qty').'</td>';
  4691. if (getDolGlobalInt('PRODUCT_USE_UNITS')) {
  4692. print '<td class="linecoluseunit left">'.$langs->trans('Unit').'</td>';
  4693. }
  4694. print '<td class="linecoldiscount right">'.$langs->trans('ReductionShort').'</td>';
  4695. print '<td class="linecolht right">'.$langs->trans('TotalHT').'</td>';
  4696. print '<td class="center">'.$form->showCheckAddButtons('checkforselect', 1).'</td>';
  4697. print '</tr>';
  4698. $i = 0;
  4699. if (!empty($this->lines)) {
  4700. foreach ($this->lines as $line) {
  4701. $reshook = 0;
  4702. //if (is_object($hookmanager) && (($line->product_type == 9 && !empty($line->special_code)) || !empty($line->fk_parent_line))) {
  4703. if (is_object($hookmanager)) { // Old code is commented on preceding line.
  4704. $parameters = array('line'=>$line, 'i'=>$i, 'restrictlist'=>$restrictlist, 'selectedLines'=> $selectedLines);
  4705. if (!empty($line->fk_parent_line)) {
  4706. $parameters['fk_parent_line'] = $line->fk_parent_line;
  4707. }
  4708. $reshook = $hookmanager->executeHooks('printOriginObjectLine', $parameters, $this, $action); // Note that $action and $object may have been modified by some hooks
  4709. }
  4710. if (empty($reshook)) {
  4711. $this->printOriginLine($line, '', $restrictlist, '/core/tpl', $selectedLines);
  4712. }
  4713. $i++;
  4714. }
  4715. }
  4716. }
  4717. /**
  4718. * Return HTML with a line of table array of source object lines
  4719. * TODO Move this and previous function into output html class file (htmlline.class.php).
  4720. * If lines are into a template, title must also be into a template
  4721. * But for the moment we don't know if it's possible as we keep a method available on overloaded objects.
  4722. *
  4723. * @param CommonObjectLine $line Line
  4724. * @param string $var Not used
  4725. * @param string $restrictlist ''=All lines, 'services'=Restrict to services only (strike line if not)
  4726. * @param string $defaulttpldir Directory where to find the template
  4727. * @param array $selectedLines Array of lines id for selected lines
  4728. * @return void
  4729. */
  4730. public function printOriginLine($line, $var, $restrictlist = '', $defaulttpldir = '/core/tpl', $selectedLines = array())
  4731. {
  4732. global $langs, $conf;
  4733. //var_dump($line);
  4734. if (!empty($line->date_start)) {
  4735. $date_start = $line->date_start;
  4736. } else {
  4737. $date_start = $line->date_debut_prevue;
  4738. if ($line->date_debut_reel) {
  4739. $date_start = $line->date_debut_reel;
  4740. }
  4741. }
  4742. if (!empty($line->date_end)) {
  4743. $date_end = $line->date_end;
  4744. } else {
  4745. $date_end = $line->date_fin_prevue;
  4746. if ($line->date_fin_reel) {
  4747. $date_end = $line->date_fin_reel;
  4748. }
  4749. }
  4750. $this->tpl['id'] = $line->id;
  4751. $this->tpl['label'] = '';
  4752. if (!empty($line->fk_parent_line)) {
  4753. $this->tpl['label'] .= img_picto('', 'rightarrow');
  4754. }
  4755. if (($line->info_bits & 2) == 2) { // TODO Not sure this is used for source object
  4756. $discount = new DiscountAbsolute($this->db);
  4757. if (property_exists($this, 'socid')) {
  4758. $discount->fk_soc = $this->socid;
  4759. }
  4760. $this->tpl['label'] .= $discount->getNomUrl(0, 'discount');
  4761. } elseif (!empty($line->fk_product)) {
  4762. $productstatic = new Product($this->db);
  4763. $productstatic->id = $line->fk_product;
  4764. $productstatic->ref = $line->ref;
  4765. $productstatic->type = $line->fk_product_type;
  4766. if (empty($productstatic->ref)) {
  4767. $line->fetch_product();
  4768. $productstatic = $line->product;
  4769. }
  4770. $this->tpl['label'] .= $productstatic->getNomUrl(1);
  4771. $this->tpl['label'] .= ' - '.(!empty($line->label) ? $line->label : $line->product_label);
  4772. // Dates
  4773. if ($line->product_type == 1 && ($date_start || $date_end)) {
  4774. $this->tpl['label'] .= get_date_range($date_start, $date_end);
  4775. }
  4776. } else {
  4777. $this->tpl['label'] .= ($line->product_type == -1 ? '&nbsp;' : ($line->product_type == 1 ? img_object($langs->trans(''), 'service') : img_object($langs->trans(''), 'product')));
  4778. if (!empty($line->desc)) {
  4779. $this->tpl['label'] .= $line->desc;
  4780. } else {
  4781. $this->tpl['label'] .= ($line->label ? '&nbsp;'.$line->label : '');
  4782. }
  4783. // Dates
  4784. if ($line->product_type == 1 && ($date_start || $date_end)) {
  4785. $this->tpl['label'] .= get_date_range($date_start, $date_end);
  4786. }
  4787. }
  4788. if (!empty($line->desc)) {
  4789. if ($line->desc == '(CREDIT_NOTE)') { // TODO Not sure this is used for source object
  4790. $discount = new DiscountAbsolute($this->db);
  4791. $discount->fetch($line->fk_remise_except);
  4792. $this->tpl['description'] = $langs->transnoentities("DiscountFromCreditNote", $discount->getNomUrl(0));
  4793. } elseif ($line->desc == '(DEPOSIT)') { // TODO Not sure this is used for source object
  4794. $discount = new DiscountAbsolute($this->db);
  4795. $discount->fetch($line->fk_remise_except);
  4796. $this->tpl['description'] = $langs->transnoentities("DiscountFromDeposit", $discount->getNomUrl(0));
  4797. } elseif ($line->desc == '(EXCESS RECEIVED)') {
  4798. $discount = new DiscountAbsolute($this->db);
  4799. $discount->fetch($line->fk_remise_except);
  4800. $this->tpl['description'] = $langs->transnoentities("DiscountFromExcessReceived", $discount->getNomUrl(0));
  4801. } elseif ($line->desc == '(EXCESS PAID)') {
  4802. $discount = new DiscountAbsolute($this->db);
  4803. $discount->fetch($line->fk_remise_except);
  4804. $this->tpl['description'] = $langs->transnoentities("DiscountFromExcessPaid", $discount->getNomUrl(0));
  4805. } else {
  4806. $this->tpl['description'] = dol_trunc($line->desc, 60);
  4807. }
  4808. } else {
  4809. $this->tpl['description'] = '&nbsp;';
  4810. }
  4811. // VAT Rate
  4812. $this->tpl['vat_rate'] = vatrate($line->tva_tx, true);
  4813. $this->tpl['vat_rate'] .= (($line->info_bits & 1) == 1) ? '*' : '';
  4814. if (!empty($line->vat_src_code) && !preg_match('/\(/', $this->tpl['vat_rate'])) {
  4815. $this->tpl['vat_rate'] .= ' ('.$line->vat_src_code.')';
  4816. }
  4817. $this->tpl['price'] = price($line->subprice);
  4818. $this->tpl['total_ht'] = price($line->total_ht);
  4819. $this->tpl['multicurrency_price'] = price($line->multicurrency_subprice);
  4820. $this->tpl['qty'] = (($line->info_bits & 2) != 2) ? $line->qty : '&nbsp;';
  4821. if (getDolGlobalInt('PRODUCT_USE_UNITS')) {
  4822. $this->tpl['unit'] = $langs->transnoentities($line->getLabelOfUnit('long'));
  4823. }
  4824. $this->tpl['remise_percent'] = (($line->info_bits & 2) != 2) ? vatrate($line->remise_percent, true) : '&nbsp;';
  4825. // Is the line strike or not
  4826. $this->tpl['strike'] = 0;
  4827. if ($restrictlist == 'services' && $line->product_type != Product::TYPE_SERVICE) {
  4828. $this->tpl['strike'] = 1;
  4829. }
  4830. // Output template part (modules that overwrite templates must declare this into descriptor)
  4831. // Use global variables + $dateSelector + $seller and $buyer
  4832. $dirtpls = array_merge($conf->modules_parts['tpl'], array($defaulttpldir));
  4833. foreach ($dirtpls as $module => $reldir) {
  4834. if (!empty($module)) {
  4835. $tpl = dol_buildpath($reldir.'/originproductline.tpl.php');
  4836. } else {
  4837. $tpl = DOL_DOCUMENT_ROOT.$reldir.'/originproductline.tpl.php';
  4838. }
  4839. if (empty($conf->file->strict_mode)) {
  4840. $res = @include $tpl;
  4841. } else {
  4842. $res = include $tpl; // for debug
  4843. }
  4844. if ($res) {
  4845. break;
  4846. }
  4847. }
  4848. }
  4849. // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
  4850. /**
  4851. * Add resources to the current object : add entry into llx_element_resources
  4852. * Need $this->element & $this->id
  4853. *
  4854. * @param int $resource_id Resource id
  4855. * @param string $resource_type 'resource'
  4856. * @param int $busy Busy or not
  4857. * @param int $mandatory Mandatory or not
  4858. * @return int Return integer <=0 if KO, >0 if OK
  4859. */
  4860. public function add_element_resource($resource_id, $resource_type, $busy = 0, $mandatory = 0)
  4861. {
  4862. // phpcs:enable
  4863. $this->db->begin();
  4864. $sql = "INSERT INTO ".$this->db->prefix()."element_resources (";
  4865. $sql .= "resource_id";
  4866. $sql .= ", resource_type";
  4867. $sql .= ", element_id";
  4868. $sql .= ", element_type";
  4869. $sql .= ", busy";
  4870. $sql .= ", mandatory";
  4871. $sql .= ") VALUES (";
  4872. $sql .= ((int) $resource_id);
  4873. $sql .= ", '".$this->db->escape($resource_type)."'";
  4874. $sql .= ", '".$this->db->escape($this->id)."'";
  4875. $sql .= ", '".$this->db->escape($this->element)."'";
  4876. $sql .= ", '".$this->db->escape($busy)."'";
  4877. $sql .= ", '".$this->db->escape($mandatory)."'";
  4878. $sql .= ")";
  4879. dol_syslog(get_class($this)."::add_element_resource", LOG_DEBUG);
  4880. if ($this->db->query($sql)) {
  4881. $this->db->commit();
  4882. return 1;
  4883. } else {
  4884. $this->error = $this->db->lasterror();
  4885. $this->db->rollback();
  4886. return 0;
  4887. }
  4888. }
  4889. // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
  4890. /**
  4891. * Delete a link to resource line
  4892. *
  4893. * @param int $rowid Id of resource line to delete
  4894. * @param int $element element name (for trigger) TODO: use $this->element into commonobject class
  4895. * @param int $notrigger Disable all triggers
  4896. * @return int >0 if OK, <0 if KO
  4897. */
  4898. public function delete_resource($rowid, $element, $notrigger = 0)
  4899. {
  4900. // phpcs:enable
  4901. global $user;
  4902. $this->db->begin();
  4903. $sql = "DELETE FROM ".$this->db->prefix()."element_resources";
  4904. $sql .= " WHERE rowid = ".((int) $rowid);
  4905. dol_syslog(get_class($this)."::delete_resource", LOG_DEBUG);
  4906. $resql = $this->db->query($sql);
  4907. if (!$resql) {
  4908. $this->error = $this->db->lasterror();
  4909. $this->db->rollback();
  4910. return -1;
  4911. } else {
  4912. if (!$notrigger) {
  4913. $result = $this->call_trigger(strtoupper($element).'_DELETE_RESOURCE', $user);
  4914. if ($result < 0) {
  4915. $this->db->rollback();
  4916. return -1;
  4917. }
  4918. }
  4919. $this->db->commit();
  4920. return 1;
  4921. }
  4922. }
  4923. /**
  4924. * Overwrite magic function to solve problem of cloning object that are kept as references
  4925. *
  4926. * @return void
  4927. */
  4928. public function __clone()
  4929. {
  4930. // Force a copy of this->lines, otherwise it will point to same object.
  4931. if (isset($this->lines) && is_array($this->lines)) {
  4932. $nboflines = count($this->lines);
  4933. for ($i = 0; $i < $nboflines; $i++) {
  4934. if (is_object($this->lines[$i])) {
  4935. $this->lines[$i] = clone $this->lines[$i];
  4936. }
  4937. }
  4938. }
  4939. }
  4940. /**
  4941. * Common function for all objects extending CommonObject for generating documents
  4942. *
  4943. * @param string $modelspath Relative folder where generators are placed
  4944. * @param string $modele Generator to use. Caller must set it to obj->model_pdf or GETPOST('model_pdf','alpha') for example.
  4945. * @param Translate $outputlangs Output language to use
  4946. * @param int $hidedetails 1 to hide details. 0 by default
  4947. * @param int $hidedesc 1 to hide product description. 0 by default
  4948. * @param int $hideref 1 to hide product reference. 0 by default
  4949. * @param null|array $moreparams Array to provide more information
  4950. * @return int >0 if OK, <0 if KO
  4951. * @see addFileIntoDatabaseIndex()
  4952. */
  4953. protected function commonGenerateDocument($modelspath, $modele, $outputlangs, $hidedetails, $hidedesc, $hideref, $moreparams = null)
  4954. {
  4955. global $conf, $langs, $user, $hookmanager, $action;
  4956. $srctemplatepath = '';
  4957. $parameters = array('modelspath'=>$modelspath, 'modele'=>$modele, 'outputlangs'=>$outputlangs, 'hidedetails'=>$hidedetails, 'hidedesc'=>$hidedesc, 'hideref'=>$hideref, 'moreparams'=>$moreparams);
  4958. $reshook = $hookmanager->executeHooks('commonGenerateDocument', $parameters, $this, $action); // Note that $action and $object may have been modified by some hooks
  4959. if (!empty($reshook)) {
  4960. return $reshook;
  4961. }
  4962. dol_syslog("commonGenerateDocument modele=".$modele." outputlangs->defaultlang=".(is_object($outputlangs) ? $outputlangs->defaultlang : 'null'));
  4963. if (empty($modele)) {
  4964. $this->error = 'BadValueForParameterModele';
  4965. return -1;
  4966. }
  4967. // Increase limit for PDF build
  4968. $err = error_reporting();
  4969. error_reporting(0);
  4970. @set_time_limit(120);
  4971. error_reporting($err);
  4972. // If selected model is a filename template (then $modele="modelname" or "modelname:filename")
  4973. $tmp = explode(':', $modele, 2);
  4974. if (!empty($tmp[1])) {
  4975. $modele = $tmp[0];
  4976. $srctemplatepath = $tmp[1];
  4977. }
  4978. // Search template files
  4979. $file = '';
  4980. $classname = '';
  4981. $filefound = '';
  4982. $dirmodels = array('/');
  4983. if (is_array($conf->modules_parts['models'])) {
  4984. $dirmodels = array_merge($dirmodels, $conf->modules_parts['models']);
  4985. }
  4986. foreach ($dirmodels as $reldir) {
  4987. foreach (array('doc', 'pdf') as $prefix) {
  4988. if (in_array(get_class($this), array('Adherent'))) {
  4989. // Member module use prefix_modele.class.php
  4990. $file = $prefix."_".$modele.".class.php";
  4991. } else {
  4992. // Other module use prefix_modele.modules.php
  4993. $file = $prefix."_".$modele.".modules.php";
  4994. }
  4995. $file = dol_sanitizeFileName($file);
  4996. // We chack if file exists
  4997. $file = dol_buildpath($reldir.$modelspath.$file, 0);
  4998. if (file_exists($file)) {
  4999. $filefound = $file;
  5000. $classname = $prefix.'_'.$modele;
  5001. break;
  5002. }
  5003. }
  5004. if ($filefound) {
  5005. break;
  5006. }
  5007. }
  5008. if (!$filefound) {
  5009. $this->error = $langs->trans("Error").' Failed to load doc generator with modelpaths='.$modelspath.' - modele='.$modele;
  5010. $this->errors[] = $this->error;
  5011. dol_syslog($this->error, LOG_ERR);
  5012. return -1;
  5013. }
  5014. // Sanitize $filefound
  5015. $filefound = dol_sanitizePathName($filefound);
  5016. // If generator was found
  5017. global $db; // Required to solve a conception default making an include of some code that uses $db instead of $this->db just after.
  5018. require_once $filefound;
  5019. $obj = new $classname($this->db);
  5020. // If generator is ODT, we must have srctemplatepath defined, if not we set it.
  5021. if ($obj->type == 'odt' && empty($srctemplatepath)) {
  5022. $varfortemplatedir = $obj->scandir;
  5023. if ($varfortemplatedir && getDolGlobalString($varfortemplatedir)) {
  5024. $dirtoscan = getDolGlobalString($varfortemplatedir);
  5025. $listoffiles = array();
  5026. // Now we add first model found in directories scanned
  5027. $listofdir = explode(',', $dirtoscan);
  5028. foreach ($listofdir as $key => $tmpdir) {
  5029. $tmpdir = trim($tmpdir);
  5030. $tmpdir = preg_replace('/DOL_DATA_ROOT/', DOL_DATA_ROOT, $tmpdir);
  5031. if (!$tmpdir) {
  5032. unset($listofdir[$key]);
  5033. continue;
  5034. }
  5035. if (is_dir($tmpdir)) {
  5036. $tmpfiles = dol_dir_list($tmpdir, 'files', 0, '\.od(s|t)$', '', 'name', SORT_ASC, 0);
  5037. if (count($tmpfiles)) {
  5038. $listoffiles = array_merge($listoffiles, $tmpfiles);
  5039. }
  5040. }
  5041. }
  5042. if (count($listoffiles)) {
  5043. foreach ($listoffiles as $record) {
  5044. $srctemplatepath = $record['fullname'];
  5045. break;
  5046. }
  5047. }
  5048. }
  5049. if (empty($srctemplatepath)) {
  5050. $this->error = 'ErrorGenerationAskedForOdtTemplateWithSrcFileNotDefined';
  5051. return -1;
  5052. }
  5053. }
  5054. if ($obj->type == 'odt' && !empty($srctemplatepath)) {
  5055. if (!dol_is_file($srctemplatepath)) {
  5056. dol_syslog("Failed to locate template file ".$srctemplatepath, LOG_WARNING);
  5057. $this->error = 'ErrorGenerationAskedForOdtTemplateWithSrcFileNotFound';
  5058. return -1;
  5059. }
  5060. }
  5061. // We save charset_output to restore it because write_file can change it if needed for
  5062. // output format that does not support UTF8.
  5063. $sav_charset_output = empty($outputlangs->charset_output) ? '' : $outputlangs->charset_output;
  5064. // update model_pdf in object
  5065. $this->model_pdf = $modele;
  5066. if (in_array(get_class($this), array('Adherent'))) {
  5067. $resultwritefile = $obj->write_file($this, $outputlangs, $srctemplatepath, 'member', 1, 'tmp_cards', $moreparams);
  5068. } else {
  5069. $resultwritefile = $obj->write_file($this, $outputlangs, $srctemplatepath, $hidedetails, $hidedesc, $hideref, $moreparams);
  5070. }
  5071. // After call of write_file $obj->result['fullpath'] is set with generated file. It will be used to update the ECM database index.
  5072. if ($resultwritefile > 0) {
  5073. $outputlangs->charset_output = $sav_charset_output;
  5074. // We delete old preview
  5075. require_once DOL_DOCUMENT_ROOT.'/core/lib/files.lib.php';
  5076. dol_delete_preview($this);
  5077. // Index file in database
  5078. if (!empty($obj->result['fullpath'])) {
  5079. $destfull = $obj->result['fullpath'];
  5080. // Update the last_main_doc field into main object (if document generator has property ->update_main_doc_field set)
  5081. $update_main_doc_field = 0;
  5082. if (!empty($obj->update_main_doc_field)) {
  5083. $update_main_doc_field = 1;
  5084. }
  5085. // Check that the file exists, before indexing it.
  5086. // Hint: It does not exist, if we create a PDF and auto delete the ODT File
  5087. if (dol_is_file($destfull)) {
  5088. $this->indexFile($destfull, $update_main_doc_field);
  5089. }
  5090. } else {
  5091. dol_syslog('Method ->write_file was called on object '.get_class($obj).' and return a success but the return array ->result["fullpath"] was not set.', LOG_WARNING);
  5092. }
  5093. // Success in building document. We build meta file.
  5094. dol_meta_create($this);
  5095. return 1;
  5096. } else {
  5097. $outputlangs->charset_output = $sav_charset_output;
  5098. $this->error = $obj->error;
  5099. $this->errors = $obj->errors;
  5100. dol_syslog("Error generating document for ".__CLASS__.". Error: ".$obj->error, LOG_ERR);
  5101. return -1;
  5102. }
  5103. }
  5104. /**
  5105. * Index a file into the ECM database
  5106. *
  5107. * @param string $destfull Full path of file to index
  5108. * @param int $update_main_doc_field Update field main_doc fied into the table of object.
  5109. * This param is set when called for a document generation if document generator hase
  5110. * ->update_main_doc_field set and returns ->result['fullpath'].
  5111. * @return int Return integer <0 if KO, >0 if OK
  5112. */
  5113. public function indexFile($destfull, $update_main_doc_field)
  5114. {
  5115. global $conf, $user;
  5116. $upload_dir = dirname($destfull);
  5117. $destfile = basename($destfull);
  5118. $rel_dir = preg_replace('/^'.preg_quote(DOL_DATA_ROOT, '/').'/', '', $upload_dir);
  5119. if (!preg_match('/[\\/]temp[\\/]|[\\/]thumbs|\.meta$/', $rel_dir)) { // If not a tmp dir
  5120. $filename = basename($destfile);
  5121. $rel_dir = preg_replace('/[\\/]$/', '', $rel_dir);
  5122. $rel_dir = preg_replace('/^[\\/]/', '', $rel_dir);
  5123. include_once DOL_DOCUMENT_ROOT.'/ecm/class/ecmfiles.class.php';
  5124. $ecmfile = new EcmFiles($this->db);
  5125. $result = $ecmfile->fetch(0, '', ($rel_dir ? $rel_dir.'/' : '').$filename);
  5126. // Set the public "share" key
  5127. $setsharekey = false;
  5128. if ($this->element == 'propal' || $this->element == 'proposal') {
  5129. if (getDolGlobalInt("PROPOSAL_ALLOW_ONLINESIGN")) {
  5130. $setsharekey = true; // feature to make online signature is not set or set to on (default)
  5131. }
  5132. if (getDolGlobalInt("PROPOSAL_ALLOW_EXTERNAL_DOWNLOAD")) {
  5133. $setsharekey = true;
  5134. }
  5135. }
  5136. if ($this->element == 'commande' && getDolGlobalInt("ORDER_ALLOW_EXTERNAL_DOWNLOAD")) {
  5137. $setsharekey = true;
  5138. }
  5139. if ($this->element == 'facture' && getDolGlobalInt("INVOICE_ALLOW_EXTERNAL_DOWNLOAD")) {
  5140. $setsharekey = true;
  5141. }
  5142. if ($this->element == 'bank_account' && getDolGlobalInt("BANK_ACCOUNT_ALLOW_EXTERNAL_DOWNLOAD")) {
  5143. $setsharekey = true;
  5144. }
  5145. if ($this->element == 'product' && getDolGlobalInt("PRODUCT_ALLOW_EXTERNAL_DOWNLOAD")) {
  5146. $setsharekey = true;
  5147. }
  5148. if ($this->element == 'contrat' && getDolGlobalInt("CONTRACT_ALLOW_EXTERNAL_DOWNLOAD")) {
  5149. $setsharekey = true;
  5150. }
  5151. if ($this->element == 'fichinter' && getDolGlobalInt("FICHINTER_ALLOW_EXTERNAL_DOWNLOAD")) {
  5152. $setsharekey = true;
  5153. }
  5154. if ($this->element == 'supplier_proposal' && getDolGlobalInt("SUPPLIER_PROPOSAL_ALLOW_EXTERNAL_DOWNLOAD")) {
  5155. $setsharekey = true;
  5156. }
  5157. if ($this->element == 'societe_rib' && getDolGlobalInt("SOCIETE_RIB_ALLOW_ONLINESIGN")) {
  5158. $setsharekey = true;
  5159. }
  5160. if ($setsharekey) {
  5161. if (empty($ecmfile->share)) { // Because object not found or share not set yet
  5162. require_once DOL_DOCUMENT_ROOT.'/core/lib/security2.lib.php';
  5163. $ecmfile->share = getRandomPassword(true);
  5164. }
  5165. }
  5166. if ($result > 0) {
  5167. $ecmfile->label = md5_file(dol_osencode($destfull)); // hash of file content
  5168. $ecmfile->fullpath_orig = '';
  5169. $ecmfile->gen_or_uploaded = 'generated';
  5170. $ecmfile->description = ''; // indexed content
  5171. $ecmfile->keywords = ''; // keyword content
  5172. $result = $ecmfile->update($user);
  5173. if ($result < 0) {
  5174. setEventMessages($ecmfile->error, $ecmfile->errors, 'warnings');
  5175. return -1;
  5176. }
  5177. } else {
  5178. $ecmfile->entity = $conf->entity;
  5179. $ecmfile->filepath = $rel_dir;
  5180. $ecmfile->filename = $filename;
  5181. $ecmfile->label = md5_file(dol_osencode($destfull)); // hash of file content
  5182. $ecmfile->fullpath_orig = '';
  5183. $ecmfile->gen_or_uploaded = 'generated';
  5184. $ecmfile->description = ''; // indexed content
  5185. $ecmfile->keywords = ''; // keyword content
  5186. $ecmfile->src_object_type = $this->table_element; // $this->table_name is 'myobject' or 'mymodule_myobject'.
  5187. $ecmfile->src_object_id = $this->id;
  5188. $result = $ecmfile->create($user);
  5189. if ($result < 0) {
  5190. setEventMessages($ecmfile->error, $ecmfile->errors, 'warnings');
  5191. return -1;
  5192. }
  5193. }
  5194. /*$this->result['fullname']=$destfull;
  5195. $this->result['filepath']=$ecmfile->filepath;
  5196. $this->result['filename']=$ecmfile->filename;*/
  5197. //var_dump($obj->update_main_doc_field);exit;
  5198. if ($update_main_doc_field && !empty($this->table_element)) {
  5199. $sql = "UPDATE ".$this->db->prefix().$this->table_element." SET last_main_doc = '".$this->db->escape($ecmfile->filepath."/".$ecmfile->filename)."'";
  5200. $sql .= " WHERE rowid = ".((int) $this->id);
  5201. $resql = $this->db->query($sql);
  5202. if (!$resql) {
  5203. dol_print_error($this->db);
  5204. return -1;
  5205. } else {
  5206. $this->last_main_doc = $ecmfile->filepath.'/'.$ecmfile->filename;
  5207. }
  5208. }
  5209. }
  5210. return 1;
  5211. }
  5212. /**
  5213. * Build thumb
  5214. * @todo Move this into files.lib.php
  5215. *
  5216. * @param string $file Path file in UTF8 to original file to create thumbs from.
  5217. * @return void
  5218. */
  5219. public function addThumbs($file)
  5220. {
  5221. $file_osencoded = dol_osencode($file);
  5222. if (file_exists($file_osencoded)) {
  5223. require_once DOL_DOCUMENT_ROOT.'/core/lib/images.lib.php';
  5224. $tmparraysize = getDefaultImageSizes();
  5225. $maxwidthsmall = $tmparraysize['maxwidthsmall'];
  5226. $maxheightsmall = $tmparraysize['maxheightsmall'];
  5227. $maxwidthmini = $tmparraysize['maxwidthmini'];
  5228. $maxheightmini = $tmparraysize['maxheightmini'];
  5229. //$quality = $tmparraysize['quality'];
  5230. $quality = 50; // For thumbs, we force quality to 50
  5231. // Create small thumbs for company (Ratio is near 16/9)
  5232. // Used on logon for example
  5233. vignette($file_osencoded, $maxwidthsmall, $maxheightsmall, '_small', $quality);
  5234. // Create mini thumbs for company (Ratio is near 16/9)
  5235. // Used on menu or for setup page for example
  5236. vignette($file_osencoded, $maxwidthmini, $maxheightmini, '_mini', $quality);
  5237. }
  5238. }
  5239. /**
  5240. * Delete thumbs
  5241. * @todo Move this into files.lib.php
  5242. *
  5243. * @param string $file Path file in UTF8 to original file to delete thumbs.
  5244. * @return void
  5245. */
  5246. public function delThumbs($file)
  5247. {
  5248. $imgThumbName = getImageFileNameForSize($file, '_small'); // Full path of thumb file
  5249. dol_delete_file($imgThumbName);
  5250. $imgThumbName = getImageFileNameForSize($file, '_mini'); // Full path of thumb file
  5251. dol_delete_file($imgThumbName);
  5252. }
  5253. /* Functions common to commonobject and commonobjectline */
  5254. /* For default values */
  5255. /**
  5256. * Return the default value to use for a field when showing the create form of object.
  5257. * Return values in this order:
  5258. * 1) If parameter is available into POST, we return it first.
  5259. * 2) If not but an alternate value was provided as parameter of function, we return it.
  5260. * 3) If not but a constant $conf->global->OBJECTELEMENT_FIELDNAME is set, we return it (It is better to use the dedicated table).
  5261. * 4) Return value found into database (TODO No yet implemented)
  5262. *
  5263. * @param string $fieldname Name of field
  5264. * @param string $alternatevalue Alternate value to use
  5265. * @param string $type Type of data
  5266. * @return string|string[] Default value (can be an array if the GETPOST return an array)
  5267. **/
  5268. public function getDefaultCreateValueFor($fieldname, $alternatevalue = null, $type = 'alphanohtml')
  5269. {
  5270. global $conf, $_POST;
  5271. // If param here has been posted, we use this value first.
  5272. if (GETPOSTISSET($fieldname)) {
  5273. return GETPOST($fieldname, $type, 3);
  5274. }
  5275. if (isset($alternatevalue)) {
  5276. return $alternatevalue;
  5277. }
  5278. $newelement = $this->element;
  5279. if ($newelement == 'facture') {
  5280. $newelement = 'invoice';
  5281. }
  5282. if ($newelement == 'commande') {
  5283. $newelement = 'order';
  5284. }
  5285. if (empty($newelement)) {
  5286. dol_syslog("Ask a default value using common method getDefaultCreateValueForField on an object with no property ->element defined. Return empty string.", LOG_WARNING);
  5287. return '';
  5288. }
  5289. $keyforfieldname = strtoupper($newelement.'_DEFAULT_'.$fieldname);
  5290. //var_dump($keyforfieldname);
  5291. if (getDolGlobalString($keyforfieldname)) {
  5292. return getDolGlobalString($keyforfieldname);
  5293. }
  5294. // TODO Ad here a scan into table llx_overwrite_default with a filter on $this->element and $fieldname
  5295. // store content into $conf->cache['overwrite_default']
  5296. return '';
  5297. }
  5298. /* For triggers */
  5299. // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
  5300. /**
  5301. * Call trigger based on this instance.
  5302. * Some context information may also be provided into array property this->context.
  5303. * NB: Error from trigger are stacked in interface->errors
  5304. * NB2: If return code of triggers are < 0, action calling trigger should cancel all transaction.
  5305. *
  5306. * @param string $triggerName trigger's name to execute
  5307. * @param User $user Object user
  5308. * @return int Result of run_triggers
  5309. */
  5310. public function call_trigger($triggerName, $user)
  5311. {
  5312. // phpcs:enable
  5313. global $langs, $conf;
  5314. if (!empty(self::TRIGGER_PREFIX) && strpos($triggerName, self::TRIGGER_PREFIX . '_') !== 0) {
  5315. dol_print_error('', 'The trigger "' . $triggerName . '" does not start with "' . self::TRIGGER_PREFIX . '_" as required.');
  5316. exit;
  5317. }
  5318. if (!is_object($langs)) { // If lang was not defined, we set it. It is required by run_triggers().
  5319. include_once DOL_DOCUMENT_ROOT.'/core/class/translate.class.php';
  5320. $langs = new Translate('', $conf);
  5321. }
  5322. include_once DOL_DOCUMENT_ROOT.'/core/class/interfaces.class.php';
  5323. $interface = new Interfaces($this->db);
  5324. $result = $interface->run_triggers($triggerName, $this, $user, $langs, $conf);
  5325. if ($result < 0) {
  5326. if (!empty($this->errors)) {
  5327. $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.
  5328. } else {
  5329. $this->errors = $interface->errors;
  5330. }
  5331. }
  5332. return $result;
  5333. }
  5334. /* Functions for data in other language */
  5335. /**
  5336. * Function to get alternative languages of a data into $this->array_languages
  5337. * This method is NOT called by method fetch of objects but must be called separately.
  5338. *
  5339. * @return int Return integer <0 if error, 0 if no values of alternative languages to find nor found, 1 if a value was found and loaded
  5340. * @see fetch_optionnals()
  5341. */
  5342. public function fetchValuesForExtraLanguages()
  5343. {
  5344. // To avoid SQL errors. Probably not the better solution though
  5345. if (!$this->element) {
  5346. return 0;
  5347. }
  5348. if (!($this->id > 0)) {
  5349. return 0;
  5350. }
  5351. if (is_array($this->array_languages)) {
  5352. return 1;
  5353. }
  5354. $this->array_languages = array();
  5355. $element = $this->element;
  5356. if ($element == 'categorie') {
  5357. $element = 'categories'; // For compatibility
  5358. }
  5359. // Request to get translation values for object
  5360. $sql = "SELECT rowid, property, lang , value";
  5361. $sql .= " FROM ".$this->db->prefix()."object_lang";
  5362. $sql .= " WHERE type_object = '".$this->db->escape($element)."'";
  5363. $sql .= " AND fk_object = ".((int) $this->id);
  5364. //dol_syslog(get_class($this)."::fetch_optionals get extrafields data for ".$this->table_element, LOG_DEBUG); // Too verbose
  5365. $resql = $this->db->query($sql);
  5366. if ($resql) {
  5367. $numrows = $this->db->num_rows($resql);
  5368. if ($numrows) {
  5369. $i = 0;
  5370. while ($i < $numrows) {
  5371. $obj = $this->db->fetch_object($resql);
  5372. $key = $obj->property;
  5373. $value = $obj->value;
  5374. $codelang = $obj->lang;
  5375. $type = $this->fields[$key]['type'];
  5376. // we can add this attribute to object
  5377. if (preg_match('/date/', $type)) {
  5378. $this->array_languages[$key][$codelang] = $this->db->jdate($value);
  5379. } else {
  5380. $this->array_languages[$key][$codelang] = $value;
  5381. }
  5382. $i++;
  5383. }
  5384. }
  5385. $this->db->free($resql);
  5386. if ($numrows) {
  5387. return $numrows;
  5388. } else {
  5389. return 0;
  5390. }
  5391. } else {
  5392. dol_print_error($this->db);
  5393. return -1;
  5394. }
  5395. }
  5396. /**
  5397. * Fill array_options property of object by extrafields value (using for data sent by forms)
  5398. *
  5399. * @param string $onlykey Only the following key is filled. When we make update of only one language field ($action = 'update_languages'), calling page must set this to avoid to have other languages being reset.
  5400. * @return int 1 if array_options set, 0 if no value, -1 if error (field required missing for example)
  5401. */
  5402. public function setValuesForExtraLanguages($onlykey = '')
  5403. {
  5404. global $_POST, $langs;
  5405. // Get extra fields
  5406. foreach ($_POST as $postfieldkey => $postfieldvalue) {
  5407. $tmparray = explode('-', $postfieldkey);
  5408. if ($tmparray[0] != 'field') {
  5409. continue;
  5410. }
  5411. $element = $tmparray[1];
  5412. $key = $tmparray[2];
  5413. $codelang = $tmparray[3];
  5414. //var_dump("postfieldkey=".$postfieldkey." element=".$element." key=".$key." codelang=".$codelang);
  5415. if (!empty($onlykey) && $key != $onlykey) {
  5416. continue;
  5417. }
  5418. if ($element != $this->element) {
  5419. continue;
  5420. }
  5421. $key_type = $this->fields[$key]['type'];
  5422. $enabled = 1;
  5423. if (isset($this->fields[$key]['enabled'])) {
  5424. $enabled = dol_eval($this->fields[$key]['enabled'], 1, 1, '1');
  5425. }
  5426. /*$perms = 1;
  5427. if (isset($this->fields[$key]['perms']))
  5428. {
  5429. $perms = dol_eval($this->fields[$key]['perms'], 1, 1, '1');
  5430. }*/
  5431. if (empty($enabled)) {
  5432. continue;
  5433. }
  5434. //if (empty($perms)) continue;
  5435. if (in_array($key_type, array('date'))) {
  5436. // Clean parameters
  5437. // TODO GMT date in memory must be GMT so we should add gm=true in parameters
  5438. $value_key = dol_mktime(0, 0, 0, GETPOST($postfieldkey."month", 'int'), GETPOST($postfieldkey."day", 'int'), GETPOST($postfieldkey."year", 'int'));
  5439. } elseif (in_array($key_type, array('datetime'))) {
  5440. // Clean parameters
  5441. // TODO GMT date in memory must be GMT so we should add gm=true in parameters
  5442. $value_key = dol_mktime(GETPOST($postfieldkey."hour", 'int'), GETPOST($postfieldkey."min", 'int'), 0, GETPOST($postfieldkey."month", 'int'), GETPOST($postfieldkey."day", 'int'), GETPOST($postfieldkey."year", 'int'));
  5443. } elseif (in_array($key_type, array('checkbox', 'chkbxlst'))) {
  5444. $value_arr = GETPOST($postfieldkey, 'array'); // check if an array
  5445. if (!empty($value_arr)) {
  5446. $value_key = implode(',', $value_arr);
  5447. } else {
  5448. $value_key = '';
  5449. }
  5450. } elseif (in_array($key_type, array('price', 'double'))) {
  5451. $value_arr = GETPOST($postfieldkey, 'alpha');
  5452. $value_key = price2num($value_arr);
  5453. } else {
  5454. $value_key = GETPOST($postfieldkey);
  5455. if (in_array($key_type, array('link')) && $value_key == '-1') {
  5456. $value_key = '';
  5457. }
  5458. }
  5459. $this->array_languages[$key][$codelang] = $value_key;
  5460. /*if ($nofillrequired) {
  5461. $langs->load('errors');
  5462. setEventMessages($langs->trans('ErrorFieldsRequired').' : '.implode(', ', $error_field_required), null, 'errors');
  5463. return -1;
  5464. }*/
  5465. }
  5466. return 1;
  5467. }
  5468. /* Functions for extrafields */
  5469. /**
  5470. * Function to make a fetch but set environment to avoid to load computed values before.
  5471. *
  5472. * @param int $id ID of object
  5473. * @return int >0 if OK, 0 if not found, <0 if KO
  5474. */
  5475. public function fetchNoCompute($id)
  5476. {
  5477. global $conf;
  5478. $savDisableCompute = $conf->disable_compute;
  5479. $conf->disable_compute = 1;
  5480. $ret = $this->fetch($id); /* @phpstan-ignore-line */
  5481. $conf->disable_compute = $savDisableCompute;
  5482. return $ret;
  5483. }
  5484. // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
  5485. /**
  5486. * Function to get extra fields of an object into $this->array_options
  5487. * This method is in most cases called by method fetch of objects but you can call it separately.
  5488. *
  5489. * @param int $rowid Id of line. Use the id of object if not defined. Deprecated. Function must be called without parameters.
  5490. * @param array $optionsArray Array resulting of call of extrafields->fetch_name_optionals_label(). Deprecated. Function must be called without parameters.
  5491. * @return int Return integer <0 if error, 0 if no values of extrafield to find nor found, 1 if an attribute is found and value loaded
  5492. * @see fetchValuesForExtraLanguages()
  5493. */
  5494. public function fetch_optionals($rowid = null, $optionsArray = null)
  5495. {
  5496. // phpcs:enable
  5497. global $conf, $extrafields;
  5498. if (empty($rowid)) {
  5499. $rowid = $this->id;
  5500. }
  5501. if (empty($rowid) && isset($this->rowid)) {
  5502. $rowid = $this->rowid; // deprecated
  5503. }
  5504. // To avoid SQL errors. Probably not the better solution though
  5505. if (!$this->table_element) {
  5506. return 0;
  5507. }
  5508. $this->array_options = array();
  5509. if (!is_array($optionsArray)) {
  5510. // If $extrafields is not a known object, we initialize it. Best practice is to have $extrafields defined into card.php or list.php page.
  5511. if (!isset($extrafields) || !is_object($extrafields)) {
  5512. require_once DOL_DOCUMENT_ROOT.'/core/class/extrafields.class.php';
  5513. $extrafields = new ExtraFields($this->db);
  5514. }
  5515. // Load array of extrafields for elementype = $this->table_element
  5516. if (empty($extrafields->attributes[$this->table_element]['loaded'])) {
  5517. $extrafields->fetch_name_optionals_label($this->table_element);
  5518. }
  5519. $optionsArray = (!empty($extrafields->attributes[$this->table_element]['label']) ? $extrafields->attributes[$this->table_element]['label'] : null);
  5520. } else {
  5521. global $extrafields;
  5522. dol_syslog("Warning: fetch_optionals was called with param optionsArray defined when you should pass null now", LOG_WARNING);
  5523. }
  5524. $table_element = $this->table_element;
  5525. if ($table_element == 'categorie') {
  5526. $table_element = 'categories'; // For compatibility
  5527. }
  5528. // Request to get complementary values
  5529. if (is_array($optionsArray) && count($optionsArray) > 0) {
  5530. $sql = "SELECT rowid";
  5531. foreach ($optionsArray as $name => $label) {
  5532. if (empty($extrafields->attributes[$this->table_element]['type'][$name]) || $extrafields->attributes[$this->table_element]['type'][$name] != 'separate') {
  5533. $sql .= ", ".$name;
  5534. }
  5535. }
  5536. $sql .= " FROM ".$this->db->prefix().$table_element."_extrafields";
  5537. $sql .= " WHERE fk_object = ".((int) $rowid);
  5538. //dol_syslog(get_class($this)."::fetch_optionals get extrafields data for ".$this->table_element, LOG_DEBUG); // Too verbose
  5539. $resql = $this->db->query($sql);
  5540. if ($resql) {
  5541. $numrows = $this->db->num_rows($resql);
  5542. if ($numrows) {
  5543. $tab = $this->db->fetch_array($resql);
  5544. foreach ($tab as $key => $value) {
  5545. // 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)
  5546. if ($key != 'rowid' && $key != 'tms' && $key != 'fk_member' && !is_int($key)) {
  5547. // we can add this attribute to object
  5548. if (!empty($extrafields->attributes[$this->table_element]) && in_array($extrafields->attributes[$this->table_element]['type'][$key], array('date', 'datetime'))) {
  5549. //var_dump($extrafields->attributes[$this->table_element]['type'][$key]);
  5550. $this->array_options["options_".$key] = $this->db->jdate($value);
  5551. } else {
  5552. $this->array_options["options_".$key] = $value;
  5553. }
  5554. //var_dump('key '.$key.' '.$value.' type='.$extrafields->attributes[$this->table_element]['type'][$key].' '.$this->array_options["options_".$key]);
  5555. }
  5556. if (!empty($extrafields->attributes[$this->table_element]['type'][$key]) && $extrafields->attributes[$this->table_element]['type'][$key] == 'password') {
  5557. if (!empty($value) && preg_match('/^dolcrypt:/', $value)) {
  5558. $this->array_options["options_".$key] = dolDecrypt($value);
  5559. }
  5560. }
  5561. }
  5562. }
  5563. // If field is a computed field, value must become result of compute (regardless of whether a row exists
  5564. // in the element's extrafields table)
  5565. if (is_array($extrafields->attributes[$this->table_element]['label'])) {
  5566. foreach ($extrafields->attributes[$this->table_element]['label'] as $key => $val) {
  5567. if (!empty($extrafields->attributes[$this->table_element]) && !empty($extrafields->attributes[$this->table_element]['computed'][$key])) {
  5568. //var_dump($conf->disable_compute);
  5569. if (empty($conf->disable_compute)) {
  5570. global $objectoffield; // We set a global variable to $objectoffield so
  5571. $objectoffield = $this; // we can use it inside computed formula
  5572. $this->array_options['options_' . $key] = dol_eval($extrafields->attributes[$this->table_element]['computed'][$key], 1, 0, '2');
  5573. }
  5574. }
  5575. }
  5576. }
  5577. $this->db->free($resql);
  5578. if ($numrows) {
  5579. return $numrows;
  5580. } else {
  5581. return 0;
  5582. }
  5583. } else {
  5584. $this->errors[]=$this->db->lasterror;
  5585. return -1;
  5586. }
  5587. }
  5588. return 0;
  5589. }
  5590. /**
  5591. * Delete all extra fields values for the current object.
  5592. *
  5593. * @return int Return integer <0 if KO, >0 if OK
  5594. * @see deleteExtraLanguages(), insertExtraField(), updateExtraField(), setValueFrom()
  5595. */
  5596. public function deleteExtraFields()
  5597. {
  5598. global $conf;
  5599. if (getDolGlobalString('MAIN_EXTRAFIELDS_DISABLED')) {
  5600. return 0;
  5601. }
  5602. $this->db->begin();
  5603. $table_element = $this->table_element;
  5604. if ($table_element == 'categorie') {
  5605. $table_element = 'categories'; // For compatibility
  5606. }
  5607. dol_syslog(get_class($this)."::deleteExtraFields delete", LOG_DEBUG);
  5608. $sql_del = "DELETE FROM ".$this->db->prefix().$table_element."_extrafields WHERE fk_object = ".((int) $this->id);
  5609. $resql = $this->db->query($sql_del);
  5610. if (!$resql) {
  5611. $this->error = $this->db->lasterror();
  5612. $this->db->rollback();
  5613. return -1;
  5614. } else {
  5615. $this->db->commit();
  5616. return 1;
  5617. }
  5618. }
  5619. /**
  5620. * Add/Update all extra fields values for the current object.
  5621. * Data to describe values to insert/update are stored into $this->array_options=array('options_codeforfield1'=>'valueforfield1', 'options_codeforfield2'=>'valueforfield2', ...)
  5622. * This function delete record with all extrafields and insert them again from the array $this->array_options.
  5623. *
  5624. * @param string $trigger If defined, call also the trigger (for example COMPANY_MODIFY)
  5625. * @param User $userused Object user
  5626. * @return int -1=error, O=did nothing, 1=OK
  5627. * @see insertExtraLanguages(), updateExtraField(), deleteExtraField(), setValueFrom()
  5628. */
  5629. public function insertExtraFields($trigger = '', $userused = null)
  5630. {
  5631. global $conf, $langs, $user;
  5632. if (getDolGlobalString('MAIN_EXTRAFIELDS_DISABLED')) {
  5633. return 0;
  5634. }
  5635. if (empty($userused)) {
  5636. $userused = $user;
  5637. }
  5638. $error = 0;
  5639. if (!empty($this->array_options)) {
  5640. // Check parameters
  5641. $langs->load('admin');
  5642. require_once DOL_DOCUMENT_ROOT.'/core/class/extrafields.class.php';
  5643. $extrafields = new ExtraFields($this->db);
  5644. $target_extrafields = $extrafields->fetch_name_optionals_label($this->table_element);
  5645. // Eliminate copied source object extra fields that do not exist in target object
  5646. $new_array_options = array();
  5647. foreach ($this->array_options as $key => $value) {
  5648. if (in_array(substr($key, 8), array_keys($target_extrafields))) { // We remove the 'options_' from $key for test
  5649. $new_array_options[$key] = $value;
  5650. } elseif (in_array($key, array_keys($target_extrafields))) { // We test on $key that does not contain the 'options_' prefix
  5651. $new_array_options['options_'.$key] = $value;
  5652. }
  5653. }
  5654. foreach ($new_array_options as $key => $value) {
  5655. $attributeKey = substr($key, 8); // Remove 'options_' prefix
  5656. $attributeType = $extrafields->attributes[$this->table_element]['type'][$attributeKey];
  5657. $attributeLabel = $extrafields->attributes[$this->table_element]['label'][$attributeKey];
  5658. $attributeParam = $extrafields->attributes[$this->table_element]['param'][$attributeKey];
  5659. $attributeRequired = $extrafields->attributes[$this->table_element]['required'][$attributeKey];
  5660. $attributeUnique = $extrafields->attributes[$this->table_element]['unique'][$attributeKey];
  5661. $attrfieldcomputed = $extrafields->attributes[$this->table_element]['computed'][$attributeKey];
  5662. // If we clone, we have to clean unique extrafields to prevent duplicates.
  5663. // This behaviour can be prevented by external code by changing $this->context['createfromclone'] value in createFrom hook
  5664. if (!empty($this->context['createfromclone']) && $this->context['createfromclone'] == 'createfromclone' && !empty($attributeUnique)) {
  5665. $new_array_options[$key] = null;
  5666. }
  5667. // Similar code than into insertExtraFields
  5668. if ($attributeRequired) {
  5669. $v = $this->array_options[$key];
  5670. if (ExtraFields::isEmptyValue($v, $attributeType)) {
  5671. $langs->load("errors");
  5672. dol_syslog("Mandatory field '".$key."' is empty during create and set to required into definition of extrafields");
  5673. $this->errors[] = $langs->trans('ErrorFieldRequired', $attributeLabel);
  5674. return -1;
  5675. }
  5676. }
  5677. //dol_syslog("attributeLabel=".$attributeLabel, LOG_DEBUG);
  5678. //dol_syslog("attributeType=".$attributeType, LOG_DEBUG);
  5679. if (!empty($attrfieldcomputed)) {
  5680. if (getDolGlobalString('MAIN_STORE_COMPUTED_EXTRAFIELDS')) {
  5681. $value = dol_eval($attrfieldcomputed, 1, 0, '2');
  5682. dol_syslog($langs->trans("Extrafieldcomputed")." on ".$attributeLabel."(".$value.")", LOG_DEBUG);
  5683. $new_array_options[$key] = $value;
  5684. } else {
  5685. $new_array_options[$key] = null;
  5686. }
  5687. }
  5688. switch ($attributeType) {
  5689. case 'int':
  5690. if (!is_numeric($value) && $value != '') {
  5691. $this->errors[] = $langs->trans("ExtraFieldHasWrongValue", $attributeLabel);
  5692. return -1;
  5693. } elseif ($value == '') {
  5694. $new_array_options[$key] = null;
  5695. }
  5696. break;
  5697. case 'price':
  5698. case 'double':
  5699. $value = price2num($value);
  5700. if (!is_numeric($value) && $value != '') {
  5701. dol_syslog($langs->trans("ExtraFieldHasWrongValue")." for ".$attributeLabel."(".$value."is not '".$attributeType."')", LOG_DEBUG);
  5702. $this->errors[] = $langs->trans("ExtraFieldHasWrongValue", $attributeLabel);
  5703. return -1;
  5704. } elseif ($value == '') {
  5705. $value = null;
  5706. }
  5707. //dol_syslog("double value"." sur ".$attributeLabel."(".$value." is '".$attributeType."')", LOG_DEBUG);
  5708. $new_array_options[$key] = $value;
  5709. break;
  5710. /*case 'select': // Not required, we chosed value='0' for undefined values
  5711. if ($value=='-1')
  5712. {
  5713. $this->array_options[$key] = null;
  5714. }
  5715. break;*/
  5716. case 'password':
  5717. $algo = '';
  5718. if ($this->array_options[$key] != '' && is_array($extrafields->attributes[$this->table_element]['param'][$attributeKey]['options'])) {
  5719. // If there is an encryption choice, we use it to crypt data before insert
  5720. $tmparrays = array_keys($extrafields->attributes[$this->table_element]['param'][$attributeKey]['options']);
  5721. $algo = reset($tmparrays);
  5722. if ($algo != '') {
  5723. //global $action; // $action may be 'create', 'update', 'update_extras'...
  5724. //var_dump($action);
  5725. //var_dump($this->oldcopy);exit;
  5726. if (is_object($this->oldcopy)) { // If this->oldcopy is not defined, we can't know if we change attribute or not, so we must keep value
  5727. //var_dump('iii'.$algo.' '.$this->oldcopy->array_options[$key].' -> '.$this->array_options[$key]);
  5728. if (isset($this->oldcopy->array_options[$key]) && $this->array_options[$key] == $this->oldcopy->array_options[$key]) {
  5729. // If old value crypted in database is same than submited new value, it means we don't change it, so we don't update.
  5730. if ($algo == 'dolcrypt') { // dolibarr reversible encryption
  5731. if (!preg_match('/^dolcrypt:/', $this->array_options[$key])) {
  5732. $new_array_options[$key] = dolEncrypt($this->array_options[$key]); // warning, must be called when on the master
  5733. } else {
  5734. $new_array_options[$key] = $this->array_options[$key]; // Value is kept
  5735. }
  5736. } else {
  5737. $new_array_options[$key] = $this->array_options[$key]; // Value is kept
  5738. }
  5739. } else {
  5740. // If value has changed
  5741. if ($algo == 'dolcrypt') { // dolibarr reversible encryption
  5742. if (!preg_match('/^dolcrypt:/', $this->array_options[$key])) {
  5743. $new_array_options[$key] = dolEncrypt($this->array_options[$key]); // warning, must be called when on the master
  5744. } else {
  5745. $new_array_options[$key] = $this->array_options[$key]; // Value is kept
  5746. }
  5747. } else {
  5748. $new_array_options[$key] = dol_hash($this->array_options[$key], $algo);
  5749. }
  5750. }
  5751. } else {
  5752. //var_dump('jjj'.$algo.' '.$this->oldcopy->array_options[$key].' -> '.$this->array_options[$key]);
  5753. // If this->oldcopy is not defined, we can't know if we change attribute or not, so we must keep value
  5754. if ($algo == 'dolcrypt' && !preg_match('/^dolcrypt:/', $this->array_options[$key])) { // dolibarr reversible encryption
  5755. $new_array_options[$key] = dolEncrypt($this->array_options[$key]); // warning, must be called when on the master
  5756. } else {
  5757. $new_array_options[$key] = $this->array_options[$key]; // Value is kept
  5758. }
  5759. }
  5760. } else {
  5761. // No encryption
  5762. $new_array_options[$key] = $this->array_options[$key]; // Value is kept
  5763. }
  5764. } else { // Common usage
  5765. $new_array_options[$key] = $this->array_options[$key]; // Value is kept
  5766. }
  5767. break;
  5768. case 'date':
  5769. case 'datetime':
  5770. // If data is a string instead of a timestamp, we convert it
  5771. if (!is_numeric($this->array_options[$key]) || $this->array_options[$key] != intval($this->array_options[$key])) {
  5772. $this->array_options[$key] = strtotime($this->array_options[$key]);
  5773. }
  5774. $new_array_options[$key] = $this->db->idate($this->array_options[$key]);
  5775. break;
  5776. case 'datetimegmt':
  5777. // If data is a string instead of a timestamp, we convert it
  5778. if (!is_numeric($this->array_options[$key]) || $this->array_options[$key] != intval($this->array_options[$key])) {
  5779. $this->array_options[$key] = strtotime($this->array_options[$key]);
  5780. }
  5781. $new_array_options[$key] = $this->db->idate($this->array_options[$key], 'gmt');
  5782. break;
  5783. case 'link':
  5784. $param_list = array_keys($attributeParam['options']);
  5785. // 0 : ObjectName
  5786. // 1 : classPath
  5787. $InfoFieldList = explode(":", $param_list[0]);
  5788. dol_include_once($InfoFieldList[1]);
  5789. if ($InfoFieldList[0] && class_exists($InfoFieldList[0])) {
  5790. if ($value == '-1') { // -1 is key for no defined in combo list of objects
  5791. $new_array_options[$key] = '';
  5792. } elseif ($value) {
  5793. $object = new $InfoFieldList[0]($this->db);
  5794. if (is_numeric($value)) {
  5795. $res = $object->fetch($value); // Common case
  5796. } else {
  5797. $res = $object->fetch('', $value); // For compatibility
  5798. }
  5799. if ($res > 0) {
  5800. $new_array_options[$key] = $object->id;
  5801. } else {
  5802. $this->error = "Id/Ref '".$value."' for object '".$object->element."' not found";
  5803. return -1;
  5804. }
  5805. }
  5806. } else {
  5807. dol_syslog('Error bad setup of extrafield', LOG_WARNING);
  5808. }
  5809. break;
  5810. case 'checkbox':
  5811. case 'chkbxlst':
  5812. if (is_array($this->array_options[$key])) {
  5813. $new_array_options[$key] = implode(',', $this->array_options[$key]);
  5814. } else {
  5815. $new_array_options[$key] = $this->array_options[$key];
  5816. }
  5817. break;
  5818. }
  5819. }
  5820. $this->db->begin();
  5821. $table_element = $this->table_element;
  5822. if ($table_element == 'categorie') {
  5823. $table_element = 'categories'; // For compatibility
  5824. }
  5825. dol_syslog(get_class($this)."::insertExtraFields delete then insert", LOG_DEBUG);
  5826. $sql_del = "DELETE FROM ".$this->db->prefix().$table_element."_extrafields WHERE fk_object = ".((int) $this->id);
  5827. $this->db->query($sql_del);
  5828. $sql = "INSERT INTO ".$this->db->prefix().$table_element."_extrafields (fk_object";
  5829. foreach ($new_array_options as $key => $value) {
  5830. $attributeKey = substr($key, 8); // Remove 'options_' prefix
  5831. // Add field of attribut
  5832. if ($extrafields->attributes[$this->table_element]['type'][$attributeKey] != 'separate') { // Only for other type than separator
  5833. $sql .= ",".$attributeKey;
  5834. }
  5835. }
  5836. // We must insert a default value for fields for other entities that are mandatory to avoid not null error
  5837. if (!empty($extrafields->attributes[$this->table_element]['mandatoryfieldsofotherentities']) && is_array($extrafields->attributes[$this->table_element]['mandatoryfieldsofotherentities'])) {
  5838. foreach ($extrafields->attributes[$this->table_element]['mandatoryfieldsofotherentities'] as $tmpkey => $tmpval) {
  5839. if (!isset($extrafields->attributes[$this->table_element]['type'][$tmpkey])) { // If field not already added previously
  5840. $sql .= ",".$tmpkey;
  5841. }
  5842. }
  5843. }
  5844. $sql .= ") VALUES (".$this->id;
  5845. foreach ($new_array_options as $key => $value) {
  5846. $attributeKey = substr($key, 8); // Remove 'options_' prefix
  5847. // Add field of attribute
  5848. if ($extrafields->attributes[$this->table_element]['type'][$attributeKey] != 'separate') { // Only for other type than separator)
  5849. if ($new_array_options[$key] != '' || $new_array_options[$key] == '0') {
  5850. $sql .= ",'".$this->db->escape($new_array_options[$key])."'";
  5851. } else {
  5852. $sql .= ",null";
  5853. }
  5854. }
  5855. }
  5856. // We must insert a default value for fields for other entities that are mandatory to avoid not null error
  5857. if (!empty($extrafields->attributes[$this->table_element]['mandatoryfieldsofotherentities']) && is_array($extrafields->attributes[$this->table_element]['mandatoryfieldsofotherentities'])) {
  5858. foreach ($extrafields->attributes[$this->table_element]['mandatoryfieldsofotherentities'] as $tmpkey => $tmpval) {
  5859. if (!isset($extrafields->attributes[$this->table_element]['type'][$tmpkey])) { // If field not already added previously
  5860. if (in_array($tmpval, array('int', 'double', 'price'))) {
  5861. $sql .= ", 0";
  5862. } else {
  5863. $sql .= ", ''";
  5864. }
  5865. }
  5866. }
  5867. }
  5868. $sql .= ")";
  5869. $resql = $this->db->query($sql);
  5870. if (!$resql) {
  5871. $this->error = $this->db->lasterror();
  5872. $error++;
  5873. }
  5874. if (!$error && $trigger) {
  5875. // Call trigger
  5876. $this->context = array('extrafieldaddupdate'=>1);
  5877. $result = $this->call_trigger($trigger, $userused);
  5878. if ($result < 0) {
  5879. $error++;
  5880. }
  5881. // End call trigger
  5882. }
  5883. if ($error) {
  5884. $this->db->rollback();
  5885. return -1;
  5886. } else {
  5887. $this->db->commit();
  5888. return 1;
  5889. }
  5890. } else {
  5891. return 0;
  5892. }
  5893. }
  5894. /**
  5895. * Add/Update all extra fields values for the current object.
  5896. * Data to describe values to insert/update are stored into $this->array_options=array('options_codeforfield1'=>'valueforfield1', 'options_codeforfield2'=>'valueforfield2', ...)
  5897. * This function delete record with all extrafields and insert them again from the array $this->array_options.
  5898. *
  5899. * @param string $trigger If defined, call also the trigger (for example COMPANY_MODIFY)
  5900. * @param User $userused Object user
  5901. * @return int -1=error, O=did nothing, 1=OK
  5902. * @see insertExtraFields(), updateExtraField(), setValueFrom()
  5903. */
  5904. public function insertExtraLanguages($trigger = '', $userused = null)
  5905. {
  5906. global $conf, $langs, $user;
  5907. if (empty($userused)) {
  5908. $userused = $user;
  5909. }
  5910. $error = 0;
  5911. if (getDolGlobalString('MAIN_EXTRALANGUAGES_DISABLED')) {
  5912. return 0; // For avoid conflicts if trigger used
  5913. }
  5914. if (is_array($this->array_languages)) {
  5915. $new_array_languages = $this->array_languages;
  5916. foreach ($new_array_languages as $key => $value) {
  5917. $attributeKey = $key;
  5918. $attributeType = $this->fields[$attributeKey]['type'];
  5919. $attributeLabel = $this->fields[$attributeKey]['label'];
  5920. //dol_syslog("attributeLabel=".$attributeLabel, LOG_DEBUG);
  5921. //dol_syslog("attributeType=".$attributeType, LOG_DEBUG);
  5922. switch ($attributeType) {
  5923. case 'int':
  5924. if (!is_numeric($value) && $value != '') {
  5925. $this->errors[] = $langs->trans("ExtraLanguageHasWrongValue", $attributeLabel);
  5926. return -1;
  5927. } elseif ($value == '') {
  5928. $new_array_languages[$key] = null;
  5929. }
  5930. break;
  5931. case 'double':
  5932. $value = price2num($value);
  5933. if (!is_numeric($value) && $value != '') {
  5934. dol_syslog($langs->trans("ExtraLanguageHasWrongValue")." sur ".$attributeLabel."(".$value."is not '".$attributeType."')", LOG_DEBUG);
  5935. $this->errors[] = $langs->trans("ExtraLanguageHasWrongValue", $attributeLabel);
  5936. return -1;
  5937. } elseif ($value == '') {
  5938. $new_array_languages[$key] = null;
  5939. } else {
  5940. $new_array_languages[$key] = $value;
  5941. }
  5942. break;
  5943. /*case 'select': // Not required, we chosed value='0' for undefined values
  5944. if ($value=='-1')
  5945. {
  5946. $this->array_options[$key] = null;
  5947. }
  5948. break;*/
  5949. }
  5950. }
  5951. $this->db->begin();
  5952. $table_element = $this->table_element;
  5953. if ($table_element == 'categorie') {
  5954. $table_element = 'categories'; // For compatibility
  5955. }
  5956. dol_syslog(get_class($this)."::insertExtraLanguages delete then insert", LOG_DEBUG);
  5957. foreach ($new_array_languages as $key => $langcodearray) { // $key = 'name', 'town', ...
  5958. foreach ($langcodearray as $langcode => $value) {
  5959. $sql_del = "DELETE FROM ".$this->db->prefix()."object_lang";
  5960. $sql_del .= " WHERE fk_object = ".((int) $this->id)." AND property = '".$this->db->escape($key)."' AND type_object = '".$this->db->escape($table_element)."'";
  5961. $sql_del .= " AND lang = '".$this->db->escape($langcode)."'";
  5962. $this->db->query($sql_del);
  5963. if ($value !== '') {
  5964. $sql = "INSERT INTO ".$this->db->prefix()."object_lang (fk_object, property, type_object, lang, value";
  5965. $sql .= ") VALUES (".$this->id.", '".$this->db->escape($key)."', '".$this->db->escape($table_element)."', '".$this->db->escape($langcode)."', '".$this->db->escape($value)."'";
  5966. $sql .= ")";
  5967. $resql = $this->db->query($sql);
  5968. if (!$resql) {
  5969. $this->error = $this->db->lasterror();
  5970. $error++;
  5971. break;
  5972. }
  5973. }
  5974. }
  5975. }
  5976. if (!$error && $trigger) {
  5977. // Call trigger
  5978. $this->context = array('extralanguagesaddupdate'=>1);
  5979. $result = $this->call_trigger($trigger, $userused);
  5980. if ($result < 0) {
  5981. $error++;
  5982. }
  5983. // End call trigger
  5984. }
  5985. if ($error) {
  5986. $this->db->rollback();
  5987. return -1;
  5988. } else {
  5989. $this->db->commit();
  5990. return 1;
  5991. }
  5992. } else {
  5993. return 0;
  5994. }
  5995. }
  5996. /**
  5997. * Update 1 extra field value for the current object. Keep other fields unchanged.
  5998. * Data to describe values to update are stored into $this->array_options=array('options_codeforfield1'=>'valueforfield1', 'options_codeforfield2'=>'valueforfield2', ...)
  5999. *
  6000. * @param string $key Key of the extrafield to update (without starting 'options_')
  6001. * @param string $trigger If defined, call also the trigger (for example COMPANY_MODIFY)
  6002. * @param User $userused Object user
  6003. * @return int -1=error, O=did nothing, 1=OK
  6004. * @see updateExtraLanguages(), insertExtraFields(), deleteExtraFields(), setValueFrom()
  6005. */
  6006. public function updateExtraField($key, $trigger = null, $userused = null)
  6007. {
  6008. global $conf, $langs, $user;
  6009. if (getDolGlobalString('MAIN_EXTRAFIELDS_DISABLED')) {
  6010. return 0;
  6011. }
  6012. if (empty($userused)) {
  6013. $userused = $user;
  6014. }
  6015. $error = 0;
  6016. if (!empty($this->array_options) && isset($this->array_options["options_".$key])) {
  6017. // Check parameters
  6018. $langs->load('admin');
  6019. require_once DOL_DOCUMENT_ROOT.'/core/class/extrafields.class.php';
  6020. $extrafields = new ExtraFields($this->db);
  6021. $extrafields->fetch_name_optionals_label($this->table_element);
  6022. $value = $this->array_options["options_".$key];
  6023. $attributeKey = $key;
  6024. $attributeType = $extrafields->attributes[$this->table_element]['type'][$key];
  6025. $attributeLabel = $extrafields->attributes[$this->table_element]['label'][$key];
  6026. $attributeParam = $extrafields->attributes[$this->table_element]['param'][$key];
  6027. $attributeRequired = $extrafields->attributes[$this->table_element]['required'][$key];
  6028. $attributeUnique = $extrafields->attributes[$this->table_element]['unique'][$attributeKey];
  6029. $attrfieldcomputed = $extrafields->attributes[$this->table_element]['computed'][$key];
  6030. // Similar code than into insertExtraFields
  6031. if ($attributeRequired) {
  6032. $mandatorypb = false;
  6033. if ($attributeType == 'link' && $this->array_options["options_".$key] == '-1') {
  6034. $mandatorypb = true;
  6035. }
  6036. if ($this->array_options["options_".$key] === '') {
  6037. $mandatorypb = true;
  6038. }
  6039. if ($mandatorypb) {
  6040. $langs->load("errors");
  6041. dol_syslog("Mandatory field 'options_".$key."' is empty during update and set to required into definition of extrafields");
  6042. $this->errors[] = $langs->trans('ErrorFieldRequired', $attributeLabel);
  6043. return -1;
  6044. }
  6045. }
  6046. // $new_array_options will be used for direct update, so must contains formated data for the UPDATE.
  6047. $new_array_options = $this->array_options;
  6048. //dol_syslog("attributeLabel=".$attributeLabel, LOG_DEBUG);
  6049. //dol_syslog("attributeType=".$attributeType, LOG_DEBUG);
  6050. if (!empty($attrfieldcomputed)) {
  6051. if (getDolGlobalString('MAIN_STORE_COMPUTED_EXTRAFIELDS')) {
  6052. $value = dol_eval($attrfieldcomputed, 1, 0, '2');
  6053. dol_syslog($langs->trans("Extrafieldcomputed")." sur ".$attributeLabel."(".$value.")", LOG_DEBUG);
  6054. $new_array_options["options_".$key] = $value;
  6055. $this->array_options["options_".$key] = $new_array_options["options_".$key];
  6056. } else {
  6057. $new_array_options["options_".$key] = null;
  6058. $this->array_options["options_".$key] = $new_array_options["options_".$key];
  6059. }
  6060. }
  6061. switch ($attributeType) {
  6062. case 'int':
  6063. if (!is_numeric($value) && $value != '') {
  6064. $this->errors[] = $langs->trans("ExtraFieldHasWrongValue", $attributeLabel);
  6065. return -1;
  6066. } elseif ($value === '') {
  6067. $new_array_options["options_".$key] = null;
  6068. $this->array_options["options_".$key] = $new_array_options["options_".$key];
  6069. }
  6070. break;
  6071. case 'double':
  6072. $value = price2num($value);
  6073. if (!is_numeric($value) && $value != '') {
  6074. dol_syslog($langs->trans("ExtraFieldHasWrongValue")." sur ".$attributeLabel."(".$value."is not '".$attributeType."')", LOG_DEBUG);
  6075. $this->errors[] = $langs->trans("ExtraFieldHasWrongValue", $attributeLabel);
  6076. return -1;
  6077. } elseif ($value === '') {
  6078. $value = null;
  6079. }
  6080. //dol_syslog("double value"." sur ".$attributeLabel."(".$value." is '".$attributeType."')", LOG_DEBUG);
  6081. $new_array_options["options_".$key] = $value;
  6082. $this->array_options["options_".$key] = $new_array_options["options_".$key];
  6083. break;
  6084. /*case 'select': // Not required, we chosed value='0' for undefined values
  6085. if ($value=='-1')
  6086. {
  6087. $new_array_options["options_".$key] = $value;
  6088. $this->array_options["options_".$key] = $new_array_options["options_".$key];
  6089. }
  6090. break;*/
  6091. case 'price':
  6092. $new_array_options["options_".$key] = price2num($this->array_options["options_".$key]);
  6093. $this->array_options["options_".$key] = $new_array_options["options_".$key];
  6094. break;
  6095. case 'password':
  6096. $algo = '';
  6097. if ($this->array_options["options_".$key] != '' && is_array($extrafields->attributes[$this->table_element]['param'][$attributeKey]['options'])) {
  6098. // If there is an encryption choice, we use it to crypt data before insert
  6099. $tmparrays = array_keys($extrafields->attributes[$this->table_element]['param'][$attributeKey]['options']);
  6100. $algo = reset($tmparrays);
  6101. if ($algo != '') {
  6102. //global $action; // $action may be 'create', 'update', 'update_extras'...
  6103. //var_dump($action);
  6104. //var_dump($this->oldcopy);exit;
  6105. //var_dump($key.' '.$this->array_options["options_".$key].' '.$algo);
  6106. if (is_object($this->oldcopy)) { // If this->oldcopy is not defined, we can't know if we change attribute or not, so we must keep value
  6107. //var_dump($this->oldcopy->array_options["options_".$key]); var_dump($this->array_options["options_".$key]);
  6108. if (isset($this->oldcopy->array_options["options_".$key]) && $this->array_options["options_".$key] == $this->oldcopy->array_options["options_".$key]) { // If old value crypted in database is same than submited new value, it means we don't change it, so we don't update.
  6109. if ($algo == 'dolcrypt') { // dolibarr reversible encryption
  6110. if (!preg_match('/^dolcrypt:/', $this->array_options["options_".$key])) {
  6111. $new_array_options["options_".$key] = dolEncrypt($this->array_options["options_".$key]); // warning, must be called when on the master
  6112. } else {
  6113. $new_array_options["options_".$key] = $this->array_options["options_".$key]; // Value is kept
  6114. }
  6115. } else {
  6116. $new_array_options["options_".$key] = $this->array_options["options_".$key]; // Value is kept
  6117. }
  6118. } else {
  6119. if ($algo == 'dolcrypt') { // dolibarr reversible encryption
  6120. if (!preg_match('/^dolcrypt:/', $this->array_options["options_".$key])) {
  6121. $new_array_options["options_".$key] = dolEncrypt($this->array_options["options_".$key]);
  6122. } else {
  6123. $new_array_options["options_".$key] = $this->array_options["options_".$key]; // Value is kept
  6124. }
  6125. } else {
  6126. $new_array_options["options_".$key] = dol_hash($this->array_options["options_".$key], $algo);
  6127. }
  6128. }
  6129. } else {
  6130. if ($algo == 'dolcrypt' && !preg_match('/^dolcrypt:/', $this->array_options["options_".$key])) { // dolibarr reversible encryption
  6131. $new_array_options["options_".$key] = dolEncrypt($this->array_options["options_".$key]); // warning, must be called when on the master
  6132. } else {
  6133. $new_array_options["options_".$key] = $this->array_options["options_".$key]; // Value is kept
  6134. }
  6135. }
  6136. } else {
  6137. // No encryption
  6138. $new_array_options["options_".$key] = $this->array_options["options_".$key]; // Value is kept
  6139. }
  6140. } else { // Common usage
  6141. $new_array_options["options_".$key] = $this->array_options["options_".$key]; // Value is kept
  6142. }
  6143. $this->array_options["options_".$key] = $new_array_options["options_".$key];
  6144. break;
  6145. case 'date':
  6146. case 'datetime':
  6147. if (empty($this->array_options["options_".$key])) {
  6148. $new_array_options["options_".$key] = null;
  6149. $this->array_options["options_".$key] = $new_array_options["options_".$key];
  6150. } else {
  6151. $new_array_options["options_".$key] = $this->db->idate($this->array_options["options_".$key]);
  6152. }
  6153. break;
  6154. case 'datetimegmt':
  6155. if (empty($this->array_options["options_".$key])) {
  6156. $new_array_options["options_".$key] = null;
  6157. $this->array_options["options_".$key] = $new_array_options["options_".$key];
  6158. } else {
  6159. $new_array_options["options_".$key] = $this->db->idate($this->array_options["options_".$key], 'gmt');
  6160. }
  6161. break;
  6162. case 'boolean':
  6163. if (empty($this->array_options["options_".$key])) {
  6164. $new_array_options["options_".$key] = null;
  6165. $this->array_options["options_".$key] = $new_array_options["options_".$key];
  6166. }
  6167. break;
  6168. case 'link':
  6169. if ($this->array_options["options_".$key] === '') {
  6170. $new_array_options["options_".$key] = null;
  6171. $this->array_options["options_".$key] = $new_array_options["options_".$key];
  6172. }
  6173. break;
  6174. /*
  6175. case 'link':
  6176. $param_list = array_keys($attributeParam['options']);
  6177. // 0 : ObjectName
  6178. // 1 : classPath
  6179. $InfoFieldList = explode(":", $param_list[0]);
  6180. dol_include_once($InfoFieldList[1]);
  6181. if ($InfoFieldList[0] && class_exists($InfoFieldList[0]))
  6182. {
  6183. if ($value == '-1') // -1 is key for no defined in combo list of objects
  6184. {
  6185. $new_array_options[$key] = '';
  6186. } elseif ($value) {
  6187. $object = new $InfoFieldList[0]($this->db);
  6188. if (is_numeric($value)) $res = $object->fetch($value); // Common case
  6189. else $res = $object->fetch('', $value); // For compatibility
  6190. if ($res > 0) $new_array_options[$key] = $object->id;
  6191. else {
  6192. $this->error = "Id/Ref '".$value."' for object '".$object->element."' not found";
  6193. $this->db->rollback();
  6194. return -1;
  6195. }
  6196. }
  6197. } else {
  6198. dol_syslog('Error bad setup of extrafield', LOG_WARNING);
  6199. }
  6200. break;
  6201. */
  6202. case 'checkbox':
  6203. case 'chkbxlst':
  6204. $new_array_options = array();
  6205. if (is_array($this->array_options["options_".$key])) {
  6206. $new_array_options["options_".$key] = implode(',', $this->array_options["options_".$key]);
  6207. } else {
  6208. $new_array_options["options_".$key] = $this->array_options["options_".$key];
  6209. }
  6210. $this->array_options["options_".$key] = $new_array_options["options_".$key];
  6211. break;
  6212. }
  6213. $this->db->begin();
  6214. $linealreadyfound = 0;
  6215. // Check if there is already a line for this object (in most cases, it is, but sometimes it is not, for example when extra field has been created after), so we must keep this overload)
  6216. $sql = "SELECT COUNT(rowid) as nb FROM ".$this->db->prefix().$this->table_element."_extrafields WHERE fk_object = ".((int) $this->id);
  6217. $resql = $this->db->query($sql);
  6218. if ($resql) {
  6219. $tmpobj = $this->db->fetch_object($resql);
  6220. if ($tmpobj) {
  6221. $linealreadyfound = $tmpobj->nb;
  6222. }
  6223. }
  6224. //var_dump('linealreadyfound='.$linealreadyfound.' sql='.$sql); exit;
  6225. if ($linealreadyfound) {
  6226. if ($this->array_options["options_".$key] === null) {
  6227. $sql = "UPDATE ".$this->db->prefix().$this->table_element."_extrafields SET ".$key." = null";
  6228. } else {
  6229. $sql = "UPDATE ".$this->db->prefix().$this->table_element."_extrafields SET ".$key." = '".$this->db->escape($new_array_options["options_".$key])."'";
  6230. }
  6231. $sql .= " WHERE fk_object = ".((int) $this->id);
  6232. $resql = $this->db->query($sql);
  6233. if (!$resql) {
  6234. $error++;
  6235. $this->error = $this->db->lasterror();
  6236. }
  6237. } else {
  6238. $result = $this->insertExtraFields('', $user);
  6239. if ($result < 0) {
  6240. $error++;
  6241. }
  6242. }
  6243. if (!$error && $trigger) {
  6244. // Call trigger
  6245. $this->context = array('extrafieldupdate'=>1);
  6246. $result = $this->call_trigger($trigger, $userused);
  6247. if ($result < 0) {
  6248. $error++;
  6249. }
  6250. // End call trigger
  6251. }
  6252. if ($error) {
  6253. dol_syslog(__METHOD__.$this->error, LOG_ERR);
  6254. $this->db->rollback();
  6255. return -1;
  6256. } else {
  6257. $this->db->commit();
  6258. return 1;
  6259. }
  6260. } else {
  6261. return 0;
  6262. }
  6263. }
  6264. /**
  6265. * Update an extra language value for the current object.
  6266. * Data to describe values to update are stored into $this->array_options=array('options_codeforfield1'=>'valueforfield1', 'options_codeforfield2'=>'valueforfield2', ...)
  6267. *
  6268. * @param string $key Key of the extrafield (without starting 'options_')
  6269. * @param string $trigger If defined, call also the trigger (for example COMPANY_MODIFY)
  6270. * @param User $userused Object user
  6271. * @return int -1=error, O=did nothing, 1=OK
  6272. * @see updateExtraFields(), insertExtraLanguages()
  6273. */
  6274. public function updateExtraLanguages($key, $trigger = null, $userused = null)
  6275. {
  6276. global $conf, $langs, $user;
  6277. if (empty($userused)) {
  6278. $userused = $user;
  6279. }
  6280. $error = 0;
  6281. if (getDolGlobalString('MAIN_EXTRALANGUAGES_DISABLED')) {
  6282. return 0; // For avoid conflicts if trigger used
  6283. }
  6284. return 0;
  6285. }
  6286. /**
  6287. * Return HTML string to put an input field into a page
  6288. * Code very similar with showInputField of extra fields
  6289. *
  6290. * @param array|null $val Array of properties for field to show (used only if ->fields not defined)
  6291. * @param string $key Key of attribute
  6292. * @param string|array $value Preselected value to show (for date type it must be in timestamp format, for amount or price it must be a php numeric value, for array type must be array)
  6293. * @param string $moreparam To add more parameters on html input tag
  6294. * @param string $keysuffix Prefix string to add into name and id of field (can be used to avoid duplicate names)
  6295. * @param string $keyprefix Suffix string to add into name and id of field (can be used to avoid duplicate names)
  6296. * @param string|int $morecss Value for css to define style/length of field. May also be a numeric.
  6297. * @param int $nonewbutton Force to not show the new button on field that are links to object
  6298. * @return string
  6299. */
  6300. public function showInputField($val, $key, $value, $moreparam = '', $keysuffix = '', $keyprefix = '', $morecss = 0, $nonewbutton = 0)
  6301. {
  6302. global $conf, $langs, $form;
  6303. if (!is_object($form)) {
  6304. require_once DOL_DOCUMENT_ROOT.'/core/class/html.form.class.php';
  6305. $form = new Form($this->db);
  6306. }
  6307. if (!empty($this->fields)) {
  6308. $val = $this->fields[$key];
  6309. }
  6310. // Validation tests and output
  6311. $fieldValidationErrorMsg = '';
  6312. $validationClass = '';
  6313. $fieldValidationErrorMsg = $this->getFieldError($key);
  6314. if (!empty($fieldValidationErrorMsg)) {
  6315. $validationClass = ' --error'; // the -- is use as class state in css : .--error can't be be defined alone it must be define with another class like .my-class.--error or input.--error
  6316. } else {
  6317. $validationClass = ' --success'; // the -- is use as class state in css : .--success can't be be defined alone it must be define with another class like .my-class.--success or input.--success
  6318. }
  6319. $out = '';
  6320. $type = '';
  6321. $isDependList = 0;
  6322. $param = array();
  6323. $param['options'] = array();
  6324. $reg = array();
  6325. $size = !empty($this->fields[$key]['size']) ? $this->fields[$key]['size'] : 0;
  6326. // Because we work on extrafields
  6327. if (preg_match('/^(integer|link):(.*):(.*):(.*):(.*)/i', $val['type'], $reg)) {
  6328. $param['options'] = array($reg[2].':'.$reg[3].':'.$reg[4].':'.$reg[5] => 'N');
  6329. $type = 'link';
  6330. } elseif (preg_match('/^(integer|link):(.*):(.*):(.*)/i', $val['type'], $reg)) {
  6331. $param['options'] = array($reg[2].':'.$reg[3].':'.$reg[4] => 'N');
  6332. $type = 'link';
  6333. } elseif (preg_match('/^(integer|link):(.*):(.*)/i', $val['type'], $reg)) {
  6334. $param['options'] = array($reg[2].':'.$reg[3] => 'N');
  6335. $type = 'link';
  6336. } elseif (preg_match('/^(sellist):(.*):(.*):(.*):(.*)/i', $val['type'], $reg)) {
  6337. $param['options'] = array($reg[2].':'.$reg[3].':'.$reg[4].':'.$reg[5] => 'N');
  6338. $type = 'sellist';
  6339. } elseif (preg_match('/^(sellist):(.*):(.*):(.*)/i', $val['type'], $reg)) {
  6340. $param['options'] = array($reg[2].':'.$reg[3].':'.$reg[4] => 'N');
  6341. $type = 'sellist';
  6342. } elseif (preg_match('/^(sellist):(.*):(.*)/i', $val['type'], $reg)) {
  6343. $param['options'] = array($reg[2].':'.$reg[3] => 'N');
  6344. $type = 'sellist';
  6345. } elseif (preg_match('/^chkbxlst:(.*)/i', $val['type'], $reg)) {
  6346. $param['options'] = array($reg[1] => 'N');
  6347. $type = 'chkbxlst';
  6348. } elseif (preg_match('/varchar\((\d+)\)/', $val['type'], $reg)) {
  6349. $param['options'] = array();
  6350. $type = 'varchar';
  6351. $size = $reg[1];
  6352. } elseif (preg_match('/varchar/', $val['type'])) {
  6353. $param['options'] = array();
  6354. $type = 'varchar';
  6355. } else {
  6356. $param['options'] = array();
  6357. $type = $this->fields[$key]['type'];
  6358. }
  6359. //var_dump($type); var_dump($param['options']);
  6360. // Special case that force options and type ($type can be integer, varchar, ...)
  6361. if (!empty($this->fields[$key]['arrayofkeyval']) && is_array($this->fields[$key]['arrayofkeyval'])) {
  6362. $param['options'] = $this->fields[$key]['arrayofkeyval'];
  6363. $type = (($this->fields[$key]['type']=='checkbox') ? $this->fields[$key]['type'] : 'select');
  6364. }
  6365. $label = $this->fields[$key]['label'];
  6366. //$elementtype=$this->fields[$key]['elementtype']; // Seems not used
  6367. $default = (!empty($this->fields[$key]['default']) ? $this->fields[$key]['default'] : '');
  6368. $computed = (!empty($this->fields[$key]['computed']) ? $this->fields[$key]['computed'] : '');
  6369. $unique = (!empty($this->fields[$key]['unique']) ? $this->fields[$key]['unique'] : 0);
  6370. $required = (!empty($this->fields[$key]['required']) ? $this->fields[$key]['required'] : 0);
  6371. $autofocusoncreate = (!empty($this->fields[$key]['autofocusoncreate']) ? $this->fields[$key]['autofocusoncreate'] : 0);
  6372. $langfile = (!empty($this->fields[$key]['langfile']) ? $this->fields[$key]['langfile'] : '');
  6373. $list = (!empty($this->fields[$key]['list']) ? $this->fields[$key]['list'] : 0);
  6374. $hidden = (in_array(abs($this->fields[$key]['visible']), array(0, 2)) ? 1 : 0);
  6375. $objectid = $this->id;
  6376. if ($computed) {
  6377. if (!preg_match('/^search_/', $keyprefix)) {
  6378. return '<span class="opacitymedium">'.$langs->trans("AutomaticallyCalculated").'</span>';
  6379. } else {
  6380. return '';
  6381. }
  6382. }
  6383. // Set value of $morecss. For this, we use in priority showsize from parameters, then $val['css'] then autodefine
  6384. if (empty($morecss) && !empty($val['css'])) {
  6385. $morecss = $val['css'];
  6386. } elseif (empty($morecss)) {
  6387. if ($type == 'date') {
  6388. $morecss = 'minwidth100imp';
  6389. } elseif ($type == 'datetime' || $type == 'link') { // link means an foreign key to another primary id
  6390. $morecss = 'minwidth200imp';
  6391. } elseif (in_array($type, array('int', 'integer', 'price')) || preg_match('/^double(\([0-9],[0-9]\)){0,1}/', $type)) {
  6392. $morecss = 'maxwidth75';
  6393. } elseif ($type == 'url') {
  6394. $morecss = 'minwidth400';
  6395. } elseif ($type == 'boolean') {
  6396. $morecss = '';
  6397. } else {
  6398. if (round($size) < 12) {
  6399. $morecss = 'minwidth100';
  6400. } elseif (round($size) <= 48) {
  6401. $morecss = 'minwidth200';
  6402. } else {
  6403. $morecss = 'minwidth400';
  6404. }
  6405. }
  6406. }
  6407. // Add validation state class
  6408. if (!empty($validationClass)) {
  6409. $morecss.= $validationClass;
  6410. }
  6411. if (in_array($type, array('date'))) {
  6412. $tmp = explode(',', $size);
  6413. $newsize = $tmp[0];
  6414. $showtime = 0;
  6415. // Do not show current date when field not required (see selectDate() method)
  6416. if (!$required && $value == '') {
  6417. $value = '-1';
  6418. }
  6419. // TODO Must also support $moreparam
  6420. $out = $form->selectDate($value, $keyprefix.$key.$keysuffix, $showtime, $showtime, $required, '', 1, (($keyprefix != 'search_' && $keyprefix != 'search_options_') ? 1 : 0), 0, 1);
  6421. } elseif (in_array($type, array('datetime'))) {
  6422. $tmp = explode(',', $size);
  6423. $newsize = $tmp[0];
  6424. $showtime = 1;
  6425. // Do not show current date when field not required (see selectDate() method)
  6426. if (!$required && $value == '') {
  6427. $value = '-1';
  6428. }
  6429. // TODO Must also support $moreparam
  6430. $out = $form->selectDate($value, $keyprefix.$key.$keysuffix, $showtime, $showtime, $required, '', 1, (($keyprefix != 'search_' && $keyprefix != 'search_options_') ? 1 : 0), 0, 1, '', '', '', 1, '', '', 'tzuserrel');
  6431. } elseif (in_array($type, array('duration'))) {
  6432. $out = $form->select_duration($keyprefix.$key.$keysuffix, $value, 0, 'text', 0, 1);
  6433. } elseif (in_array($type, array('int', 'integer'))) {
  6434. $tmp = explode(',', $size);
  6435. $newsize = $tmp[0];
  6436. $out = '<input type="text" class="flat '.$morecss.'" name="'.$keyprefix.$key.$keysuffix.'" id="'.$keyprefix.$key.$keysuffix.'"'.($newsize > 0 ? ' maxlength="'.$newsize.'"' : '').' value="'.dol_escape_htmltag($value).'"'.($moreparam ? $moreparam : '').($autofocusoncreate ? ' autofocus' : '').'>';
  6437. } elseif (in_array($type, array('real'))) {
  6438. $out = '<input type="text" class="flat '.$morecss.'" name="'.$keyprefix.$key.$keysuffix.'" id="'.$keyprefix.$key.$keysuffix.'" value="'.dol_escape_htmltag($value).'"'.($moreparam ? $moreparam : '').($autofocusoncreate ? ' autofocus' : '').'>';
  6439. } elseif (preg_match('/varchar/', $type)) {
  6440. $out = '<input type="text" class="flat '.$morecss.'" name="'.$keyprefix.$key.$keysuffix.'" id="'.$keyprefix.$key.$keysuffix.'"'.($size > 0 ? ' maxlength="'.$size.'"' : '').' value="'.dol_escape_htmltag($value).'"'.($moreparam ? $moreparam : '').($autofocusoncreate ? ' autofocus' : '').'>';
  6441. } elseif (in_array($type, array('email', 'mail', 'phone', 'url', 'ip'))) {
  6442. $out = '<input type="text" class="flat '.$morecss.'" name="'.$keyprefix.$key.$keysuffix.'" id="'.$keyprefix.$key.$keysuffix.'" value="'.dol_escape_htmltag($value).'" '.($moreparam ? $moreparam : '').($autofocusoncreate ? ' autofocus' : '').'>';
  6443. } elseif (preg_match('/^text/', $type)) {
  6444. if (!preg_match('/search_/', $keyprefix)) { // If keyprefix is search_ or search_options_, we must just use a simple text field
  6445. require_once DOL_DOCUMENT_ROOT.'/core/class/doleditor.class.php';
  6446. $doleditor = new DolEditor($keyprefix.$key.$keysuffix, $value, '', 200, 'dolibarr_notes', 'In', false, false, false, ROWS_5, '90%');
  6447. $out = $doleditor->Create(1);
  6448. } else {
  6449. $out = '<input type="text" class="flat '.$morecss.' maxwidthonsmartphone" name="'.$keyprefix.$key.$keysuffix.'" id="'.$keyprefix.$key.$keysuffix.'" value="'.dol_escape_htmltag($value).'" '.($moreparam ? $moreparam : '').'>';
  6450. }
  6451. } elseif (preg_match('/^html/', $type)) {
  6452. if (!preg_match('/search_/', $keyprefix)) { // If keyprefix is search_ or search_options_, we must just use a simple text field
  6453. require_once DOL_DOCUMENT_ROOT.'/core/class/doleditor.class.php';
  6454. $doleditor = new DolEditor($keyprefix.$key.$keysuffix, $value, '', 200, 'dolibarr_notes', 'In', false, false, isModEnabled('fckeditor') && $conf->global->FCKEDITOR_ENABLE_SOCIETE, ROWS_5, '90%');
  6455. $out = $doleditor->Create(1, '', true, '', '', $moreparam, $morecss);
  6456. } else {
  6457. $out = '<input type="text" class="flat '.$morecss.' maxwidthonsmartphone" name="'.$keyprefix.$key.$keysuffix.'" id="'.$keyprefix.$key.$keysuffix.'" value="'.dol_escape_htmltag($value).'" '.($moreparam ? $moreparam : '').'>';
  6458. }
  6459. } elseif ($type == 'boolean') {
  6460. $checked = '';
  6461. if (!empty($value)) {
  6462. $checked = ' checked value="1" ';
  6463. } else {
  6464. $checked = ' value="1" ';
  6465. }
  6466. $out = '<input type="checkbox" class="flat '.$morecss.' maxwidthonsmartphone" name="'.$keyprefix.$key.$keysuffix.'" id="'.$keyprefix.$key.$keysuffix.'" '.$checked.' '.($moreparam ? $moreparam : '').'>';
  6467. } elseif ($type == 'price') {
  6468. if (!empty($value)) { // $value in memory is a php numeric, we format it into user number format.
  6469. $value = price($value);
  6470. }
  6471. $out = '<input type="text" class="flat '.$morecss.' maxwidthonsmartphone" name="'.$keyprefix.$key.$keysuffix.'" id="'.$keyprefix.$key.$keysuffix.'" value="'.$value.'" '.($moreparam ? $moreparam : '').'> '.$langs->getCurrencySymbol($conf->currency);
  6472. } elseif (preg_match('/^double(\([0-9],[0-9]\)){0,1}/', $type)) {
  6473. if (!empty($value)) { // $value in memory is a php numeric, we format it into user number format.
  6474. $value = price($value);
  6475. }
  6476. $out = '<input type="text" class="flat '.$morecss.' maxwidthonsmartphone" name="'.$keyprefix.$key.$keysuffix.'" id="'.$keyprefix.$key.$keysuffix.'" value="'.$value.'" '.($moreparam ? $moreparam : '').'> ';
  6477. } elseif ($type == 'select') { // combo list
  6478. $out = '';
  6479. if (!empty($conf->use_javascript_ajax) && !getDolGlobalString('MAIN_EXTRAFIELDS_DISABLE_SELECT2')) {
  6480. include_once DOL_DOCUMENT_ROOT.'/core/lib/ajax.lib.php';
  6481. $out .= ajax_combobox($keyprefix.$key.$keysuffix, array(), 0);
  6482. }
  6483. $out .= '<select class="flat '.$morecss.' maxwidthonsmartphone" name="'.$keyprefix.$key.$keysuffix.'" id="'.$keyprefix.$key.$keysuffix.'" '.($moreparam ? $moreparam : '').'>';
  6484. if ((!isset($this->fields[$key]['default'])) || ($this->fields[$key]['notnull'] != 1)) {
  6485. $out .= '<option value="0">&nbsp;</option>';
  6486. }
  6487. foreach ($param['options'] as $keyb => $valb) {
  6488. if ((string) $keyb == '') {
  6489. continue;
  6490. }
  6491. if (strpos($valb, "|") !== false) {
  6492. list($valb, $parent) = explode('|', $valb);
  6493. }
  6494. $out .= '<option value="'.$keyb.'"';
  6495. $out .= (((string) $value == (string) $keyb) ? ' selected' : '');
  6496. $out .= (!empty($parent) ? ' parent="'.$parent.'"' : '');
  6497. $out .= '>'.$valb.'</option>';
  6498. }
  6499. $out .= '</select>';
  6500. } elseif ($type == 'sellist') {
  6501. $out = '';
  6502. if (!empty($conf->use_javascript_ajax) && !getDolGlobalString('MAIN_EXTRAFIELDS_DISABLE_SELECT2')) {
  6503. include_once DOL_DOCUMENT_ROOT.'/core/lib/ajax.lib.php';
  6504. $out .= ajax_combobox($keyprefix.$key.$keysuffix, array(), 0);
  6505. }
  6506. $out .= '<select class="flat '.$morecss.' maxwidthonsmartphone" name="'.$keyprefix.$key.$keysuffix.'" id="'.$keyprefix.$key.$keysuffix.'" '.($moreparam ? $moreparam : '').'>';
  6507. if (is_array($param['options'])) {
  6508. $param_list = array_keys($param['options']);
  6509. $InfoFieldList = explode(":", $param_list[0], 5);
  6510. if (! empty($InfoFieldList[4])) {
  6511. $pos = 0;
  6512. $parenthesisopen = 0;
  6513. while (substr($InfoFieldList[4], $pos, 1) !== '' && ($parenthesisopen || $pos == 0 || substr($InfoFieldList[4], $pos, 1) != ':')) {
  6514. if (substr($InfoFieldList[4], $pos, 1) == '(') {
  6515. $parenthesisopen++;
  6516. }
  6517. if (substr($InfoFieldList[4], $pos, 1) == ')') {
  6518. $parenthesisopen--;
  6519. }
  6520. $pos++;
  6521. }
  6522. $tmpbefore = substr($InfoFieldList[4], 0, $pos);
  6523. $tmpafter = substr($InfoFieldList[4], $pos+1);
  6524. //var_dump($InfoFieldList[4].' -> '.$pos); var_dump($tmpafter);
  6525. $InfoFieldList[4] = $tmpbefore;
  6526. if ($tmpafter !== '') {
  6527. $InfoFieldList = array_merge($InfoFieldList, explode(':', $tmpafter));
  6528. }
  6529. //var_dump($InfoFieldList);
  6530. }
  6531. $parentName = '';
  6532. $parentField = '';
  6533. // 0 : tableName
  6534. // 1 : label field name
  6535. // 2 : key fields name (if differ of rowid)
  6536. // 3 : key field parent (for dependent lists)
  6537. // 4 : where clause filter on column or table extrafield, syntax field='value' or extra.field=value
  6538. // 5 : id category type
  6539. // 6 : ids categories list separated by comma for category root
  6540. // 7 : sort field
  6541. $keyList = (empty($InfoFieldList[2]) ? 'rowid' : $InfoFieldList[2].' as rowid');
  6542. if (count($InfoFieldList) > 4 && !empty($InfoFieldList[4])) {
  6543. if (strpos($InfoFieldList[4], 'extra.') !== false) {
  6544. $keyList = 'main.'.$InfoFieldList[2].' as rowid';
  6545. } else {
  6546. $keyList = $InfoFieldList[2].' as rowid';
  6547. }
  6548. }
  6549. if (count($InfoFieldList) > 3 && !empty($InfoFieldList[3])) {
  6550. list($parentName, $parentField) = explode('|', $InfoFieldList[3]);
  6551. $keyList .= ', '.$parentField;
  6552. }
  6553. $filter_categorie = false;
  6554. if (count($InfoFieldList) > 5) {
  6555. if ($InfoFieldList[0] == 'categorie') {
  6556. $filter_categorie = true;
  6557. }
  6558. }
  6559. if ($filter_categorie === false) {
  6560. $fields_label = explode('|', $InfoFieldList[1]);
  6561. if (is_array($fields_label)) {
  6562. $keyList .= ', ';
  6563. $keyList .= implode(', ', $fields_label);
  6564. }
  6565. $sqlwhere = '';
  6566. $sql = "SELECT " . $keyList;
  6567. $sql .= " FROM " . $this->db->prefix() . $InfoFieldList[0];
  6568. if (!empty($InfoFieldList[4])) {
  6569. // can use SELECT request
  6570. if (strpos($InfoFieldList[4], '$SEL$') !== false) {
  6571. $InfoFieldList[4] = str_replace('$SEL$', 'SELECT', $InfoFieldList[4]);
  6572. }
  6573. // current object id can be use into filter
  6574. if (strpos($InfoFieldList[4], '$ID$') !== false && !empty($objectid)) {
  6575. $InfoFieldList[4] = str_replace('$ID$', $objectid, $InfoFieldList[4]);
  6576. } else {
  6577. $InfoFieldList[4] = str_replace('$ID$', '0', $InfoFieldList[4]);
  6578. }
  6579. // We have to join on extrafield table
  6580. $errstr = '';
  6581. if (strpos($InfoFieldList[4], 'extra') !== false) {
  6582. $sql .= " as main, " . $this->db->prefix() . $InfoFieldList[0] . "_extrafields as extra";
  6583. $sqlwhere .= " WHERE extra.fk_object=main." . $InfoFieldList[2];
  6584. $sqlwhere .= " AND " . forgeSQLFromUniversalSearchCriteria($InfoFieldList[4], $errstr, 1);
  6585. } else {
  6586. $sqlwhere .= " WHERE " . forgeSQLFromUniversalSearchCriteria($InfoFieldList[4], $errstr, 1);
  6587. }
  6588. } else {
  6589. $sqlwhere .= ' WHERE 1=1';
  6590. }
  6591. // Some tables may have field, some other not. For the moment we disable it.
  6592. if (in_array($InfoFieldList[0], array('tablewithentity'))) {
  6593. $sqlwhere .= " AND entity = " . ((int) $conf->entity);
  6594. }
  6595. $sql .= $sqlwhere;
  6596. //print $sql;
  6597. // Note: $InfoFieldList can be 'sellist:TableName:LabelFieldName[:KeyFieldName[:KeyFieldParent[:Filter[:CategoryIdType[:CategoryIdList[:Sortfield]]]]]]'
  6598. if (isset($InfoFieldList[7]) && preg_match('/^[a-z0-9_\-,]+$/i', $InfoFieldList[7])) {
  6599. $sql .= " ORDER BY ".$this->db->escape($InfoFieldList[7]);
  6600. } else {
  6601. $sql .= " ORDER BY ".$this->db->sanitize(implode(', ', $fields_label));
  6602. }
  6603. dol_syslog(get_class($this) . '::showInputField type=sellist', LOG_DEBUG);
  6604. $resql = $this->db->query($sql);
  6605. if ($resql) {
  6606. $out .= '<option value="0">&nbsp;</option>';
  6607. $num = $this->db->num_rows($resql);
  6608. $i = 0;
  6609. while ($i < $num) {
  6610. $labeltoshow = '';
  6611. $obj = $this->db->fetch_object($resql);
  6612. // Several field into label (eq table:code|libelle:rowid)
  6613. $notrans = false;
  6614. $fields_label = explode('|', $InfoFieldList[1]);
  6615. if (count($fields_label) > 1) {
  6616. $notrans = true;
  6617. foreach ($fields_label as $field_toshow) {
  6618. $labeltoshow .= $obj->$field_toshow . ' ';
  6619. }
  6620. } else {
  6621. $labeltoshow = $obj->{$InfoFieldList[1]};
  6622. }
  6623. $labeltoshow = dol_trunc($labeltoshow, 45);
  6624. if ($value == $obj->rowid) {
  6625. foreach ($fields_label as $field_toshow) {
  6626. $translabel = $langs->trans($obj->$field_toshow);
  6627. if ($translabel != $obj->$field_toshow) {
  6628. $labeltoshow = dol_trunc($translabel) . ' ';
  6629. } else {
  6630. $labeltoshow = dol_trunc($obj->$field_toshow) . ' ';
  6631. }
  6632. }
  6633. $out .= '<option value="' . $obj->rowid . '" selected>' . $labeltoshow . '</option>';
  6634. } else {
  6635. if (!$notrans) {
  6636. $translabel = $langs->trans($obj->{$InfoFieldList[1]});
  6637. if ($translabel != $obj->{$InfoFieldList[1]}) {
  6638. $labeltoshow = dol_trunc($translabel, 18);
  6639. } else {
  6640. $labeltoshow = dol_trunc($obj->{$InfoFieldList[1]});
  6641. }
  6642. }
  6643. if (empty($labeltoshow)) {
  6644. $labeltoshow = '(not defined)';
  6645. }
  6646. if ($value == $obj->rowid) {
  6647. $out .= '<option value="' . $obj->rowid . '" selected>' . $labeltoshow . '</option>';
  6648. }
  6649. if (!empty($InfoFieldList[3]) && $parentField) {
  6650. $parent = $parentName . ':' . $obj->{$parentField};
  6651. $isDependList = 1;
  6652. }
  6653. $out .= '<option value="' . $obj->rowid . '"';
  6654. $out .= ($value == $obj->rowid ? ' selected' : '');
  6655. $out .= (!empty($parent) ? ' parent="' . $parent . '"' : '');
  6656. $out .= '>' . $labeltoshow . '</option>';
  6657. }
  6658. $i++;
  6659. }
  6660. $this->db->free($resql);
  6661. } else {
  6662. print 'Error in request ' . $sql . ' ' . $this->db->lasterror() . '. Check setup of extra parameters.<br>';
  6663. }
  6664. } else {
  6665. require_once DOL_DOCUMENT_ROOT.'/categories/class/categorie.class.php';
  6666. $data = $form->select_all_categories(Categorie::$MAP_ID_TO_CODE[$InfoFieldList[5]], '', 'parent', 64, $InfoFieldList[6], 1, 1);
  6667. $out .= '<option value="0">&nbsp;</option>';
  6668. foreach ($data as $data_key => $data_value) {
  6669. $out .= '<option value="' . $data_key . '"';
  6670. $out .= ($value == $data_key ? ' selected' : '');
  6671. $out .= '>' . $data_value . '</option>';
  6672. }
  6673. }
  6674. }
  6675. $out .= '</select>';
  6676. } elseif ($type == 'checkbox') {
  6677. $value_arr = explode(',', $value);
  6678. $out = $form->multiselectarray($keyprefix.$key.$keysuffix, (empty($param['options']) ? null : $param['options']), $value_arr, '', 0, $morecss, 0, '100%');
  6679. } elseif ($type == 'radio') {
  6680. $out = '';
  6681. foreach ($param['options'] as $keyopt => $valopt) {
  6682. $out .= '<input class="flat '.$morecss.'" type="radio" name="'.$keyprefix.$key.$keysuffix.'" id="'.$keyprefix.$key.$keysuffix.'" '.($moreparam ? $moreparam : '');
  6683. $out .= ' value="'.$keyopt.'"';
  6684. $out .= ' id="'.$keyprefix.$key.$keysuffix.'_'.$keyopt.'"';
  6685. $out .= ($value == $keyopt ? 'checked' : '');
  6686. $out .= '/><label for="'.$keyprefix.$key.$keysuffix.'_'.$keyopt.'">'.$valopt.'</label><br>';
  6687. }
  6688. } elseif ($type == 'chkbxlst') {
  6689. if (is_array($value)) {
  6690. $value_arr = $value;
  6691. } else {
  6692. $value_arr = explode(',', $value);
  6693. }
  6694. if (is_array($param['options'])) {
  6695. $param_list = array_keys($param['options']);
  6696. $InfoFieldList = explode(":", $param_list[0]);
  6697. $parentName = '';
  6698. $parentField = '';
  6699. // 0 : tableName
  6700. // 1 : label field name
  6701. // 2 : key fields name (if differ of rowid)
  6702. // 3 : key field parent (for dependent lists)
  6703. // 4 : where clause filter on column or table extrafield, syntax field='value' or extra.field=value
  6704. // 5 : id category type
  6705. // 6 : ids categories list separated by comma for category root
  6706. $keyList = (empty($InfoFieldList[2]) ? 'rowid' : $InfoFieldList[2].' as rowid');
  6707. if (count($InfoFieldList) > 3 && !empty($InfoFieldList[3])) {
  6708. list($parentName, $parentField) = explode('|', $InfoFieldList[3]);
  6709. $keyList .= ', '.$parentField;
  6710. }
  6711. if (count($InfoFieldList) > 4 && !empty($InfoFieldList[4])) {
  6712. if (strpos($InfoFieldList[4], 'extra.') !== false) {
  6713. $keyList = 'main.'.$InfoFieldList[2].' as rowid';
  6714. } else {
  6715. $keyList = $InfoFieldList[2].' as rowid';
  6716. }
  6717. }
  6718. $filter_categorie = false;
  6719. if (count($InfoFieldList) > 5) {
  6720. if ($InfoFieldList[0] == 'categorie') {
  6721. $filter_categorie = true;
  6722. }
  6723. }
  6724. if ($filter_categorie === false) {
  6725. $fields_label = explode('|', $InfoFieldList[1]);
  6726. if (is_array($fields_label)) {
  6727. $keyList .= ', ';
  6728. $keyList .= implode(', ', $fields_label);
  6729. }
  6730. $sqlwhere = '';
  6731. $sql = "SELECT " . $keyList;
  6732. $sql .= ' FROM ' . $this->db->prefix() . $InfoFieldList[0];
  6733. if (!empty($InfoFieldList[4])) {
  6734. // can use SELECT request
  6735. if (strpos($InfoFieldList[4], '$SEL$') !== false) {
  6736. $InfoFieldList[4] = str_replace('$SEL$', 'SELECT', $InfoFieldList[4]);
  6737. }
  6738. // current object id can be use into filter
  6739. if (strpos($InfoFieldList[4], '$ID$') !== false && !empty($objectid)) {
  6740. $InfoFieldList[4] = str_replace('$ID$', $objectid, $InfoFieldList[4]);
  6741. } else {
  6742. $InfoFieldList[4] = str_replace('$ID$', '0', $InfoFieldList[4]);
  6743. }
  6744. // We have to join on extrafield table
  6745. if (strpos($InfoFieldList[4], 'extra') !== false) {
  6746. $sql .= ' as main, ' . $this->db->prefix() . $InfoFieldList[0] . '_extrafields as extra';
  6747. $sqlwhere .= " WHERE extra.fk_object=main." . $InfoFieldList[2] . " AND " . $InfoFieldList[4];
  6748. } else {
  6749. $sqlwhere .= " WHERE " . $InfoFieldList[4];
  6750. }
  6751. } else {
  6752. $sqlwhere .= ' WHERE 1=1';
  6753. }
  6754. // Some tables may have field, some other not. For the moment we disable it.
  6755. if (in_array($InfoFieldList[0], array('tablewithentity'))) {
  6756. $sqlwhere .= " AND entity = " . ((int) $conf->entity);
  6757. }
  6758. // $sql.=preg_replace('/^ AND /','',$sqlwhere);
  6759. // print $sql;
  6760. $sql .= $sqlwhere;
  6761. dol_syslog(get_class($this) . '::showInputField type=chkbxlst', LOG_DEBUG);
  6762. $resql = $this->db->query($sql);
  6763. if ($resql) {
  6764. $num = $this->db->num_rows($resql);
  6765. $i = 0;
  6766. $data = array();
  6767. while ($i < $num) {
  6768. $labeltoshow = '';
  6769. $obj = $this->db->fetch_object($resql);
  6770. $notrans = false;
  6771. // Several field into label (eq table:code|libelle:rowid)
  6772. $fields_label = explode('|', $InfoFieldList[1]);
  6773. if (count($fields_label) > 1) {
  6774. $notrans = true;
  6775. foreach ($fields_label as $field_toshow) {
  6776. $labeltoshow .= $obj->$field_toshow . ' ';
  6777. }
  6778. } else {
  6779. $labeltoshow = $obj->{$InfoFieldList[1]};
  6780. }
  6781. $labeltoshow = dol_trunc($labeltoshow, 45);
  6782. if (is_array($value_arr) && in_array($obj->rowid, $value_arr)) {
  6783. foreach ($fields_label as $field_toshow) {
  6784. $translabel = $langs->trans($obj->$field_toshow);
  6785. if ($translabel != $obj->$field_toshow) {
  6786. $labeltoshow = dol_trunc($translabel, 18) . ' ';
  6787. } else {
  6788. $labeltoshow = dol_trunc($obj->$field_toshow, 18) . ' ';
  6789. }
  6790. }
  6791. $data[$obj->rowid] = $labeltoshow;
  6792. } else {
  6793. if (!$notrans) {
  6794. $translabel = $langs->trans($obj->{$InfoFieldList[1]});
  6795. if ($translabel != $obj->{$InfoFieldList[1]}) {
  6796. $labeltoshow = dol_trunc($translabel, 18);
  6797. } else {
  6798. $labeltoshow = dol_trunc($obj->{$InfoFieldList[1]}, 18);
  6799. }
  6800. }
  6801. if (empty($labeltoshow)) {
  6802. $labeltoshow = '(not defined)';
  6803. }
  6804. if (is_array($value_arr) && in_array($obj->rowid, $value_arr)) {
  6805. $data[$obj->rowid] = $labeltoshow;
  6806. }
  6807. if (!empty($InfoFieldList[3]) && $parentField) {
  6808. $parent = $parentName . ':' . $obj->{$parentField};
  6809. $isDependList = 1;
  6810. }
  6811. $data[$obj->rowid] = $labeltoshow;
  6812. }
  6813. $i++;
  6814. }
  6815. $this->db->free($resql);
  6816. $out = $form->multiselectarray($keyprefix . $key . $keysuffix, $data, $value_arr, '', 0, $morecss, 0, '100%');
  6817. } else {
  6818. print 'Error in request ' . $sql . ' ' . $this->db->lasterror() . '. Check setup of extra parameters.<br>';
  6819. }
  6820. } else {
  6821. require_once DOL_DOCUMENT_ROOT.'/categories/class/categorie.class.php';
  6822. $data = $form->select_all_categories(Categorie::$MAP_ID_TO_CODE[$InfoFieldList[5]], '', 'parent', 64, $InfoFieldList[6], 1, 1);
  6823. $out = $form->multiselectarray($keyprefix . $key . $keysuffix, $data, $value_arr, '', 0, $morecss, 0, '100%');
  6824. }
  6825. }
  6826. } elseif ($type == 'link') {
  6827. $param_list = array_keys($param['options']); // $param_list='ObjectName:classPath[:AddCreateButtonOrNot[:Filter[:Sortfield]]]'
  6828. $param_list_array = explode(':', $param_list[0]);
  6829. $showempty = (($required && $default != '') ? 0 : 1);
  6830. if (!preg_match('/search_/', $keyprefix)) {
  6831. if (!empty($param_list_array[2])) { // If the entry into $fields is set to add a create button
  6832. if (!empty($this->fields[$key]['picto'])) {
  6833. $morecss .= ' widthcentpercentminusxx';
  6834. } else {
  6835. $morecss .= ' widthcentpercentminusx';
  6836. }
  6837. } else {
  6838. if (!empty($this->fields[$key]['picto'])) {
  6839. $morecss .= ' widthcentpercentminusx';
  6840. }
  6841. }
  6842. }
  6843. //$out = $form->selectForForms($param_list[0], $keyprefix.$key.$keysuffix, $value, $showempty, '', '', $morecss, $moreparam, 0, (empty($val['disabled']) ? 0 : 1), '');
  6844. $out = $form->selectForForms($param_list_array[0], $keyprefix.$key.$keysuffix, $value, $showempty, '', '', $morecss, $moreparam, 0, (empty($val['disabled']) ? 0 : 1), '', $this->element.':'.$key.$keysuffix);
  6845. if (!empty($param_list_array[2])) { // If the entry into $fields is set, we must add a create button
  6846. if ((!GETPOSTISSET('backtopage') || strpos(GETPOST('backtopage'), $_SERVER['PHP_SELF']) === 0) // // To avoid to open several times the 'Plus' button (we accept only one level)
  6847. && empty($val['disabled']) && empty($nonewbutton)) { // and to avoid to show the button if the field is protected by a "disabled".
  6848. list($class, $classfile) = explode(':', $param_list[0]);
  6849. if (file_exists(dol_buildpath(dirname(dirname($classfile)).'/card.php'))) {
  6850. $url_path = dol_buildpath(dirname(dirname($classfile)).'/card.php', 1);
  6851. } else {
  6852. $url_path = dol_buildpath(dirname(dirname($classfile)).'/'.strtolower($class).'_card.php', 1);
  6853. }
  6854. $paramforthenewlink = '';
  6855. $paramforthenewlink .= (GETPOSTISSET('action') ? '&action='.GETPOST('action', 'aZ09') : '');
  6856. $paramforthenewlink .= (GETPOSTISSET('id') ? '&id='.GETPOST('id', 'int') : '');
  6857. $paramforthenewlink .= (GETPOSTISSET('origin') ? '&origin='.GETPOST('origin', 'aZ09') : '');
  6858. $paramforthenewlink .= (GETPOSTISSET('originid') ? '&originid='.GETPOST('originid', 'int') : '');
  6859. $paramforthenewlink .= '&fk_'.strtolower($class).'=--IDFORBACKTOPAGE--';
  6860. // TODO Add Javascript code to add input fields already filled into $paramforthenewlink so we won't loose them when going back to main page
  6861. $out .= '<a class="butActionNew" title="'.$langs->trans("New").'" href="'.$url_path.'?action=create&backtopage='.urlencode($_SERVER['PHP_SELF'].($paramforthenewlink ? '?'.$paramforthenewlink : '')).'"><span class="fa fa-plus-circle valignmiddle"></span></a>';
  6862. }
  6863. }
  6864. } elseif ($type == 'password') {
  6865. // If prefix is 'search_', field is used as a filter, we use a common text field.
  6866. if ($keyprefix.$key.$keysuffix == 'pass_crypted') {
  6867. $out = '<input type="'.($keyprefix == 'search_' ? 'text' : 'password').'" class="flat '.$morecss.'" name="pass" id="pass" value="" '.($moreparam ? $moreparam : '').'>';
  6868. $out .= '<input type="hidden" name="pass_crypted" id="pass_crypted" value="'.$value.'" '.($moreparam ? $moreparam : '').'>';
  6869. } else {
  6870. $out = '<input type="'.($keyprefix == 'search_' ? 'text' : 'password').'" class="flat '.$morecss.'" name="'.$keyprefix.$key.$keysuffix.'" id="'.$keyprefix.$key.$keysuffix.'" value="'.$value.'" '.($moreparam ? $moreparam : '').'>';
  6871. }
  6872. } elseif ($type == 'array') {
  6873. $newval = $val;
  6874. $newval['type'] = 'varchar(256)';
  6875. $out = '';
  6876. if (!empty($value)) {
  6877. foreach ($value as $option) {
  6878. $out .= '<span><a class="'.dol_escape_htmltag($keyprefix.$key.$keysuffix).'_del" href="javascript:;"><span class="fa fa-minus-circle valignmiddle"></span></a> ';
  6879. $out .= $this->showInputField($newval, $keyprefix.$key.$keysuffix.'[]', $option, $moreparam, '', '', $morecss).'<br></span>';
  6880. }
  6881. }
  6882. $out .= '<a id="'.dol_escape_htmltag($keyprefix.$key.$keysuffix).'_add" href="javascript:;"><span class="fa fa-plus-circle valignmiddle"></span></a>';
  6883. $newInput = '<span><a class="'.dol_escape_htmltag($keyprefix.$key.$keysuffix).'_del" href="javascript:;"><span class="fa fa-minus-circle valignmiddle"></span></a> ';
  6884. $newInput .= $this->showInputField($newval, $keyprefix.$key.$keysuffix.'[]', '', $moreparam, '', '', $morecss).'<br></span>';
  6885. if (!empty($conf->use_javascript_ajax)) {
  6886. $out .= '
  6887. <script nonce="'.getNonce().'">
  6888. $(document).ready(function() {
  6889. $("a#'.dol_escape_js($keyprefix.$key.$keysuffix).'_add").click(function() {
  6890. $("'.dol_escape_js($newInput).'").insertBefore(this);
  6891. });
  6892. $(document).on("click", "a.'.dol_escape_js($keyprefix.$key.$keysuffix).'_del", function() {
  6893. $(this).parent().remove();
  6894. });
  6895. });
  6896. </script>';
  6897. }
  6898. }
  6899. if (!empty($hidden)) {
  6900. $out = '<input type="hidden" value="'.$value.'" name="'.$keyprefix.$key.$keysuffix.'" id="'.$keyprefix.$key.$keysuffix.'"/>';
  6901. }
  6902. if ($isDependList==1) {
  6903. $out .= $this->getJSListDependancies('_common');
  6904. }
  6905. /* Add comments
  6906. if ($type == 'date') $out.=' (YYYY-MM-DD)';
  6907. elseif ($type == 'datetime') $out.=' (YYYY-MM-DD HH:MM:SS)';
  6908. */
  6909. // Display error message for field
  6910. if (!empty($fieldValidationErrorMsg) && function_exists('getFieldErrorIcon')) {
  6911. $out .= ' '.getFieldErrorIcon($fieldValidationErrorMsg);
  6912. }
  6913. return $out;
  6914. }
  6915. /**
  6916. * Return HTML string to show a field into a page
  6917. * Code very similar with showOutputField of extra fields
  6918. *
  6919. * @param array $val Array of properties of field to show
  6920. * @param string $key Key of attribute
  6921. * @param string $value Preselected value to show (for date type it must be in timestamp format, for amount or price it must be a php numeric value)
  6922. * @param string $moreparam To add more parameters on html tag
  6923. * @param string $keysuffix Prefix string to add into name and id of field (can be used to avoid duplicate names)
  6924. * @param string $keyprefix Suffix string to add into name and id of field (can be used to avoid duplicate names)
  6925. * @param mixed $morecss Value for CSS to use (Old usage: May also be a numeric to define a size).
  6926. * @return string
  6927. */
  6928. public function showOutputField($val, $key, $value, $moreparam = '', $keysuffix = '', $keyprefix = '', $morecss = '')
  6929. {
  6930. global $conf, $langs, $form;
  6931. if (!is_object($form)) {
  6932. require_once DOL_DOCUMENT_ROOT.'/core/class/html.form.class.php';
  6933. $form = new Form($this->db);
  6934. }
  6935. $label = empty($val['label']) ? '' : $val['label'];
  6936. $type = empty($val['type']) ? '' : $val['type'];
  6937. $size = empty($val['css']) ? '' : $val['css'];
  6938. $reg = array();
  6939. // Convert var to be able to share same code than showOutputField of extrafields
  6940. if (preg_match('/varchar\((\d+)\)/', $type, $reg)) {
  6941. $type = 'varchar'; // convert varchar(xx) int varchar
  6942. $size = $reg[1];
  6943. } elseif (preg_match('/varchar/', $type)) {
  6944. $type = 'varchar'; // convert varchar(xx) int varchar
  6945. }
  6946. if (!empty($val['arrayofkeyval']) && is_array($val['arrayofkeyval'])) {
  6947. $type = (($this->fields[$key]['type']=='checkbox') ? $this->fields[$key]['type'] : 'select');
  6948. }
  6949. if (preg_match('/^integer:(.*):(.*)/i', $val['type'], $reg)) {
  6950. $type = 'link';
  6951. }
  6952. $default = empty($val['default']) ? '' : $val['default'];
  6953. $computed = empty($val['computed']) ? '' : $val['computed'];
  6954. $unique = empty($val['unique']) ? '' : $val['unique'];
  6955. $required = empty($val['required']) ? '' : $val['required'];
  6956. $param = array();
  6957. $param['options'] = array();
  6958. if (!empty($val['arrayofkeyval']) && is_array($val['arrayofkeyval'])) {
  6959. $param['options'] = $val['arrayofkeyval'];
  6960. }
  6961. if (preg_match('/^integer:([^:]*):([^:]*)/i', $val['type'], $reg)) { // ex: integer:User:user/class/user.class.php
  6962. $type = 'link';
  6963. $stringforoptions = $reg[1].':'.$reg[2];
  6964. // Special case: Force addition of getnomurlparam1 to -1 for users
  6965. if ($reg[1] == 'User') {
  6966. $stringforoptions .= ':#getnomurlparam1=-1';
  6967. }
  6968. $param['options'] = array($stringforoptions => $stringforoptions);
  6969. } elseif (preg_match('/^sellist:(.*):(.*):(.*):(.*)/i', $val['type'], $reg)) {
  6970. $param['options'] = array($reg[1].':'.$reg[2].':'.$reg[3].':'.$reg[4] => 'N');
  6971. $type = 'sellist';
  6972. } elseif (preg_match('/^sellist:(.*):(.*):(.*)/i', $val['type'], $reg)) {
  6973. $param['options'] = array($reg[1].':'.$reg[2].':'.$reg[3] => 'N');
  6974. $type = 'sellist';
  6975. } elseif (preg_match('/^sellist:(.*):(.*)/i', $val['type'], $reg)) {
  6976. $param['options'] = array($reg[1].':'.$reg[2] => 'N');
  6977. $type = 'sellist';
  6978. } elseif (preg_match('/^chkbxlst:(.*)/i', $val['type'], $reg)) {
  6979. $param['options'] = array($reg[1] => 'N');
  6980. $type = 'chkbxlst';
  6981. }
  6982. $langfile = empty($val['langfile']) ? '' : $val['langfile'];
  6983. $list = (empty($val['list']) ? '' : $val['list']);
  6984. $help = (empty($val['help']) ? '' : $val['help']);
  6985. $hidden = (($val['visible'] == 0) ? 1 : 0); // If zero, we are sure it is hidden, otherwise we show. If it depends on mode (view/create/edit form or list, this must be filtered by caller)
  6986. if ($hidden) {
  6987. return '';
  6988. }
  6989. // If field is a computed field, value must become result of compute
  6990. if ($computed) {
  6991. // Make the eval of compute string
  6992. //var_dump($computed);
  6993. $value = dol_eval($computed, 1, 0, '2');
  6994. }
  6995. if (empty($morecss)) {
  6996. if ($type == 'date') {
  6997. $morecss = 'minwidth100imp';
  6998. } elseif ($type == 'datetime' || $type == 'timestamp') {
  6999. $morecss = 'minwidth200imp';
  7000. } elseif (in_array($type, array('int', 'double', 'price'))) {
  7001. $morecss = 'maxwidth75';
  7002. } elseif ($type == 'url') {
  7003. $morecss = 'minwidth400';
  7004. } elseif ($type == 'boolean') {
  7005. $morecss = '';
  7006. } else {
  7007. if (is_numeric($size) && round($size) < 12) {
  7008. $morecss = 'minwidth100';
  7009. } elseif (is_numeric($size) && round($size) <= 48) {
  7010. $morecss = 'minwidth200';
  7011. } else {
  7012. $morecss = 'minwidth400';
  7013. }
  7014. }
  7015. }
  7016. // Format output value differently according to properties of field
  7017. if (in_array($key, array('rowid', 'ref')) && method_exists($this, 'getNomUrl')) {
  7018. if ($key != 'rowid' || empty($this->fields['ref'])) { // If we want ref field or if we want ID and there is no ref field, we show the link.
  7019. $value = $this->getNomUrl(1, '', 0, '', 1);
  7020. }
  7021. } elseif ($key == 'status' && method_exists($this, 'getLibStatut')) {
  7022. $value = $this->getLibStatut(3);
  7023. } elseif ($type == 'date') {
  7024. if (!empty($value)) {
  7025. $value = dol_print_date($value, 'day'); // We suppose dates without time are always gmt (storage of course + output)
  7026. } else {
  7027. $value = '';
  7028. }
  7029. } elseif ($type == 'datetime' || $type == 'timestamp') {
  7030. if (!empty($value)) {
  7031. $value = dol_print_date($value, 'dayhour', 'tzuserrel');
  7032. } else {
  7033. $value = '';
  7034. }
  7035. } elseif ($type == 'duration') {
  7036. include_once DOL_DOCUMENT_ROOT.'/core/lib/date.lib.php';
  7037. if (!is_null($value) && $value !== '') {
  7038. $value = convertSecondToTime($value, 'allhourmin');
  7039. }
  7040. } elseif ($type == 'double' || $type == 'real') {
  7041. if (!is_null($value) && $value !== '') {
  7042. $value = price($value);
  7043. }
  7044. } elseif ($type == 'boolean') {
  7045. $checked = '';
  7046. if (!empty($value)) {
  7047. $checked = ' checked ';
  7048. }
  7049. $value = '<input type="checkbox" '.$checked.' '.($moreparam ? $moreparam : '').' readonly disabled>';
  7050. } elseif ($type == 'mail' || $type == 'email') {
  7051. $value = dol_print_email($value, 0, 0, 0, 64, 1, 1);
  7052. } elseif ($type == 'url') {
  7053. $value = dol_print_url($value, '_blank', 32, 1);
  7054. } elseif ($type == 'phone') {
  7055. $value = dol_print_phone($value, '', 0, 0, '', '&nbsp;', 'phone');
  7056. } elseif ($type == 'ip') {
  7057. $value = dol_print_ip($value, 0);
  7058. } elseif ($type == 'price') {
  7059. if (!is_null($value) && $value !== '') {
  7060. $value = price($value, 0, $langs, 0, 0, -1, $conf->currency);
  7061. }
  7062. } elseif ($type == 'select') {
  7063. $value = isset($param['options'][$value]) ? $param['options'][$value] : '';
  7064. } elseif ($type == 'sellist') {
  7065. $param_list = array_keys($param['options']);
  7066. $InfoFieldList = explode(":", $param_list[0]);
  7067. $selectkey = "rowid";
  7068. $keyList = 'rowid';
  7069. if (count($InfoFieldList) > 4 && !empty($InfoFieldList[4])) {
  7070. $selectkey = $InfoFieldList[2];
  7071. $keyList = $InfoFieldList[2].' as rowid';
  7072. }
  7073. $fields_label = explode('|', $InfoFieldList[1]);
  7074. if (is_array($fields_label)) {
  7075. $keyList .= ', ';
  7076. $keyList .= implode(', ', $fields_label);
  7077. }
  7078. $filter_categorie = false;
  7079. if (count($InfoFieldList) > 5) {
  7080. if ($InfoFieldList[0] == 'categorie') {
  7081. $filter_categorie = true;
  7082. }
  7083. }
  7084. $sql = "SELECT ".$keyList;
  7085. $sql .= ' FROM '.$this->db->prefix().$InfoFieldList[0];
  7086. if (strpos($InfoFieldList[4], 'extra') !== false) {
  7087. $sql .= ' as main';
  7088. }
  7089. if ($selectkey == 'rowid' && empty($value)) {
  7090. $sql .= " WHERE ".$selectkey." = 0";
  7091. } elseif ($selectkey == 'rowid') {
  7092. $sql .= " WHERE ".$selectkey." = ".((int) $value);
  7093. } else {
  7094. $sql .= " WHERE ".$selectkey." = '".$this->db->escape($value)."'";
  7095. }
  7096. //$sql.= ' AND entity = '.$conf->entity;
  7097. dol_syslog(get_class($this).':showOutputField:$type=sellist', LOG_DEBUG);
  7098. $resql = $this->db->query($sql);
  7099. if ($resql) {
  7100. if ($filter_categorie === false) {
  7101. $value = ''; // value was used, so now we reste it to use it to build final output
  7102. $numrows = $this->db->num_rows($resql);
  7103. if ($numrows) {
  7104. $obj = $this->db->fetch_object($resql);
  7105. // Several field into label (eq table:code|libelle:rowid)
  7106. $fields_label = explode('|', $InfoFieldList[1]);
  7107. if (is_array($fields_label) && count($fields_label) > 1) {
  7108. foreach ($fields_label as $field_toshow) {
  7109. $translabel = '';
  7110. if (!empty($obj->$field_toshow)) {
  7111. $translabel = $langs->trans($obj->$field_toshow);
  7112. }
  7113. if ($translabel != $field_toshow) {
  7114. $value .= dol_trunc($translabel, 18) . ' ';
  7115. } else {
  7116. $value .= $obj->$field_toshow . ' ';
  7117. }
  7118. }
  7119. } else {
  7120. $translabel = '';
  7121. if (!empty($obj->{$InfoFieldList[1]})) {
  7122. $translabel = $langs->trans($obj->{$InfoFieldList[1]});
  7123. }
  7124. if ($translabel != $obj->{$InfoFieldList[1]}) {
  7125. $value = dol_trunc($translabel, 18);
  7126. } else {
  7127. $value = $obj->{$InfoFieldList[1]};
  7128. }
  7129. }
  7130. }
  7131. } else {
  7132. require_once DOL_DOCUMENT_ROOT . '/categories/class/categorie.class.php';
  7133. $toprint = array();
  7134. $obj = $this->db->fetch_object($resql);
  7135. $c = new Categorie($this->db);
  7136. $c->fetch($obj->rowid);
  7137. $ways = $c->print_all_ways(); // $ways[0] = "ccc2 >> ccc2a >> ccc2a1" with html formatted text
  7138. foreach ($ways as $way) {
  7139. $toprint[] = '<li class="select2-search-choice-dolibarr noborderoncategories"' . ($c->color ? ' style="background: #' . $c->color . ';"' : ' style="background: #aaa"') . '>' . img_object('', 'category') . ' ' . $way . '</li>';
  7140. }
  7141. $value = '<div class="select2-container-multi-dolibarr" style="width: 90%;"><ul class="select2-choices-dolibarr">'.implode(' ', $toprint).'</ul></div>';
  7142. }
  7143. } else {
  7144. dol_syslog(get_class($this).'::showOutputField error '.$this->db->lasterror(), LOG_WARNING);
  7145. }
  7146. } elseif ($type == 'radio') {
  7147. $value = $param['options'][$value];
  7148. } elseif ($type == 'checkbox') {
  7149. $value_arr = explode(',', $value);
  7150. $value = '';
  7151. if (is_array($value_arr) && count($value_arr) > 0) {
  7152. $toprint = array();
  7153. foreach ($value_arr as $keyval => $valueval) {
  7154. if (!empty($valueval)) {
  7155. $toprint[] = '<li class="select2-search-choice-dolibarr noborderoncategories" style="background: #bbb">' . $param['options'][$valueval] . '</li>';
  7156. }
  7157. }
  7158. if (!empty($toprint)) {
  7159. $value = '<div class="select2-container-multi-dolibarr" style="width: 90%;"><ul class="select2-choices-dolibarr">' . implode(' ', $toprint) . '</ul></div>';
  7160. }
  7161. }
  7162. } elseif ($type == 'chkbxlst') {
  7163. $value_arr = explode(',', $value);
  7164. $param_list = array_keys($param['options']);
  7165. $InfoFieldList = explode(":", $param_list[0]);
  7166. $selectkey = "rowid";
  7167. $keyList = 'rowid';
  7168. if (count($InfoFieldList) >= 3) {
  7169. $selectkey = $InfoFieldList[2];
  7170. $keyList = $InfoFieldList[2].' as rowid';
  7171. }
  7172. $fields_label = explode('|', $InfoFieldList[1]);
  7173. if (is_array($fields_label)) {
  7174. $keyList .= ', ';
  7175. $keyList .= implode(', ', $fields_label);
  7176. }
  7177. $filter_categorie = false;
  7178. if (count($InfoFieldList) > 5) {
  7179. if ($InfoFieldList[0] == 'categorie') {
  7180. $filter_categorie = true;
  7181. }
  7182. }
  7183. $sql = "SELECT ".$keyList;
  7184. $sql .= ' FROM '.$this->db->prefix().$InfoFieldList[0];
  7185. if (strpos($InfoFieldList[4], 'extra') !== false) {
  7186. $sql .= ' as main';
  7187. }
  7188. // $sql.= " WHERE ".$selectkey."='".$this->db->escape($value)."'";
  7189. // $sql.= ' AND entity = '.$conf->entity;
  7190. dol_syslog(get_class($this).':showOutputField:$type=chkbxlst', LOG_DEBUG);
  7191. $resql = $this->db->query($sql);
  7192. if ($resql) {
  7193. if ($filter_categorie === false) {
  7194. $value = ''; // value was used, so now we reste it to use it to build final output
  7195. $toprint = array();
  7196. while ($obj = $this->db->fetch_object($resql)) {
  7197. // Several field into label (eq table:code|libelle:rowid)
  7198. $fields_label = explode('|', $InfoFieldList[1]);
  7199. if (is_array($value_arr) && in_array($obj->rowid, $value_arr)) {
  7200. if (is_array($fields_label) && count($fields_label) > 1) {
  7201. foreach ($fields_label as $field_toshow) {
  7202. $translabel = '';
  7203. if (!empty($obj->$field_toshow)) {
  7204. $translabel = $langs->trans($obj->$field_toshow);
  7205. }
  7206. if ($translabel != $field_toshow) {
  7207. $toprint[] = '<li class="select2-search-choice-dolibarr noborderoncategories" style="background: #bbb">' . dol_trunc($translabel, 18) . '</li>';
  7208. } else {
  7209. $toprint[] = '<li class="select2-search-choice-dolibarr noborderoncategories" style="background: #bbb">' . $obj->$field_toshow . '</li>';
  7210. }
  7211. }
  7212. } else {
  7213. $translabel = '';
  7214. if (!empty($obj->{$InfoFieldList[1]})) {
  7215. $translabel = $langs->trans($obj->{$InfoFieldList[1]});
  7216. }
  7217. if ($translabel != $obj->{$InfoFieldList[1]}) {
  7218. $toprint[] = '<li class="select2-search-choice-dolibarr noborderoncategories" style="background: #bbb">' . dol_trunc($translabel, 18) . '</li>';
  7219. } else {
  7220. $toprint[] = '<li class="select2-search-choice-dolibarr noborderoncategories" style="background: #bbb">' . $obj->{$InfoFieldList[1]} . '</li>';
  7221. }
  7222. }
  7223. }
  7224. }
  7225. } else {
  7226. require_once DOL_DOCUMENT_ROOT . '/categories/class/categorie.class.php';
  7227. $toprint = array();
  7228. while ($obj = $this->db->fetch_object($resql)) {
  7229. if (is_array($value_arr) && in_array($obj->rowid, $value_arr)) {
  7230. $c = new Categorie($this->db);
  7231. $c->fetch($obj->rowid);
  7232. $ways = $c->print_all_ways(); // $ways[0] = "ccc2 >> ccc2a >> ccc2a1" with html formatted text
  7233. foreach ($ways as $way) {
  7234. $toprint[] = '<li class="select2-search-choice-dolibarr noborderoncategories"' . ($c->color ? ' style="background: #' . $c->color . ';"' : ' style="background: #aaa"') . '>' . img_object('', 'category') . ' ' . $way . '</li>';
  7235. }
  7236. }
  7237. }
  7238. }
  7239. $value = '<div class="select2-container-multi-dolibarr" style="width: 90%;"><ul class="select2-choices-dolibarr">'.implode(' ', $toprint).'</ul></div>';
  7240. } else {
  7241. dol_syslog(get_class($this).'::showOutputField error '.$this->db->lasterror(), LOG_WARNING);
  7242. }
  7243. } elseif ($type == 'link') {
  7244. $out = '';
  7245. // only if something to display (perf)
  7246. if ($value) {
  7247. $param_list = array_keys($param['options']);
  7248. // Example: $param_list='ObjectClass:PathToClass[:AddCreateButtonOrNot[:Filter[:Sortfield]]]'
  7249. // Example: $param_list='ObjectClass:PathToClass:#getnomurlparam1=-1#getnomurlparam2=customer'
  7250. $InfoFieldList = explode(":", $param_list[0]);
  7251. $classname = $InfoFieldList[0];
  7252. $classpath = $InfoFieldList[1];
  7253. // Set $getnomurlparam1 et getnomurlparam2
  7254. $getnomurlparam = 3;
  7255. $getnomurlparam2 = '';
  7256. $regtmp = array();
  7257. if (preg_match('/#getnomurlparam1=([^#]*)/', $param_list[0], $regtmp)) {
  7258. $getnomurlparam = $regtmp[1];
  7259. }
  7260. if (preg_match('/#getnomurlparam2=([^#]*)/', $param_list[0], $regtmp)) {
  7261. $getnomurlparam2 = $regtmp[1];
  7262. }
  7263. if (!empty($classpath)) {
  7264. dol_include_once($InfoFieldList[1]);
  7265. if ($classname && class_exists($classname)) {
  7266. $object = new $classname($this->db);
  7267. if ($object->element === 'product') { // Special case for product because default valut of fetch are wrong
  7268. $result = $object->fetch($value, '', '', '', 0, 1, 1);
  7269. } else {
  7270. $result = $object->fetch($value);
  7271. }
  7272. if ($result > 0) {
  7273. if ($object->element === 'product') {
  7274. $get_name_url_param_arr = array($getnomurlparam, $getnomurlparam2, 0, -1, 0, '', 0);
  7275. if (isset($val['get_name_url_params'])) {
  7276. $get_name_url_params = explode(':', $val['get_name_url_params']);
  7277. if (!empty($get_name_url_params)) {
  7278. $param_num_max = count($get_name_url_param_arr) - 1;
  7279. foreach ($get_name_url_params as $param_num => $param_value) {
  7280. if ($param_num > $param_num_max) {
  7281. break;
  7282. }
  7283. $get_name_url_param_arr[$param_num] = $param_value;
  7284. }
  7285. }
  7286. }
  7287. /**
  7288. * @var Product $object
  7289. */
  7290. $value = $object->getNomUrl($get_name_url_param_arr[0], $get_name_url_param_arr[1], $get_name_url_param_arr[2], $get_name_url_param_arr[3], $get_name_url_param_arr[4], $get_name_url_param_arr[5], $get_name_url_param_arr[6]);
  7291. } else {
  7292. $value = $object->getNomUrl($getnomurlparam, $getnomurlparam2);
  7293. }
  7294. } else {
  7295. $value = '';
  7296. }
  7297. }
  7298. } else {
  7299. dol_syslog('Error bad setup of extrafield', LOG_WARNING);
  7300. return 'Error bad setup of extrafield';
  7301. }
  7302. } else {
  7303. $value = '';
  7304. }
  7305. } elseif ($type == 'password') {
  7306. $value = preg_replace('/./i', '*', $value);
  7307. } elseif ($type == 'array') {
  7308. $value = implode('<br>', $value);
  7309. } else { // text|html|varchar
  7310. $value = dol_htmlentitiesbr($value);
  7311. }
  7312. //print $type.'-'.$size.'-'.$value;
  7313. $out = $value;
  7314. return $out;
  7315. }
  7316. /**
  7317. * clear validation message result for a field
  7318. *
  7319. * @param string $fieldKey Key of attribute to clear
  7320. * @return void
  7321. */
  7322. public function clearFieldError($fieldKey)
  7323. {
  7324. $this->error = '';
  7325. unset($this->validateFieldsErrors[$fieldKey]);
  7326. }
  7327. /**
  7328. * set validation error message a field
  7329. *
  7330. * @param string $fieldKey Key of attribute
  7331. * @param string $msg the field error message
  7332. * @return void
  7333. */
  7334. public function setFieldError($fieldKey, $msg = '')
  7335. {
  7336. global $langs;
  7337. if (empty($msg)) {
  7338. $msg = $langs->trans("UnknowError");
  7339. }
  7340. $this->error = $this->validateFieldsErrors[$fieldKey] = $msg;
  7341. }
  7342. /**
  7343. * get field error message
  7344. *
  7345. * @param string $fieldKey Key of attribute
  7346. * @return string Error message of validation ('' if no error)
  7347. */
  7348. public function getFieldError($fieldKey)
  7349. {
  7350. if (!empty($this->validateFieldsErrors[$fieldKey])) {
  7351. return $this->validateFieldsErrors[$fieldKey];
  7352. }
  7353. return '';
  7354. }
  7355. /**
  7356. * Return validation test result for a field
  7357. *
  7358. * @param array $fields Array of properties of field to show
  7359. * @param string $fieldKey Key of attribute
  7360. * @param string $fieldValue value of attribute
  7361. * @return bool return false if fail true on success, see $this->error for error message
  7362. */
  7363. public function validateField($fields, $fieldKey, $fieldValue)
  7364. {
  7365. global $langs;
  7366. if (!class_exists('Validate')) {
  7367. require_once DOL_DOCUMENT_ROOT . '/core/class/validate.class.php';
  7368. }
  7369. $this->clearFieldError($fieldKey);
  7370. if (!isset($fields[$fieldKey])) {
  7371. $this->setFieldError($fieldKey, $langs->trans('FieldNotFoundInObject'));
  7372. return false;
  7373. }
  7374. $val = $fields[$fieldKey];
  7375. $param = array();
  7376. $param['options'] = array();
  7377. $type = $val['type'];
  7378. $required = false;
  7379. if (isset($val['notnull']) && $val['notnull'] === 1) {
  7380. // 'notnull' is set to 1 if not null in database. Set to -1 if we must set data to null if empty ('' or 0).
  7381. $required = true;
  7382. }
  7383. $maxSize = 0;
  7384. $minSize = 0;
  7385. //
  7386. // PREPARE Elements
  7387. //
  7388. $reg = array();
  7389. // Convert var to be able to share same code than showOutputField of extrafields
  7390. if (preg_match('/varchar\((\d+)\)/', $type, $reg)) {
  7391. $type = 'varchar'; // convert varchar(xx) int varchar
  7392. $maxSize = $reg[1];
  7393. } elseif (preg_match('/varchar/', $type)) {
  7394. $type = 'varchar'; // convert varchar(xx) int varchar
  7395. }
  7396. if (!empty($val['arrayofkeyval']) && is_array($val['arrayofkeyval'])) {
  7397. $type = 'select';
  7398. }
  7399. if (!empty($val['type']) && preg_match('/^integer:(.*):(.*)/i', $val['type'], $reg)) {
  7400. $type = 'link';
  7401. }
  7402. if (!empty($val['arrayofkeyval']) && is_array($val['arrayofkeyval'])) {
  7403. $param['options'] = $val['arrayofkeyval'];
  7404. }
  7405. if (preg_match('/^integer:(.*):(.*)/i', $val['type'], $reg)) {
  7406. $type = 'link';
  7407. $param['options'] = array($reg[1].':'.$reg[2]=>$reg[1].':'.$reg[2]);
  7408. } elseif (preg_match('/^sellist:(.*):(.*):(.*):(.*)/i', $val['type'], $reg)) {
  7409. $param['options'] = array($reg[1].':'.$reg[2].':'.$reg[3].':'.$reg[4] => 'N');
  7410. $type = 'sellist';
  7411. } elseif (preg_match('/^sellist:(.*):(.*):(.*)/i', $val['type'], $reg)) {
  7412. $param['options'] = array($reg[1].':'.$reg[2].':'.$reg[3] => 'N');
  7413. $type = 'sellist';
  7414. } elseif (preg_match('/^sellist:(.*):(.*)/i', $val['type'], $reg)) {
  7415. $param['options'] = array($reg[1].':'.$reg[2] => 'N');
  7416. $type = 'sellist';
  7417. }
  7418. //
  7419. // TEST Value
  7420. //
  7421. // Use Validate class to allow external Modules to use data validation part instead of concentrate all test here (factoring) or just for reuse
  7422. $validate = new Validate($this->db, $langs);
  7423. // little trick : to perform tests with good performances sort tests by quick to low
  7424. //
  7425. // COMMON TESTS
  7426. //
  7427. // Required test and empty value
  7428. if ($required && !$validate->isNotEmptyString($fieldValue)) {
  7429. $this->setFieldError($fieldKey, $validate->error);
  7430. return false;
  7431. } elseif (!$required && !$validate->isNotEmptyString($fieldValue)) {
  7432. // if no value sent and the field is not mandatory, no need to perform tests
  7433. return true;
  7434. }
  7435. // MAX Size test
  7436. if (!empty($maxSize) && !$validate->isMaxLength($fieldValue, $maxSize)) {
  7437. $this->setFieldError($fieldKey, $validate->error);
  7438. return false;
  7439. }
  7440. // MIN Size test
  7441. if (!empty($minSize) && !$validate->isMinLength($fieldValue, $minSize)) {
  7442. $this->setFieldError($fieldKey, $validate->error);
  7443. return false;
  7444. }
  7445. //
  7446. // TESTS for TYPE
  7447. //
  7448. if (in_array($type, array('date', 'datetime', 'timestamp'))) {
  7449. if (!$validate->isTimestamp($fieldValue)) {
  7450. $this->setFieldError($fieldKey, $validate->error);
  7451. return false;
  7452. } else {
  7453. return true;
  7454. }
  7455. } elseif ($type == 'duration') {
  7456. if (!$validate->isDuration($fieldValue)) {
  7457. $this->setFieldError($fieldKey, $validate->error);
  7458. return false;
  7459. } else {
  7460. return true;
  7461. }
  7462. } elseif (in_array($type, array('double', 'real', 'price'))) {
  7463. // is numeric
  7464. if (!$validate->isNumeric($fieldValue)) {
  7465. $this->setFieldError($fieldKey, $validate->error);
  7466. return false;
  7467. } else {
  7468. return true;
  7469. }
  7470. } elseif ($type == 'boolean') {
  7471. if (!$validate->isBool($fieldValue)) {
  7472. $this->setFieldError($fieldKey, $validate->error);
  7473. return false;
  7474. } else {
  7475. return true;
  7476. }
  7477. } elseif ($type == 'mail') {
  7478. if (!$validate->isEmail($fieldValue)) {
  7479. $this->setFieldError($fieldKey, $validate->error);
  7480. return false;
  7481. }
  7482. } elseif ($type == 'url') {
  7483. if (!$validate->isUrl($fieldValue)) {
  7484. $this->setFieldError($fieldKey, $validate->error);
  7485. return false;
  7486. } else {
  7487. return true;
  7488. }
  7489. } elseif ($type == 'phone') {
  7490. if (!$validate->isPhone($fieldValue)) {
  7491. $this->setFieldError($fieldKey, $validate->error);
  7492. return false;
  7493. } else {
  7494. return true;
  7495. }
  7496. } elseif ($type == 'select' || $type == 'radio') {
  7497. if (!isset($param['options'][$fieldValue])) {
  7498. $this->error = $langs->trans('RequireValidValue');
  7499. return false;
  7500. } else {
  7501. return true;
  7502. }
  7503. } elseif ($type == 'sellist' || $type == 'chkbxlst') {
  7504. $param_list = array_keys($param['options']);
  7505. $InfoFieldList = explode(":", $param_list[0]);
  7506. $value_arr = explode(',', $fieldValue);
  7507. $value_arr = array_map(array($this->db, 'escape'), $value_arr);
  7508. $selectkey = "rowid";
  7509. if (count($InfoFieldList) > 4 && !empty($InfoFieldList[4])) {
  7510. $selectkey = $InfoFieldList[2];
  7511. }
  7512. if (!$validate->isInDb($value_arr, $InfoFieldList[0], $selectkey)) {
  7513. $this->setFieldError($fieldKey, $validate->error);
  7514. return false;
  7515. } else {
  7516. return true;
  7517. }
  7518. } elseif ($type == 'link') {
  7519. $param_list = array_keys($param['options']); // $param_list='ObjectName:classPath'
  7520. $InfoFieldList = explode(":", $param_list[0]);
  7521. $classname = $InfoFieldList[0];
  7522. $classpath = $InfoFieldList[1];
  7523. if (!$validate->isFetchable($fieldValue, $classname, $classpath)) {
  7524. $this->setFieldError($fieldKey, $validate->error);
  7525. return false;
  7526. } else {
  7527. return true;
  7528. }
  7529. }
  7530. // if no test failled all is ok
  7531. return true;
  7532. }
  7533. /**
  7534. * Function to show lines of extrafields with output datas.
  7535. * This function is responsible to output the <tr> and <td> according to correct number of columns received into $params['colspan'] or <div> according to $display_type
  7536. *
  7537. * @param Extrafields $extrafields Extrafield Object
  7538. * @param string $mode Show output ('view') or input ('create' or 'edit') for extrafield
  7539. * @param array $params Optional parameters. Example: array('style'=>'class="oddeven"', 'colspan'=>$colspan)
  7540. * @param string $keysuffix Suffix string to add after name and id of field (can be used to avoid duplicate names)
  7541. * @param string $keyprefix Prefix string to add before name and id of field (can be used to avoid duplicate names)
  7542. * @param string $onetrtd All fields in same tr td. Used by objectline_create.tpl.php for example.
  7543. * @param string $display_type "card" for form display, "line" for document line display (extrafields on propal line, order line, etc...)
  7544. * @return string String with html content to show
  7545. */
  7546. public function showOptionals($extrafields, $mode = 'view', $params = null, $keysuffix = '', $keyprefix = '', $onetrtd = '', $display_type = 'card')
  7547. {
  7548. global $db, $conf, $langs, $action, $form, $hookmanager;
  7549. if (!is_object($form)) {
  7550. $form = new Form($db);
  7551. }
  7552. if (!is_object($extrafields)) {
  7553. dol_syslog('Bad parameter extrafields for showOptionals', LOG_ERR);
  7554. return 'Bad parameter extrafields for showOptionals';
  7555. }
  7556. if (!is_array($extrafields->attributes[$this->table_element])) {
  7557. dol_syslog("extrafields->attributes was not loaded with extrafields->fetch_name_optionals_label(table_element);", LOG_WARNING);
  7558. }
  7559. $out = '';
  7560. $parameters = array('mode'=>$mode, 'params'=>$params, 'keysuffix'=>$keysuffix, 'keyprefix'=>$keyprefix, 'display_type'=>$display_type);
  7561. $reshook = $hookmanager->executeHooks('showOptionals', $parameters, $this, $action); // Note that $action and $object may have been modified by hook
  7562. if (empty($reshook)) {
  7563. if (is_array($extrafields->attributes[$this->table_element]) && key_exists('label', $extrafields->attributes[$this->table_element]) && is_array($extrafields->attributes[$this->table_element]['label']) && count($extrafields->attributes[$this->table_element]['label']) > 0) {
  7564. $out .= "\n";
  7565. $out .= '<!-- commonobject:showOptionals --> ';
  7566. $out .= "\n";
  7567. $nbofextrafieldsshown = 0;
  7568. $e = 0; // var to manage the modulo (odd/even)
  7569. $lastseparatorkeyfound = '';
  7570. $extrafields_collapse_num = '';
  7571. $extrafields_collapse_num_old = '';
  7572. $i = 0;
  7573. foreach ($extrafields->attributes[$this->table_element]['label'] as $key => $label) {
  7574. $i++;
  7575. // Show only the key field in params
  7576. if (is_array($params) && array_key_exists('onlykey', $params) && $key != $params['onlykey']) {
  7577. continue;
  7578. }
  7579. // Test on 'enabled' ('enabled' is different than 'list' = 'visibility')
  7580. $enabled = 1;
  7581. if ($enabled && isset($extrafields->attributes[$this->table_element]['enabled'][$key])) {
  7582. $enabled = dol_eval($extrafields->attributes[$this->table_element]['enabled'][$key], 1, 1, '2');
  7583. }
  7584. if (empty($enabled)) {
  7585. continue;
  7586. }
  7587. $visibility = 1;
  7588. if ($visibility && isset($extrafields->attributes[$this->table_element]['list'][$key])) {
  7589. $visibility = dol_eval($extrafields->attributes[$this->table_element]['list'][$key], 1, 1, '2');
  7590. }
  7591. $perms = 1;
  7592. if ($perms && isset($extrafields->attributes[$this->table_element]['perms'][$key])) {
  7593. $perms = dol_eval($extrafields->attributes[$this->table_element]['perms'][$key], 1, 1, '2');
  7594. }
  7595. if (($mode == 'create') && abs($visibility) != 1 && abs($visibility) != 3) {
  7596. continue; // <> -1 and <> 1 and <> 3 = not visible on forms, only on list
  7597. } elseif (($mode == 'edit') && abs($visibility) != 1 && abs($visibility) != 3 && abs($visibility) != 4) {
  7598. continue; // <> -1 and <> 1 and <> 3 = not visible on forms, only on list and <> 4 = not visible at the creation
  7599. } elseif ($mode == 'view' && empty($visibility)) {
  7600. continue;
  7601. }
  7602. if (empty($perms)) {
  7603. continue;
  7604. }
  7605. // Load language if required
  7606. if (!empty($extrafields->attributes[$this->table_element]['langfile'][$key])) {
  7607. $langs->load($extrafields->attributes[$this->table_element]['langfile'][$key]);
  7608. }
  7609. $colspan = 0;
  7610. if (is_array($params) && count($params) > 0 && $display_type=='card') {
  7611. if (array_key_exists('cols', $params)) {
  7612. $colspan = $params['cols'];
  7613. } elseif (array_key_exists('colspan', $params)) { // For backward compatibility. Use cols instead now.
  7614. $reg = array();
  7615. if (preg_match('/colspan="(\d+)"/', $params['colspan'], $reg)) {
  7616. $colspan = $reg[1];
  7617. } else {
  7618. $colspan = $params['colspan'];
  7619. }
  7620. }
  7621. }
  7622. $colspan = intval($colspan);
  7623. switch ($mode) {
  7624. case "view":
  7625. $value = ((!empty($this->array_options) && array_key_exists("options_".$key.$keysuffix, $this->array_options)) ? $this->array_options["options_".$key.$keysuffix] : null); // Value may be cleaned or formated later
  7626. break;
  7627. case "create":
  7628. case "edit":
  7629. // We get the value of property found with GETPOST so it takes into account:
  7630. // default values overwrite, restore back to list link, ... (but not 'default value in database' of field)
  7631. $check = 'alphanohtml';
  7632. if (in_array($extrafields->attributes[$this->table_element]['type'][$key], array('html', 'text'))) {
  7633. $check = 'restricthtml';
  7634. }
  7635. $getposttemp = GETPOST($keyprefix.'options_'.$key.$keysuffix, $check, 3); // GETPOST can get value from GET, POST or setup of default values overwrite.
  7636. // GETPOST("options_" . $key) can be 'abc' or array(0=>'abc')
  7637. if (is_array($getposttemp) || $getposttemp != '' || GETPOSTISSET($keyprefix.'options_'.$key.$keysuffix)) {
  7638. if (is_array($getposttemp)) {
  7639. // $getposttemp is an array but following code expects a comma separated string
  7640. $value = implode(",", $getposttemp);
  7641. } else {
  7642. $value = $getposttemp;
  7643. }
  7644. } else {
  7645. $value = (!empty($this->array_options["options_".$key]) ? $this->array_options["options_".$key] : ''); // No GET, no POST, no default value, so we take value of object.
  7646. }
  7647. //var_dump($keyprefix.' - '.$key.' - '.$keysuffix.' - '.$keyprefix.'options_'.$key.$keysuffix.' - '.$this->array_options["options_".$key.$keysuffix].' - '.$getposttemp.' - '.$value);
  7648. break;
  7649. }
  7650. $nbofextrafieldsshown++;
  7651. // Output value of the current field
  7652. if ($extrafields->attributes[$this->table_element]['type'][$key] == 'separate') {
  7653. $extrafields_collapse_num = $key;
  7654. /*
  7655. $extrafield_param = $extrafields->attributes[$this->table_element]['param'][$key];
  7656. if (!empty($extrafield_param) && is_array($extrafield_param)) {
  7657. $extrafield_param_list = array_keys($extrafield_param['options']);
  7658. if (count($extrafield_param_list) > 0) {
  7659. $extrafield_collapse_display_value = intval($extrafield_param_list[0]);
  7660. if ($extrafield_collapse_display_value == 1 || $extrafield_collapse_display_value == 2) {
  7661. //$extrafields_collapse_num = $extrafields->attributes[$this->table_element]['pos'][$key];
  7662. $extrafields_collapse_num = $key;
  7663. }
  7664. }
  7665. }
  7666. */
  7667. // if colspan=0 or 1, the second column is not extended, so the separator must be on 2 columns
  7668. $out .= $extrafields->showSeparator($key, $this, ($colspan ? $colspan + 1 : 2), $display_type, $mode);
  7669. $lastseparatorkeyfound = $key;
  7670. } else {
  7671. $collapse_group = $extrafields_collapse_num.(!empty($this->id) ? '_'.$this->id : '');
  7672. $class = (!empty($extrafields->attributes[$this->table_element]['hidden'][$key]) ? 'hideobject ' : '');
  7673. $csstyle = '';
  7674. if (is_array($params) && count($params) > 0) {
  7675. if (array_key_exists('class', $params)) {
  7676. $class .= $params['class'].' ';
  7677. }
  7678. if (array_key_exists('style', $params)) {
  7679. $csstyle = $params['style'];
  7680. }
  7681. }
  7682. // add html5 elements
  7683. $domData = ' data-element="extrafield"';
  7684. $domData .= ' data-targetelement="'.$this->element.'"';
  7685. $domData .= ' data-targetid="'.$this->id.'"';
  7686. $html_id = (empty($this->id) ? '' : 'extrarow-'.$this->element.'_'.$key.'_'.$this->id);
  7687. if ($display_type=='card') {
  7688. if (getDolGlobalString('MAIN_EXTRAFIELDS_USE_TWO_COLUMS') && ($e % 2) == 0) {
  7689. $colspan = 0;
  7690. }
  7691. if ($action == 'selectlines') {
  7692. $colspan++;
  7693. }
  7694. }
  7695. // Convert date into timestamp format (value in memory must be a timestamp)
  7696. if (in_array($extrafields->attributes[$this->table_element]['type'][$key], array('date'))) {
  7697. $datenotinstring = null;
  7698. if (array_key_exists('options_'.$key, $this->array_options)) {
  7699. $datenotinstring = $this->array_options['options_'.$key];
  7700. if (!is_numeric($this->array_options['options_'.$key])) { // For backward compatibility
  7701. $datenotinstring = $this->db->jdate($datenotinstring);
  7702. }
  7703. }
  7704. $datekey = $keyprefix.'options_'.$key.$keysuffix;
  7705. $value = (GETPOSTISSET($datekey)) ? dol_mktime(12, 0, 0, GETPOST($datekey.'month', 'int', 3), GETPOST($datekey.'day', 'int', 3), GETPOST($datekey.'year', 'int', 3)) : $datenotinstring;
  7706. }
  7707. if (in_array($extrafields->attributes[$this->table_element]['type'][$key], array('datetime'))) {
  7708. $datenotinstring = null;
  7709. if (array_key_exists('options_'.$key, $this->array_options)) {
  7710. $datenotinstring = $this->array_options['options_'.$key];
  7711. if (!is_numeric($this->array_options['options_'.$key])) { // For backward compatibility
  7712. $datenotinstring = $this->db->jdate($datenotinstring);
  7713. }
  7714. }
  7715. $timekey = $keyprefix.'options_'.$key.$keysuffix;
  7716. $value = (GETPOSTISSET($timekey)) ? dol_mktime(GETPOST($timekey.'hour', 'int', 3), GETPOST($timekey.'min', 'int', 3), GETPOST($timekey.'sec', 'int', 3), GETPOST($timekey.'month', 'int', 3), GETPOST($timekey.'day', 'int', 3), GETPOST($timekey.'year', 'int', 3), 'tzuserrel') : $datenotinstring;
  7717. }
  7718. // Convert float submited string into real php numeric (value in memory must be a php numeric)
  7719. if (in_array($extrafields->attributes[$this->table_element]['type'][$key], array('price', 'double'))) {
  7720. if (GETPOSTISSET($keyprefix.'options_'.$key.$keysuffix) || $value) {
  7721. $value = price2num($value);
  7722. } elseif (isset($this->array_options['options_'.$key])) {
  7723. $value = $this->array_options['options_'.$key];
  7724. }
  7725. }
  7726. // HTML, text, select, integer and varchar: take into account default value in database if in create mode
  7727. if (in_array($extrafields->attributes[$this->table_element]['type'][$key], array('html', 'text', 'varchar', 'select', 'int', 'boolean'))) {
  7728. if ($action == 'create' || $mode == 'create') {
  7729. $value = (GETPOSTISSET($keyprefix.'options_'.$key.$keysuffix) || $value) ? $value : $extrafields->attributes[$this->table_element]['default'][$key];
  7730. }
  7731. }
  7732. $labeltoshow = $langs->trans($label);
  7733. $helptoshow = $langs->trans($extrafields->attributes[$this->table_element]['help'][$key]);
  7734. if ($display_type == 'card') {
  7735. $out .= '<tr '.($html_id ? 'id="'.$html_id.'" ' : '').$csstyle.' class="field_options_'.$key.' '.$class.$this->element.'_extras_'.$key.' trextrafields_collapse'.$collapse_group.'" '.$domData.' >';
  7736. if (getDolGlobalString('MAIN_VIEW_LINE_NUMBER') && ($action == 'view' || $action == 'valid' || $action == 'editline' || $action == 'confirm_valid' || $action == 'confirm_cancel')) {
  7737. $out .= '<td></td>';
  7738. }
  7739. $out .= '<td class="'.(empty($params['tdclass']) ? 'titlefieldcreate' : $params['tdclass']).' wordbreak';
  7740. } elseif ($display_type == 'line') {
  7741. $out .= '<div '.($html_id ? 'id="'.$html_id.'" ' : '').$csstyle.' class="fieldline_options_'.$key.' '.$class.$this->element.'_extras_'.$key.' trextrafields_collapse'.$collapse_group.'" '.$domData.' >';
  7742. $out .= '<div style="display: inline-block; padding-right:4px" class="wordbreak';
  7743. }
  7744. //$out .= "titlefield";
  7745. //if (GETPOST('action', 'restricthtml') == 'create') $out.='create';
  7746. // BUG #11554 : For public page, use red dot for required fields, instead of bold label
  7747. $tpl_context = isset($params["tpl_context"]) ? $params["tpl_context"] : "none";
  7748. if ($tpl_context != "public") { // Public page : red dot instead of fieldrequired characters
  7749. if ($mode != 'view' && !empty($extrafields->attributes[$this->table_element]['required'][$key])) {
  7750. $out .= ' fieldrequired';
  7751. }
  7752. }
  7753. $out .= '">';
  7754. if ($tpl_context == "public") { // Public page : red dot instead of fieldrequired characters
  7755. if (!empty($extrafields->attributes[$this->table_element]['help'][$key])) {
  7756. $out .= $form->textwithpicto($labeltoshow, $helptoshow);
  7757. } else {
  7758. $out .= $labeltoshow;
  7759. }
  7760. if ($mode != 'view' && !empty($extrafields->attributes[$this->table_element]['required'][$key])) {
  7761. $out .= '&nbsp;<span style="color: red">*</span>';
  7762. }
  7763. } else {
  7764. if (!empty($extrafields->attributes[$this->table_element]['help'][$key])) {
  7765. $out .= $form->textwithpicto($labeltoshow, $helptoshow);
  7766. } else {
  7767. $out .= $labeltoshow;
  7768. }
  7769. }
  7770. $out .= ($display_type == 'card' ? '</td>' : '</div>');
  7771. $html_id = !empty($this->id) ? $this->element.'_extras_'.$key.'_'.$this->id : '';
  7772. if ($display_type == 'card') {
  7773. // a first td column was already output (and may be another on before if MAIN_VIEW_LINE_NUMBER set), so this td is the next one
  7774. $out .= '<td '.($html_id ? 'id="'.$html_id.'" ' : '').' class="valuefieldcreate '.$this->element.'_extras_'.$key.'" '.($colspan ? ' colspan="'.$colspan.'"' : '').'>';
  7775. } elseif ($display_type == 'line') {
  7776. $out .= '<div '.($html_id ? 'id="'.$html_id.'" ' : '').' style="display: inline-block" class="valuefieldcreate '.$this->element.'_extras_'.$key.' extra_inline_'.$extrafields->attributes[$this->table_element]['type'][$key].'">';
  7777. }
  7778. switch ($mode) {
  7779. case "view":
  7780. $out .= $extrafields->showOutputField($key, $value, '', $this->table_element);
  7781. break;
  7782. case "create":
  7783. $out .= $extrafields->showInputField($key, $value, '', $keysuffix, '', 0, $this->id, $this->table_element);
  7784. break;
  7785. case "edit":
  7786. $out .= $extrafields->showInputField($key, $value, '', $keysuffix, '', 0, $this->id, $this->table_element);
  7787. break;
  7788. }
  7789. $out .= ($display_type=='card' ? '</td>' : '</div>');
  7790. if (getDolGlobalString('MAIN_EXTRAFIELDS_USE_TWO_COLUMS') && (($e % 2) == 1)) {
  7791. $out .= ($display_type=='card' ? '</tr>' : '</div>');
  7792. } else {
  7793. $out .= ($display_type=='card' ? '</tr>' : '</div>');
  7794. }
  7795. $e++;
  7796. }
  7797. }
  7798. $out .= "\n";
  7799. // Add code to manage list depending on others
  7800. if (!empty($conf->use_javascript_ajax)) {
  7801. $out .= $this->getJSListDependancies();
  7802. }
  7803. $out .= '<!-- commonobject:showOptionals end --> '."\n";
  7804. if (empty($nbofextrafieldsshown)) {
  7805. $out = '';
  7806. }
  7807. }
  7808. }
  7809. $out .= $hookmanager->resPrint;
  7810. return $out;
  7811. }
  7812. /**
  7813. * @param string $type Type for prefix
  7814. * @return string Javacript code to manage dependency
  7815. */
  7816. public function getJSListDependancies($type = '_extra')
  7817. {
  7818. $out = '
  7819. <script nonce="'.getNonce().'">
  7820. jQuery(document).ready(function() {
  7821. function showOptions'.$type.'(child_list, parent_list, orig_select)
  7822. {
  7823. var val = $("select[name=\""+parent_list+"\"]").val();
  7824. var parentVal = parent_list + ":" + val;
  7825. if(typeof val == "string"){
  7826. if(val != "") {
  7827. var options = orig_select.find("option[parent=\""+parentVal+"\"]").clone();
  7828. $("select[name=\""+child_list+"\"] option[parent]").remove();
  7829. $("select[name=\""+child_list+"\"]").append(options);
  7830. } else {
  7831. var options = orig_select.find("option[parent]").clone();
  7832. $("select[name=\""+child_list+"\"] option[parent]").remove();
  7833. $("select[name=\""+child_list+"\"]").append(options);
  7834. }
  7835. } else if(val > 0) {
  7836. var options = orig_select.find("option[parent=\""+parentVal+"\"]").clone();
  7837. $("select[name=\""+child_list+"\"] option[parent]").remove();
  7838. $("select[name=\""+child_list+"\"]").append(options);
  7839. } else {
  7840. var options = orig_select.find("option[parent]").clone();
  7841. $("select[name=\""+child_list+"\"] option[parent]").remove();
  7842. $("select[name=\""+child_list+"\"]").append(options);
  7843. }
  7844. }
  7845. function setListDependencies'.$type.'() {
  7846. jQuery("select option[parent]").parent().each(function() {
  7847. var orig_select = {};
  7848. var child_list = $(this).attr("name");
  7849. orig_select[child_list] = $(this).clone();
  7850. var parent = $(this).find("option[parent]:first").attr("parent");
  7851. var infos = parent.split(":");
  7852. var parent_list = infos[0];
  7853. //Hide daughters lists
  7854. if ($("#"+child_list).val() == 0 && $("#"+parent_list).val() == 0){
  7855. $("#"+child_list).hide();
  7856. //Show mother lists
  7857. } else if ($("#"+parent_list).val() != 0){
  7858. $("#"+parent_list).show();
  7859. }
  7860. //Show the child list if the parent list value is selected
  7861. $("select[name=\""+parent_list+"\"]").click(function() {
  7862. if ($(this).val() != 0){
  7863. $("#"+child_list).show()
  7864. }
  7865. });
  7866. //When we change parent list
  7867. $("select[name=\""+parent_list+"\"]").change(function() {
  7868. showOptions'.$type.'(child_list, parent_list, orig_select[child_list]);
  7869. //Select the value 0 on child list after a change on the parent list
  7870. $("#"+child_list).val(0).trigger("change");
  7871. //Hide child lists if the parent value is set to 0
  7872. if ($(this).val() == 0){
  7873. $("#"+child_list).hide();
  7874. }
  7875. });
  7876. });
  7877. }
  7878. setListDependencies'.$type.'();
  7879. });
  7880. </script>'."\n";
  7881. return $out;
  7882. }
  7883. /**
  7884. * Returns the rights used for this class
  7885. *
  7886. * @return stdClass Object of permission for the module
  7887. */
  7888. public function getRights()
  7889. {
  7890. global $user;
  7891. $module = empty($this->module) ? '' : $this->module;
  7892. $element = $this->element;
  7893. if ($element == 'facturerec') {
  7894. $element = 'facture';
  7895. } elseif ($element == 'invoice_supplier_rec') {
  7896. return !$user->hasRight('fournisseur', 'facture') ? null : $user->hasRight('fournisseur', 'facture');
  7897. } elseif ($module && $user->hasRight($module, $element)) {
  7898. // for modules built with ModuleBuilder
  7899. return $user->hasRight($module, $element);
  7900. }
  7901. return $user->rights->$element;
  7902. }
  7903. /**
  7904. * Function used to replace a thirdparty id with another one.
  7905. * This function is meant to be called from replaceThirdparty with the appropriate tables
  7906. * Column name fk_soc MUST be used to identify thirdparties
  7907. *
  7908. * @param DoliDB $dbs Database handler
  7909. * @param int $origin_id Old thirdparty id (the thirdparty to delete)
  7910. * @param int $dest_id New thirdparty id (the thirdparty that will received element of the other)
  7911. * @param string[] $tables Tables that need to be changed
  7912. * @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)
  7913. * @return bool True if success, False if error
  7914. */
  7915. public static function commonReplaceThirdparty(DoliDB $dbs, $origin_id, $dest_id, array $tables, $ignoreerrors = 0)
  7916. {
  7917. foreach ($tables as $table) {
  7918. $sql = 'UPDATE '.$dbs->prefix().$table.' SET fk_soc = '.((int) $dest_id).' WHERE fk_soc = '.((int) $origin_id);
  7919. if (!$dbs->query($sql)) {
  7920. if ($ignoreerrors) {
  7921. return true; // TODO Not enough. If there is A-B on kept thirdparty and B-C on old one, we must get A-B-C after merge. Not A-B.
  7922. }
  7923. //$this->errors = $db->lasterror();
  7924. return false;
  7925. }
  7926. }
  7927. return true;
  7928. }
  7929. /**
  7930. * Function used to replace a product id with another one.
  7931. * This function is meant to be called from replaceProduct with the appropriate tables
  7932. * Column name fk_product MUST be used to identify products
  7933. *
  7934. * @param DoliDB $dbs Database handler
  7935. * @param int $origin_id Old product id (the product to delete)
  7936. * @param int $dest_id New product id (the product that will received element of the other)
  7937. * @param string[] $tables Tables that need to be changed
  7938. * @param int $ignoreerrors Ignore errors. Return true even if errors. We need this when replacement can fails like for categories (categorie of old product may already exists on new one)
  7939. * @return bool True if success, False if error
  7940. */
  7941. public static function commonReplaceProduct(DoliDB $dbs, $origin_id, $dest_id, array $tables, $ignoreerrors = 0)
  7942. {
  7943. foreach ($tables as $table) {
  7944. $sql = 'UPDATE '.MAIN_DB_PREFIX.$table.' SET fk_product = '.((int) $dest_id).' WHERE fk_product = '.((int) $origin_id);
  7945. if (!$dbs->query($sql)) {
  7946. if ($ignoreerrors) {
  7947. return true; // TODO Not enough. If there is A-B on kept product and B-C on old one, we must get A-B-C after merge. Not A-B.
  7948. }
  7949. //$this->errors = $db->lasterror();
  7950. return false;
  7951. }
  7952. }
  7953. return true;
  7954. }
  7955. /**
  7956. * Get buy price to use for margin calculation. This function is called when buy price is unknown.
  7957. * Set buy price = sell price if ForceBuyingPriceIfNull configured,
  7958. * elseif calculation MARGIN_TYPE = 'costprice' and costprice is defined, use costprice as buyprice
  7959. * elseif calculation MARGIN_TYPE = 'pmp' and pmp is calculated, use pmp as buyprice
  7960. * else set min buy price as buy price
  7961. *
  7962. * @param float $unitPrice Product unit price
  7963. * @param float $discountPercent Line discount percent
  7964. * @param int $fk_product Product id
  7965. * @return float Return integer <0 if KO, buyprice if OK
  7966. */
  7967. public function defineBuyPrice($unitPrice = 0.0, $discountPercent = 0.0, $fk_product = 0)
  7968. {
  7969. global $conf;
  7970. $buyPrice = 0;
  7971. if (($unitPrice > 0) && (isset($conf->global->ForceBuyingPriceIfNull) && getDolGlobalInt('ForceBuyingPriceIfNull') > 0)) {
  7972. // When ForceBuyingPriceIfNull is set
  7973. $buyPrice = $unitPrice * (1 - $discountPercent / 100);
  7974. } else {
  7975. // Get cost price for margin calculation
  7976. if (!empty($fk_product) && $fk_product > 0) {
  7977. if (getDolGlobalString('MARGIN_TYPE') == 'costprice') {
  7978. require_once DOL_DOCUMENT_ROOT.'/product/class/product.class.php';
  7979. $product = new Product($this->db);
  7980. $result = $product->fetch($fk_product);
  7981. if ($result <= 0) {
  7982. $this->errors[] = 'ErrorProductIdDoesNotExists';
  7983. return -1;
  7984. }
  7985. if ($product->cost_price > 0) {
  7986. $buyPrice = $product->cost_price;
  7987. } elseif ($product->pmp > 0) {
  7988. $buyPrice = $product->pmp;
  7989. }
  7990. } elseif (getDolGlobalString('MARGIN_TYPE') == 'pmp') {
  7991. require_once DOL_DOCUMENT_ROOT.'/product/class/product.class.php';
  7992. $product = new Product($this->db);
  7993. $result = $product->fetch($fk_product);
  7994. if ($result <= 0) {
  7995. $this->errors[] = 'ErrorProductIdDoesNotExists';
  7996. return -1;
  7997. }
  7998. if ($product->pmp > 0) {
  7999. $buyPrice = $product->pmp;
  8000. }
  8001. }
  8002. if (empty($buyPrice) && isset($conf->global->MARGIN_TYPE) && in_array($conf->global->MARGIN_TYPE, array('1', 'pmp', 'costprice'))) {
  8003. require_once DOL_DOCUMENT_ROOT.'/fourn/class/fournisseur.product.class.php';
  8004. $productFournisseur = new ProductFournisseur($this->db);
  8005. if (($result = $productFournisseur->find_min_price_product_fournisseur($fk_product)) > 0) {
  8006. $buyPrice = $productFournisseur->fourn_unitprice;
  8007. } elseif ($result < 0) {
  8008. $this->errors[] = $productFournisseur->error;
  8009. return -2;
  8010. }
  8011. }
  8012. }
  8013. }
  8014. return $buyPrice;
  8015. }
  8016. // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
  8017. /**
  8018. * Show photos of an object (nbmax maximum), into several columns
  8019. *
  8020. * @param string $modulepart 'product', 'ticket', ...
  8021. * @param string $sdir Directory to scan (full absolute path)
  8022. * @param int $size 0=original size, 1='small' use thumbnail if possible
  8023. * @param int $nbmax Nombre maximum de photos (0=pas de max)
  8024. * @param int $nbbyrow Number of image per line or -1 to use div separator or 0 to use no separator. Used only if size=1 or 'small'.
  8025. * @param int $showfilename 1=Show filename
  8026. * @param int $showaction 1=Show icon with action links (resize, delete)
  8027. * @param int $maxHeight Max height of original image when size='small' (so we can use original even if small requested). If 0, always use 'small' thumb image.
  8028. * @param int $maxWidth Max width of original image when size='small'
  8029. * @param int $nolink Do not add a href link to view enlarged imaged into a new tab
  8030. * @param int|string $overwritetitle Do not add title tag on image
  8031. * @param int $usesharelink Use the public shared link of image (if not available, the 'nophoto' image will be shown instead)
  8032. * @param string $cache A string if we want to use a cached version of image
  8033. * @param string $addphotorefcss Add CSS to img of photos
  8034. * @return string Html code to show photo. Number of photos shown is saved in this->nbphoto
  8035. */
  8036. public function show_photos($modulepart, $sdir, $size = 0, $nbmax = 0, $nbbyrow = 5, $showfilename = 0, $showaction = 0, $maxHeight = 120, $maxWidth = 160, $nolink = 0, $overwritetitle = 0, $usesharelink = 0, $cache = '', $addphotorefcss = 'photoref')
  8037. {
  8038. // phpcs:enable
  8039. global $conf, $user, $langs;
  8040. include_once DOL_DOCUMENT_ROOT.'/core/lib/files.lib.php';
  8041. include_once DOL_DOCUMENT_ROOT.'/core/lib/images.lib.php';
  8042. $sortfield = 'position_name';
  8043. $sortorder = 'asc';
  8044. $dir = $sdir.'/';
  8045. $pdir = '/';
  8046. $dir .= get_exdir(0, 0, 0, 0, $this, $modulepart);
  8047. $pdir .= get_exdir(0, 0, 0, 0, $this, $modulepart);
  8048. // For backward compatibility
  8049. if ($modulepart == 'product') {
  8050. if (getDolGlobalInt('PRODUCT_USE_OLD_PATH_FOR_PHOTO')) {
  8051. $dir = $sdir.'/'.get_exdir($this->id, 2, 0, 0, $this, $modulepart).$this->id."/photos/";
  8052. $pdir = '/'.get_exdir($this->id, 2, 0, 0, $this, $modulepart).$this->id."/photos/";
  8053. }
  8054. }
  8055. // Defined relative dir to DOL_DATA_ROOT
  8056. $relativedir = '';
  8057. if ($dir) {
  8058. $relativedir = preg_replace('/^'.preg_quote(DOL_DATA_ROOT, '/').'/', '', $dir);
  8059. $relativedir = preg_replace('/^[\\/]/', '', $relativedir);
  8060. $relativedir = preg_replace('/[\\/]$/', '', $relativedir);
  8061. }
  8062. $dirthumb = $dir.'thumbs/';
  8063. $pdirthumb = $pdir.'thumbs/';
  8064. $return = '<!-- Photo -->'."\n";
  8065. $nbphoto = 0;
  8066. $filearray = dol_dir_list($dir, "files", 0, '', '(\.meta|_preview.*\.png)$', $sortfield, (strtolower($sortorder) == 'desc' ? SORT_DESC : SORT_ASC), 1);
  8067. /*if (getDolGlobalInt('PRODUCT_USE_OLD_PATH_FOR_PHOTO')) // For backward compatiblity, we scan also old dirs
  8068. {
  8069. $filearrayold=dol_dir_list($dirold,"files",0,'','(\.meta|_preview.*\.png)$',$sortfield,(strtolower($sortorder)=='desc'?SORT_DESC:SORT_ASC),1);
  8070. $filearray=array_merge($filearray, $filearrayold);
  8071. }*/
  8072. completeFileArrayWithDatabaseInfo($filearray, $relativedir);
  8073. if (count($filearray)) {
  8074. if ($sortfield && $sortorder) {
  8075. $filearray = dol_sort_array($filearray, $sortfield, $sortorder);
  8076. }
  8077. foreach ($filearray as $key => $val) {
  8078. $photo = '';
  8079. $file = $val['name'];
  8080. //if (dol_is_file($dir.$file) && image_format_supported($file) >= 0)
  8081. if (image_format_supported($file) >= 0) {
  8082. $nbphoto++;
  8083. $photo = $file;
  8084. $viewfilename = $file;
  8085. if ($size == 1 || $size == 'small') { // Format vignette
  8086. // Find name of thumb file
  8087. $photo_vignette = basename(getImageFileNameForSize($dir.$file, '_small'));
  8088. if (!dol_is_file($dirthumb.$photo_vignette)) {
  8089. // The thumb does not exists, so we will use the original file
  8090. $dirthumb = $dir;
  8091. $pdirthumb = $pdir;
  8092. $photo_vignette = basename($file);
  8093. }
  8094. // Get filesize of original file
  8095. $imgarray = dol_getImageSize($dir.$photo);
  8096. if ($nbbyrow > 0) {
  8097. if ($nbphoto == 1) {
  8098. $return .= '<table class="valigntop center centpercent" style="border: 0; padding: 2px; border-spacing: 2px; border-collapse: separate;">';
  8099. }
  8100. if ($nbphoto % $nbbyrow == 1) {
  8101. $return .= '<tr class="center valignmiddle" style="border: 1px">';
  8102. }
  8103. $return .= '<td style="width: '.ceil(100 / $nbbyrow).'%" class="photo">'."\n";
  8104. } elseif ($nbbyrow < 0) {
  8105. $return .= '<div class="inline-block">'."\n";
  8106. }
  8107. $relativefile = preg_replace('/^\//', '', $pdir.$photo);
  8108. if (empty($nolink)) {
  8109. $urladvanced = getAdvancedPreviewUrl($modulepart, $relativefile, 0, 'entity='.$this->entity);
  8110. if ($urladvanced) {
  8111. $return .= '<a href="'.$urladvanced.'">';
  8112. } else {
  8113. $return .= '<a href="'.DOL_URL_ROOT.'/viewimage.php?modulepart='.$modulepart.'&entity='.$this->entity.'&file='.urlencode($pdir.$photo).'" class="aphoto" target="_blank" rel="noopener noreferrer">';
  8114. }
  8115. }
  8116. // Show image (width height=$maxHeight)
  8117. // Si fichier vignette disponible et image source trop grande, on utilise la vignette, sinon on utilise photo origine
  8118. $alt = $langs->transnoentitiesnoconv('File').': '.$relativefile;
  8119. $alt .= ' - '.$langs->transnoentitiesnoconv('Size').': '.$imgarray['width'].'x'.$imgarray['height'];
  8120. if ($overwritetitle) {
  8121. if (is_numeric($overwritetitle)) {
  8122. $alt = '';
  8123. } else {
  8124. $alt = $overwritetitle;
  8125. }
  8126. }
  8127. if ($usesharelink) {
  8128. if ($val['share']) {
  8129. if (empty($maxHeight) || ($photo_vignette && $imgarray['height'] > $maxHeight)) {
  8130. $return .= '<!-- Show original file (thumb not yet available with shared links) -->';
  8131. $return .= '<img class="photo photowithmargin'.($addphotorefcss ? ' '.$addphotorefcss : '').'"'.($maxHeight ? ' height="'.$maxHeight.'"' : '').' src="'.DOL_URL_ROOT.'/viewimage.php?hashp='.urlencode($val['share']).($cache ? '&cache='.urlencode($cache) : '').'" title="'.dol_escape_htmltag($alt).'">';
  8132. } else {
  8133. $return .= '<!-- Show original file -->';
  8134. $return .= '<img class="photo photowithmargin'.($addphotorefcss ? ' '.$addphotorefcss : '').'" height="'.$maxHeight.'" src="'.DOL_URL_ROOT.'/viewimage.php?hashp='.urlencode($val['share']).($cache ? '&cache='.urlencode($cache) : '').'" title="'.dol_escape_htmltag($alt).'">';
  8135. }
  8136. } else {
  8137. $return .= '<!-- Show nophoto file (because file is not shared) -->';
  8138. $return .= '<img class="photo photowithmargin'.($addphotorefcss ? ' '.$addphotorefcss : '').'" height="'.$maxHeight.'" src="'.DOL_URL_ROOT.'/public/theme/common/nophoto.png" title="'.dol_escape_htmltag($alt).'">';
  8139. }
  8140. } else {
  8141. if (empty($maxHeight) || ($photo_vignette && $imgarray['height'] > $maxHeight)) {
  8142. $return .= '<!-- Show thumb -->';
  8143. $return .= '<img class="photo photowithmargin'.($addphotorefcss ? ' '.$addphotorefcss : '').' maxwidth150onsmartphone maxwidth200"'.($maxHeight ? ' height="'.$maxHeight.'"' : '').' src="'.DOL_URL_ROOT.'/viewimage.php?modulepart='.$modulepart.'&entity='.$this->entity.($cache ? '&cache='.urlencode($cache) : '').'&file='.urlencode($pdirthumb.$photo_vignette).'" title="'.dol_escape_htmltag($alt).'">';
  8144. } else {
  8145. $return .= '<!-- Show original file -->';
  8146. $return .= '<img class="photo photowithmargin'.($addphotorefcss ? ' '.$addphotorefcss : '').'" height="'.$maxHeight.'" src="'.DOL_URL_ROOT.'/viewimage.php?modulepart='.$modulepart.'&entity='.$this->entity.($cache ? '&cache='.urlencode($cache) : '').'&file='.urlencode($pdir.$photo).'" title="'.dol_escape_htmltag($alt).'">';
  8147. }
  8148. }
  8149. if (empty($nolink)) {
  8150. $return .= '</a>';
  8151. }
  8152. if ($showfilename) {
  8153. $return .= '<br>'.$viewfilename;
  8154. }
  8155. if ($showaction) {
  8156. $return .= '<br>';
  8157. // If $photo_vignette set, we add link to generate thumbs if file is an image and ->imgWidth or->imgHeight higher than limits
  8158. if ($photo_vignette && (image_format_supported($photo) > 0) && ((isset($this->imgWidth) && $this->imgWidth > $maxWidth) || (isset($this->imgHeight) && $this->imgHeight > $maxHeight))) {
  8159. $return .= '<a href="'.$_SERVER["PHP_SELF"].'?id='.$this->id.'&action=addthumb&token='.newToken().'&file='.urlencode($pdir.$viewfilename).'">'.img_picto($langs->trans('GenerateThumb'), 'refresh').'&nbsp;&nbsp;</a>';
  8160. }
  8161. // Special cas for product
  8162. if ($modulepart == 'product' && ($user->hasRight('produit', 'creer') || $user->hasRight('service', 'creer'))) {
  8163. // Link to resize
  8164. $return .= '<a href="'.DOL_URL_ROOT.'/core/photos_resize.php?modulepart='.urlencode('produit|service').'&id='.$this->id.'&file='.urlencode($pdir.$viewfilename).'" title="'.dol_escape_htmltag($langs->trans("Resize")).'">'.img_picto($langs->trans("Resize"), 'resize', '').'</a> &nbsp; ';
  8165. // Link to delete
  8166. $return .= '<a href="'.$_SERVER["PHP_SELF"].'?id='.$this->id.'&action=delete&token='.newToken().'&file='.urlencode($pdir.$viewfilename).'">';
  8167. $return .= img_delete().'</a>';
  8168. }
  8169. }
  8170. $return .= "\n";
  8171. if ($nbbyrow > 0) {
  8172. $return .= '</td>';
  8173. if (($nbphoto % $nbbyrow) == 0) {
  8174. $return .= '</tr>';
  8175. }
  8176. } elseif ($nbbyrow < 0) {
  8177. $return .= '</div>'."\n";
  8178. }
  8179. }
  8180. if (empty($size)) { // Format origine
  8181. $return .= '<img class="photo photowithmargin" src="'.DOL_URL_ROOT.'/viewimage.php?modulepart='.$modulepart.'&entity='.$this->entity.'&file='.urlencode($pdir.$photo).'">';
  8182. if ($showfilename) {
  8183. $return .= '<br>'.$viewfilename;
  8184. }
  8185. if ($showaction) {
  8186. // Special case for product
  8187. if ($modulepart == 'product' && ($user->hasRight('produit', 'creer') || $user->hasRight('service', 'creer'))) {
  8188. // Link to resize
  8189. $return .= '<a href="'.DOL_URL_ROOT.'/core/photos_resize.php?modulepart='.urlencode('produit|service').'&id='.$this->id.'&file='.urlencode($pdir.$viewfilename).'" title="'.dol_escape_htmltag($langs->trans("Resize")).'">'.img_picto($langs->trans("Resize"), 'resize', '').'</a> &nbsp; ';
  8190. // Link to delete
  8191. $return .= '<a href="'.$_SERVER["PHP_SELF"].'?id='.$this->id.'&action=delete&token='.newToken().'&file='.urlencode($pdir.$viewfilename).'">';
  8192. $return .= img_delete().'</a>';
  8193. }
  8194. }
  8195. }
  8196. // On continue ou on arrete de boucler ?
  8197. if ($nbmax && $nbphoto >= $nbmax) {
  8198. break;
  8199. }
  8200. }
  8201. }
  8202. if ($size == 1 || $size == 'small') {
  8203. if ($nbbyrow > 0) {
  8204. // Ferme tableau
  8205. while ($nbphoto % $nbbyrow) {
  8206. $return .= '<td style="width: '.ceil(100 / $nbbyrow).'%">&nbsp;</td>';
  8207. $nbphoto++;
  8208. }
  8209. if ($nbphoto) {
  8210. $return .= '</table>';
  8211. }
  8212. }
  8213. }
  8214. }
  8215. $this->nbphoto = $nbphoto;
  8216. return $return;
  8217. }
  8218. /**
  8219. * Function test if type is array
  8220. *
  8221. * @param array $info content informations of field
  8222. * @return bool true if array
  8223. */
  8224. protected function isArray($info)
  8225. {
  8226. if (is_array($info)) {
  8227. if (isset($info['type']) && $info['type'] == 'array') {
  8228. return true;
  8229. } else {
  8230. return false;
  8231. }
  8232. }
  8233. return false;
  8234. }
  8235. /**
  8236. * Function test if type is date
  8237. *
  8238. * @param array $info content informations of field
  8239. * @return bool true if date
  8240. */
  8241. public function isDate($info)
  8242. {
  8243. if (isset($info['type']) && ($info['type'] == 'date' || $info['type'] == 'datetime' || $info['type'] == 'timestamp')) {
  8244. return true;
  8245. }
  8246. return false;
  8247. }
  8248. /**
  8249. * Function test if type is duration
  8250. *
  8251. * @param array $info content informations of field
  8252. * @return bool true if field of type duration
  8253. */
  8254. public function isDuration($info)
  8255. {
  8256. if (is_array($info)) {
  8257. if (isset($info['type']) && ($info['type'] == 'duration')) {
  8258. return true;
  8259. } else {
  8260. return false;
  8261. }
  8262. } else {
  8263. return false;
  8264. }
  8265. }
  8266. /**
  8267. * Function test if type is integer
  8268. *
  8269. * @param array $info content informations of field
  8270. * @return bool true if integer
  8271. */
  8272. public function isInt($info)
  8273. {
  8274. if (is_array($info)) {
  8275. if (isset($info['type']) && (preg_match('/(^int|int$)/i', $info['type']))) {
  8276. return true;
  8277. } else {
  8278. return false;
  8279. }
  8280. } else {
  8281. return false;
  8282. }
  8283. }
  8284. /**
  8285. * Function test if type is float
  8286. *
  8287. * @param array $info content informations of field
  8288. * @return bool true if float
  8289. */
  8290. public function isFloat($info)
  8291. {
  8292. if (is_array($info)) {
  8293. if (isset($info['type']) && (preg_match('/^(double|real|price)/i', $info['type']))) {
  8294. return true;
  8295. } else {
  8296. return false;
  8297. }
  8298. }
  8299. return false;
  8300. }
  8301. /**
  8302. * Function test if type is text
  8303. *
  8304. * @param array $info content informations of field
  8305. * @return bool true if type text
  8306. */
  8307. public function isText($info)
  8308. {
  8309. if (is_array($info)) {
  8310. if (isset($info['type']) && $info['type'] == 'text') {
  8311. return true;
  8312. } else {
  8313. return false;
  8314. }
  8315. }
  8316. return false;
  8317. }
  8318. /**
  8319. * Function test if field can be null
  8320. *
  8321. * @param array $info content informations of field
  8322. * @return bool true if it can be null
  8323. */
  8324. protected function canBeNull($info)
  8325. {
  8326. if (is_array($info)) {
  8327. if (isset($info['notnull']) && $info['notnull'] != '1') {
  8328. return true;
  8329. } else {
  8330. return false;
  8331. }
  8332. }
  8333. return true;
  8334. }
  8335. /**
  8336. * Function test if field is forced to null if zero or empty
  8337. *
  8338. * @param array $info content informations of field
  8339. * @return bool true if forced to null
  8340. */
  8341. protected function isForcedToNullIfZero($info)
  8342. {
  8343. if (is_array($info)) {
  8344. if (isset($info['notnull']) && $info['notnull'] == '-1') {
  8345. return true;
  8346. } else {
  8347. return false;
  8348. }
  8349. }
  8350. return false;
  8351. }
  8352. /**
  8353. * Function test if is indexed
  8354. *
  8355. * @param array $info content informations of field
  8356. * @return bool
  8357. */
  8358. protected function isIndex($info)
  8359. {
  8360. if (is_array($info)) {
  8361. if (isset($info['index']) && $info['index'] == true) {
  8362. return true;
  8363. } else {
  8364. return false;
  8365. }
  8366. }
  8367. return false;
  8368. }
  8369. /**
  8370. * Function to prepare a part of the query for insert by returning an array with all properties of object.
  8371. *
  8372. * Note $this->${field} are set by the page that make the createCommon() or the updateCommon().
  8373. * $this->${field} should be a clean and string value (so date are formated for SQL insert).
  8374. *
  8375. * @return array Array with all values of each properties to update
  8376. */
  8377. protected function setSaveQuery()
  8378. {
  8379. global $conf;
  8380. $queryarray = array();
  8381. foreach ($this->fields as $field => $info) { // Loop on definition of fields
  8382. // Depending on field type ('datetime', ...)
  8383. if ($this->isDate($info)) {
  8384. if (empty($this->{$field})) {
  8385. $queryarray[$field] = null;
  8386. } else {
  8387. $queryarray[$field] = $this->db->idate($this->{$field});
  8388. }
  8389. } elseif ($this->isDuration($info)) {
  8390. // $this->{$field} may be null, '', 0, '0', 123, '123'
  8391. if ((isset($this->{$field}) && $this->{$field} != '') || !empty($info['notnull'])) {
  8392. if (!isset($this->{$field})) {
  8393. if (!empty($info['default'])) {
  8394. $queryarray[$field] = $info['default'];
  8395. } else {
  8396. $queryarray[$field] = 0;
  8397. }
  8398. } else {
  8399. $queryarray[$field] = (int) $this->{$field}; // If '0', it may be set to null later if $info['notnull'] == -1
  8400. }
  8401. } else {
  8402. $queryarray[$field] = null;
  8403. }
  8404. } elseif ($this->isInt($info) || $this->isFloat($info)) {
  8405. if ($field == 'entity' && is_null($this->{$field})) {
  8406. $queryarray[$field] = ((int) $conf->entity);
  8407. } else {
  8408. // $this->{$field} may be null, '', 0, '0', 123, '123'
  8409. if ((isset($this->{$field}) && $this->{$field} != '') || !empty($info['notnull'])) {
  8410. if (!isset($this->{$field})) {
  8411. $queryarray[$field] = 0;
  8412. } elseif ($this->isInt($info)) {
  8413. $queryarray[$field] = (int) $this->{$field}; // If '0', it may be set to null later if $info['notnull'] == -1
  8414. } elseif ($this->isFloat($info)) {
  8415. $queryarray[$field] = (float) $this->{$field}; // If '0', it may be set to null later if $info['notnull'] == -1
  8416. }
  8417. } else {
  8418. $queryarray[$field] = null;
  8419. }
  8420. }
  8421. } else {
  8422. // Note: If $this->{$field} is not defined, it means there is a bug into definition of ->fields or a missing declaration of property
  8423. // We should keep the warning generated by this because it is a bug somewhere else in code, not here.
  8424. $queryarray[$field] = $this->{$field};
  8425. }
  8426. if ($info['type'] == 'timestamp' && empty($queryarray[$field])) {
  8427. unset($queryarray[$field]);
  8428. }
  8429. if (!empty($info['notnull']) && $info['notnull'] == -1 && empty($queryarray[$field])) {
  8430. $queryarray[$field] = null; // May force 0 to null
  8431. }
  8432. }
  8433. return $queryarray;
  8434. }
  8435. /**
  8436. * Function to load data from a SQL pointer into properties of current object $this
  8437. *
  8438. * @param stdClass $obj Contain data of object from database
  8439. * @return void
  8440. */
  8441. public function setVarsFromFetchObj(&$obj)
  8442. {
  8443. global $db;
  8444. foreach ($this->fields as $field => $info) {
  8445. if ($this->isDate($info)) {
  8446. if (is_null($obj->$field) || $obj->$field === '' || $obj->$field === '0000-00-00 00:00:00' || $obj->$field === '1000-01-01 00:00:00') {
  8447. $this->$field = '';
  8448. } else {
  8449. $this->$field = $db->jdate($obj->$field);
  8450. }
  8451. } elseif ($this->isInt($info)) {
  8452. if ($field == 'rowid') {
  8453. $this->id = (int) $obj->$field;
  8454. } else {
  8455. if ($this->isForcedToNullIfZero($info)) {
  8456. if (empty($obj->$field)) {
  8457. $this->$field = null;
  8458. } else {
  8459. $this->$field = (float) $obj->$field;
  8460. }
  8461. } else {
  8462. if (!is_null($obj->$field) || (isset($info['notnull']) && $info['notnull'] == 1)) {
  8463. $this->$field = (int) $obj->$field;
  8464. } else {
  8465. $this->$field = null;
  8466. }
  8467. }
  8468. }
  8469. } elseif ($this->isFloat($info)) {
  8470. if ($this->isForcedToNullIfZero($info)) {
  8471. if (empty($obj->$field)) {
  8472. $this->$field = null;
  8473. } else {
  8474. $this->$field = (float) $obj->$field;
  8475. }
  8476. } else {
  8477. if (!is_null($obj->$field) || (isset($info['notnull']) && $info['notnull'] == 1)) {
  8478. $this->$field = (float) $obj->$field;
  8479. } else {
  8480. $this->$field = null;
  8481. }
  8482. }
  8483. } else {
  8484. $this->$field = isset($obj->$field) ? $obj->$field : null;
  8485. }
  8486. }
  8487. // If there is no 'ref' field, we force property ->ref to ->id for a better compatibility with common functions.
  8488. if (!isset($this->fields['ref']) && isset($this->id)) {
  8489. $this->ref = $this->id;
  8490. }
  8491. }
  8492. /**
  8493. * Sets all object fields to null. Useful for example in lists, when printing multiple lines and a different object os fetched for each line.
  8494. * @return void
  8495. */
  8496. public function emtpyObjectVars()
  8497. {
  8498. foreach ($this->fields as $field => $arr) {
  8499. $this->$field = null;
  8500. }
  8501. }
  8502. /**
  8503. * Function to concat keys of fields
  8504. *
  8505. * @param string $alias String of alias of table for fields. For example 't'. It is recommended to use '' and set alias into fields defintion.
  8506. * @param array $excludefields Array of fields to exclude
  8507. * @return string List of alias fields
  8508. */
  8509. public function getFieldList($alias = '', $excludefields = array())
  8510. {
  8511. $keys = array_keys($this->fields);
  8512. if (!empty($alias)) {
  8513. $keys_with_alias = array();
  8514. foreach ($keys as $fieldname) {
  8515. if (!empty($excludefields)) {
  8516. if (in_array($fieldname, $excludefields)) { // The field is excluded and must not be in output
  8517. continue;
  8518. }
  8519. }
  8520. $keys_with_alias[] = $alias . '.' . $fieldname;
  8521. }
  8522. return implode(',', $keys_with_alias);
  8523. } else {
  8524. return implode(',', $keys);
  8525. }
  8526. }
  8527. /**
  8528. * Add quote to field value if necessary
  8529. *
  8530. * @param string|int $value Value to protect
  8531. * @param array $fieldsentry Properties of field
  8532. * @return string
  8533. */
  8534. protected function quote($value, $fieldsentry)
  8535. {
  8536. if (is_null($value)) {
  8537. return 'NULL';
  8538. } elseif (preg_match('/^(int|double|real|price)/i', $fieldsentry['type'])) {
  8539. return price2num("$value");
  8540. } elseif (preg_match('/int$/i', $fieldsentry['type'])) {
  8541. return (int) $value;
  8542. } elseif ($fieldsentry['type'] == 'boolean') {
  8543. if ($value) {
  8544. return 'true';
  8545. } else {
  8546. return 'false';
  8547. }
  8548. } else {
  8549. return "'".$this->db->escape($value)."'";
  8550. }
  8551. }
  8552. /**
  8553. * Create object into database
  8554. *
  8555. * @param User $user User that creates
  8556. * @param bool $notrigger false=launch triggers after, true=disable triggers
  8557. * @return int Return integer <0 if KO, Id of created object if OK
  8558. */
  8559. public function createCommon(User $user, $notrigger = false)
  8560. {
  8561. global $langs;
  8562. dol_syslog(get_class($this)."::createCommon create", LOG_DEBUG);
  8563. $error = 0;
  8564. $now = dol_now();
  8565. $fieldvalues = $this->setSaveQuery();
  8566. if (array_key_exists('date_creation', $fieldvalues) && empty($fieldvalues['date_creation'])) {
  8567. $fieldvalues['date_creation'] = $this->db->idate($now);
  8568. }
  8569. if (array_key_exists('fk_user_creat', $fieldvalues) && !($fieldvalues['fk_user_creat'] > 0)) {
  8570. $fieldvalues['fk_user_creat'] = $user->id;
  8571. }
  8572. if (array_key_exists('pass_crypted', $fieldvalues) && property_exists($this, 'pass')) {
  8573. $fieldvalues['pass_crypted'] = dol_hash($this->pass);
  8574. }
  8575. unset($fieldvalues['rowid']); // The field 'rowid' is reserved field name for autoincrement field so we don't need it into insert.
  8576. if (array_key_exists('ref', $fieldvalues)) {
  8577. $fieldvalues['ref'] = dol_string_nospecial($fieldvalues['ref']); // If field is a ref, we sanitize data
  8578. }
  8579. $keys = array();
  8580. $values = array(); // Array to store string forged for SQL syntax
  8581. foreach ($fieldvalues as $k => $v) {
  8582. $keys[$k] = $k;
  8583. $value = $this->fields[$k];
  8584. $values[$k] = $this->quote($v, $value); // May return string 'NULL' if $value is null
  8585. }
  8586. // Clean and check mandatory
  8587. foreach ($keys as $key) {
  8588. // If field is an implicit foreign key field (so type = 'integer:...')
  8589. if (preg_match('/^integer:/i', $this->fields[$key]['type']) && $values[$key] == '-1') {
  8590. $values[$key] = '';
  8591. }
  8592. if (!empty($this->fields[$key]['foreignkey']) && $values[$key] == '-1') {
  8593. $values[$key] = '';
  8594. }
  8595. if (isset($this->fields[$key]['notnull']) && $this->fields[$key]['notnull'] == 1 && (!isset($values[$key]) || $values[$key] === 'NULL') && is_null($this->fields[$key]['default'])) {
  8596. $error++;
  8597. $langs->load("errors");
  8598. dol_syslog("Mandatory field '".$key."' is empty and required into ->fields definition of class");
  8599. $this->errors[] = $langs->trans("ErrorFieldRequired", $this->fields[$key]['label']);
  8600. }
  8601. // If value is null and there is a default value for field
  8602. if (isset($this->fields[$key]['notnull']) && $this->fields[$key]['notnull'] == 1 && (!isset($values[$key]) || $values[$key] === 'NULL') && !is_null($this->fields[$key]['default'])) {
  8603. $values[$key] = $this->quote($this->fields[$key]['default'], $this->fields[$key]);
  8604. }
  8605. // If field is an implicit foreign key field (so type = 'integer:...')
  8606. if (preg_match('/^integer:/i', $this->fields[$key]['type']) && empty($values[$key])) {
  8607. if (isset($this->fields[$key]['default'])) {
  8608. $values[$key] = ((int) $this->fields[$key]['default']);
  8609. } else {
  8610. $values[$key] = 'null';
  8611. }
  8612. }
  8613. if (!empty($this->fields[$key]['foreignkey']) && empty($values[$key])) {
  8614. $values[$key] = 'null';
  8615. }
  8616. }
  8617. if ($error) {
  8618. return -1;
  8619. }
  8620. $this->db->begin();
  8621. if (!$error) {
  8622. $sql = "INSERT INTO ".$this->db->prefix().$this->table_element;
  8623. $sql .= " (".implode(", ", $keys).')';
  8624. $sql .= " VALUES (".implode(", ", $values).")"; // $values can contains 'abc' or 123
  8625. $res = $this->db->query($sql);
  8626. if (!$res) {
  8627. $error++;
  8628. if ($this->db->lasterrno() == 'DB_ERROR_RECORD_ALREADY_EXISTS') {
  8629. $this->errors[] = "ErrorRefAlreadyExists";
  8630. } else {
  8631. $this->errors[] = $this->db->lasterror();
  8632. }
  8633. }
  8634. }
  8635. if (!$error) {
  8636. $this->id = $this->db->last_insert_id($this->db->prefix().$this->table_element);
  8637. }
  8638. // If we have a field ref with a default value of (PROV)
  8639. if (!$error) {
  8640. if (key_exists('ref', $this->fields) && key_exists('notnull', $this->fields['ref']) && $this->fields['ref']['notnull'] > 0 && key_exists('default', $this->fields['ref']) && $this->fields['ref']['default'] == '(PROV)') {
  8641. $sql = "UPDATE ".$this->db->prefix().$this->table_element." SET ref = '(PROV".((int) $this->id).")' WHERE (ref = '(PROV)' OR ref = '') AND rowid = ".((int) $this->id);
  8642. $resqlupdate = $this->db->query($sql);
  8643. if ($resqlupdate === false) {
  8644. $error++;
  8645. $this->errors[] = $this->db->lasterror();
  8646. } else {
  8647. $this->ref = '(PROV'.$this->id.')';
  8648. }
  8649. }
  8650. }
  8651. // Create extrafields
  8652. if (!$error) {
  8653. $result = $this->insertExtraFields();
  8654. if ($result < 0) {
  8655. $error++;
  8656. }
  8657. }
  8658. // Create lines
  8659. if (!empty($this->table_element_line) && !empty($this->fk_element)) {
  8660. foreach ($this->lines as $line) {
  8661. $keyforparent = $this->fk_element;
  8662. $line->$keyforparent = $this->id;
  8663. // Test and convert into object this->lines[$i]. When coming from REST API, we may still have an array
  8664. //if (! is_object($line)) $line=json_decode(json_encode($line), false); // convert recursively array into object.
  8665. if (!is_object($line)) {
  8666. $line = (object) $line;
  8667. }
  8668. $result = 0;
  8669. if (method_exists($line, 'insert')) {
  8670. $result = $line->insert($user, 1);
  8671. } elseif (method_exists($line, 'create')) {
  8672. $result = $line->create($user, 1);
  8673. }
  8674. if ($result < 0) {
  8675. $this->error = $line->error;
  8676. $this->db->rollback();
  8677. return -1;
  8678. }
  8679. }
  8680. }
  8681. // Triggers
  8682. if (!$error && !$notrigger) {
  8683. // Call triggers
  8684. $result = $this->call_trigger(strtoupper(get_class($this)).'_CREATE', $user);
  8685. if ($result < 0) {
  8686. $error++;
  8687. }
  8688. // End call triggers
  8689. }
  8690. // Commit or rollback
  8691. if ($error) {
  8692. $this->db->rollback();
  8693. return -1;
  8694. } else {
  8695. $this->db->commit();
  8696. return $this->id;
  8697. }
  8698. }
  8699. /**
  8700. * Load object in memory from the database. This does not load line. This is done by parent fetch() that call fetchCommon
  8701. *
  8702. * @param int $id Id object
  8703. * @param string $ref Ref
  8704. * @param string $morewhere More SQL filters (' AND ...')
  8705. * @param int $noextrafields 0=Default to load extrafields, 1=No extrafields
  8706. * @return int Return integer <0 if KO, 0 if not found, >0 if OK
  8707. */
  8708. public function fetchCommon($id, $ref = null, $morewhere = '', $noextrafields = 0)
  8709. {
  8710. if (empty($id) && empty($ref) && empty($morewhere)) {
  8711. return -1;
  8712. }
  8713. $fieldlist = $this->getFieldList('t');
  8714. if (empty($fieldlist)) {
  8715. return 0;
  8716. }
  8717. $sql = "SELECT ".$fieldlist;
  8718. $sql .= " FROM ".$this->db->prefix().$this->table_element.' as t';
  8719. if (!empty($id)) {
  8720. $sql .= ' WHERE t.rowid = '.((int) $id);
  8721. } elseif (!empty($ref)) {
  8722. $sql .= " WHERE t.ref = '".$this->db->escape($ref)."'";
  8723. } else {
  8724. $sql .= ' WHERE 1 = 1'; // usage with empty id and empty ref is very rare
  8725. }
  8726. if (empty($id) && isset($this->ismultientitymanaged) && $this->ismultientitymanaged == 1) {
  8727. $sql .= ' AND t.entity IN ('.getEntity($this->element).')';
  8728. }
  8729. if ($morewhere) {
  8730. $sql .= $morewhere;
  8731. }
  8732. $sql .= ' LIMIT 1'; // This is a fetch, to be sure to get only one record
  8733. $res = $this->db->query($sql);
  8734. if ($res) {
  8735. $obj = $this->db->fetch_object($res);
  8736. if ($obj) {
  8737. $this->setVarsFromFetchObj($obj);
  8738. // Retrieve all extrafield
  8739. // fetch optionals attributes and labels
  8740. if (empty($noextrafields)) {
  8741. $result = $this->fetch_optionals();
  8742. if ($result < 0) {
  8743. $this->error = $this->db->lasterror();
  8744. $this->errors[] = $this->error;
  8745. return -4;
  8746. }
  8747. }
  8748. return $this->id;
  8749. } else {
  8750. return 0;
  8751. }
  8752. } else {
  8753. $this->error = $this->db->lasterror();
  8754. $this->errors[] = $this->error;
  8755. return -1;
  8756. }
  8757. }
  8758. /**
  8759. * Load object in memory from the database
  8760. *
  8761. * @param string $morewhere More SQL filters (' AND ...')
  8762. * @param int $noextrafields 0=Default to load extrafields, 1=No extrafields
  8763. * @return int Return integer <0 if KO, 0 if not found, >0 if OK
  8764. */
  8765. public function fetchLinesCommon($morewhere = '', $noextrafields = 0)
  8766. {
  8767. $objectlineclassname = get_class($this).'Line';
  8768. if (!class_exists($objectlineclassname)) {
  8769. $this->error = 'Error, class '.$objectlineclassname.' not found during call of fetchLinesCommon';
  8770. return -1;
  8771. }
  8772. $objectline = new $objectlineclassname($this->db);
  8773. $sql = "SELECT ".$objectline->getFieldList('l');
  8774. $sql .= " FROM ".$this->db->prefix().$objectline->table_element." as l";
  8775. $sql .= " WHERE l.fk_".$this->db->escape($this->element)." = ".((int) $this->id);
  8776. if ($morewhere) {
  8777. $sql .= $morewhere;
  8778. }
  8779. if (isset($objectline->fields['position'])) {
  8780. $sql .= $this->db->order('position', 'ASC');
  8781. }
  8782. $resql = $this->db->query($sql);
  8783. if ($resql) {
  8784. $num_rows = $this->db->num_rows($resql);
  8785. $i = 0;
  8786. while ($i < $num_rows) {
  8787. $obj = $this->db->fetch_object($resql);
  8788. if ($obj) {
  8789. $newline = new $objectlineclassname($this->db);
  8790. $newline->setVarsFromFetchObj($obj);
  8791. // Note: extrafields load of line not yet supported
  8792. /*
  8793. if (empty($noextrafields)) {
  8794. // Load extrafields of line
  8795. }*/
  8796. $this->lines[] = $newline;
  8797. }
  8798. $i++;
  8799. }
  8800. return 1;
  8801. } else {
  8802. $this->error = $this->db->lasterror();
  8803. $this->errors[] = $this->error;
  8804. return -1;
  8805. }
  8806. }
  8807. /**
  8808. * Update object into database
  8809. *
  8810. * @param User $user User that modifies
  8811. * @param bool $notrigger false=launch triggers after, true=disable triggers
  8812. * @return int Return integer <0 if KO, >0 if OK
  8813. */
  8814. public function updateCommon(User $user, $notrigger = false)
  8815. {
  8816. dol_syslog(get_class($this)."::updateCommon update", LOG_DEBUG);
  8817. $error = 0;
  8818. $now = dol_now();
  8819. // $this->oldcopy should have been set by the caller of update
  8820. //if (empty($this->oldcopy)) {
  8821. // $this->oldcopy = dol_clone($this);
  8822. //}
  8823. $fieldvalues = $this->setSaveQuery();
  8824. if (array_key_exists('date_modification', $fieldvalues) && empty($fieldvalues['date_modification'])) {
  8825. $fieldvalues['date_modification'] = $this->db->idate($now);
  8826. }
  8827. if (array_key_exists('fk_user_modif', $fieldvalues) && !($fieldvalues['fk_user_modif'] > 0)) {
  8828. $fieldvalues['fk_user_modif'] = $user->id;
  8829. }
  8830. unset($fieldvalues['rowid']); // The field 'rowid' is reserved field name for autoincrement field so we don't need it into update.
  8831. if (array_key_exists('ref', $fieldvalues)) {
  8832. $fieldvalues['ref'] = dol_string_nospecial($fieldvalues['ref']); // If field is a ref, we sanitize data
  8833. }
  8834. // Add quotes and escape on fields with type string
  8835. $keys = array();
  8836. $values = array();
  8837. $tmp = array();
  8838. foreach ($fieldvalues as $k => $v) {
  8839. $keys[$k] = $k;
  8840. $value = $this->fields[$k];
  8841. $values[$k] = $this->quote($v, $value);
  8842. $tmp[] = $k.'='.$this->quote($v, $this->fields[$k]);
  8843. }
  8844. // Clean and check mandatory fields
  8845. foreach ($keys as $key) {
  8846. if (preg_match('/^integer:/i', $this->fields[$key]['type']) && $values[$key] == '-1') {
  8847. $values[$key] = ''; // This is an implicit foreign key field
  8848. }
  8849. if (!empty($this->fields[$key]['foreignkey']) && $values[$key] == '-1') {
  8850. $values[$key] = ''; // This is an explicit foreign key field
  8851. }
  8852. //var_dump($key.'-'.$values[$key].'-'.($this->fields[$key]['notnull'] == 1));
  8853. /*
  8854. if ($this->fields[$key]['notnull'] == 1 && empty($values[$key]))
  8855. {
  8856. $error++;
  8857. $this->errors[]=$langs->trans("ErrorFieldRequired", $this->fields[$key]['label']);
  8858. }*/
  8859. }
  8860. $sql = 'UPDATE '.$this->db->prefix().$this->table_element.' SET '.implode(', ', $tmp).' WHERE rowid='.((int) $this->id);
  8861. $this->db->begin();
  8862. if (!$error) {
  8863. $res = $this->db->query($sql);
  8864. if (!$res) {
  8865. $error++;
  8866. $this->errors[] = $this->db->lasterror();
  8867. }
  8868. }
  8869. // Update extrafield
  8870. if (!$error) {
  8871. $result = $this->insertExtraFields(); // This delete and reinsert extrafields
  8872. if ($result < 0) {
  8873. $error++;
  8874. }
  8875. }
  8876. // Triggers
  8877. if (!$error && !$notrigger) {
  8878. // Call triggers
  8879. $result = $this->call_trigger(strtoupper(get_class($this)).'_MODIFY', $user);
  8880. if ($result < 0) {
  8881. $error++;
  8882. } //Do also here what you must do to rollback action if trigger fail
  8883. // End call triggers
  8884. }
  8885. // Commit or rollback
  8886. if ($error) {
  8887. $this->db->rollback();
  8888. return -1;
  8889. } else {
  8890. $this->db->commit();
  8891. return $this->id;
  8892. }
  8893. }
  8894. /**
  8895. * Delete object in database
  8896. *
  8897. * @param User $user User that deletes
  8898. * @param bool $notrigger false=launch triggers after, true=disable triggers
  8899. * @param int $forcechilddeletion 0=no, 1=Force deletion of children
  8900. * @return int Return integer <0 if KO, 0=Nothing done because object has child, >0 if OK
  8901. */
  8902. public function deleteCommon(User $user, $notrigger = false, $forcechilddeletion = 0)
  8903. {
  8904. dol_syslog(get_class($this)."::deleteCommon delete", LOG_DEBUG);
  8905. $error = 0;
  8906. $this->db->begin();
  8907. if ($forcechilddeletion) { // Force also delete of childtables that should lock deletion in standard case when option force is off
  8908. foreach ($this->childtables as $table) {
  8909. $sql = "DELETE FROM ".$this->db->prefix().$table." WHERE ".$this->fk_element." = ".((int) $this->id);
  8910. $resql = $this->db->query($sql);
  8911. if (!$resql) {
  8912. $this->error = $this->db->lasterror();
  8913. $this->errors[] = $this->error;
  8914. $this->db->rollback();
  8915. return -1;
  8916. }
  8917. }
  8918. } elseif (!empty($this->childtables)) { // If object has childs linked with a foreign key field, we check all child tables.
  8919. $objectisused = $this->isObjectUsed($this->id);
  8920. if (!empty($objectisused)) {
  8921. dol_syslog(get_class($this)."::deleteCommon Can't delete record as it has some child", LOG_WARNING);
  8922. $this->error = 'ErrorRecordHasChildren';
  8923. $this->errors[] = $this->error;
  8924. $this->db->rollback();
  8925. return 0;
  8926. }
  8927. }
  8928. // Delete cascade first
  8929. if (is_array($this->childtablesoncascade) && !empty($this->childtablesoncascade)) {
  8930. foreach ($this->childtablesoncascade as $table) {
  8931. $deleteFromObject = explode(':', $table);
  8932. if (count($deleteFromObject) >= 2) {
  8933. $className = str_replace('@', '', $deleteFromObject[0]);
  8934. $filePath = $deleteFromObject[1];
  8935. $columnName = $deleteFromObject[2];
  8936. $TMoreSQL = array();
  8937. if (!empty($deleteFromObject[3])) {
  8938. $TMoreSQL['customsql'] = $deleteFromObject[3];
  8939. }
  8940. if (dol_include_once($filePath)) {
  8941. $childObject = new $className($this->db);
  8942. if (method_exists($childObject, 'deleteByParentField')) {
  8943. $result = $childObject->deleteByParentField($this->id, $columnName, $TMoreSQL);
  8944. if ($result < 0) {
  8945. $error++;
  8946. $this->errors[] = $childObject->error;
  8947. break;
  8948. }
  8949. } else {
  8950. $error++;
  8951. $this->errors[] = "You defined a cascade delete on an object $childObject but there is no method deleteByParentField for it";
  8952. break;
  8953. }
  8954. } else {
  8955. $error++;
  8956. $this->errors[] = 'Cannot include child class file '.$filePath;
  8957. break;
  8958. }
  8959. } else {
  8960. // Delete record in child table
  8961. $sql = "DELETE FROM ".$this->db->prefix().$table." WHERE ".$this->fk_element." = ".((int) $this->id);
  8962. $resql = $this->db->query($sql);
  8963. if (!$resql) {
  8964. $error++;
  8965. $this->error = $this->db->lasterror();
  8966. $this->errors[] = $this->error;
  8967. break;
  8968. }
  8969. }
  8970. }
  8971. }
  8972. if (!$error) {
  8973. if (!$notrigger) {
  8974. // Call triggers
  8975. $result = $this->call_trigger(strtoupper(get_class($this)).'_DELETE', $user);
  8976. if ($result < 0) {
  8977. $error++;
  8978. } // Do also here what you must do to rollback action if trigger fail
  8979. // End call triggers
  8980. }
  8981. }
  8982. // Delete llx_ecm_files
  8983. if (!$error) {
  8984. $res = $this->deleteEcmFiles(1); // Deleting files physically is done later with the dol_delete_dir_recursive
  8985. if (!$res) {
  8986. $error++;
  8987. }
  8988. }
  8989. // Delete linked object
  8990. $res = $this->deleteObjectLinked();
  8991. if ($res < 0) {
  8992. $error++;
  8993. }
  8994. if (!$error && !empty($this->isextrafieldmanaged)) {
  8995. $result = $this->deleteExtraFields();
  8996. if ($result < 0) {
  8997. $error++;
  8998. }
  8999. }
  9000. if (!$error) {
  9001. $sql = 'DELETE FROM '.$this->db->prefix().$this->table_element.' WHERE rowid='.((int) $this->id);
  9002. $resql = $this->db->query($sql);
  9003. if (!$resql) {
  9004. $error++;
  9005. $this->errors[] = $this->db->lasterror();
  9006. }
  9007. }
  9008. // Commit or rollback
  9009. if ($error) {
  9010. $this->db->rollback();
  9011. return -1;
  9012. } else {
  9013. $this->db->commit();
  9014. return 1;
  9015. }
  9016. }
  9017. /**
  9018. * Delete all child object from a parent ID
  9019. *
  9020. * @param int $parentId Parent Id
  9021. * @param string $parentField Name of Foreign key parent column
  9022. * @param array $filter an array filter
  9023. * @param string $filtermode AND or OR
  9024. * @return int Return integer <0 if KO, >0 if OK
  9025. * @throws Exception
  9026. */
  9027. public function deleteByParentField($parentId = 0, $parentField = '', $filter = array(), $filtermode = "AND")
  9028. {
  9029. global $user;
  9030. $error = 0;
  9031. $deleted = 0;
  9032. if (!empty($parentId) && !empty($parentField)) {
  9033. $this->db->begin();
  9034. $sql = "SELECT rowid FROM ".$this->db->prefix().$this->table_element;
  9035. $sql .= " WHERE ".$parentField." = ".(int) $parentId;
  9036. // Manage filters
  9037. $sqlwhere = array();
  9038. if (count($filter) > 0) {
  9039. foreach ($filter as $key => $value) {
  9040. if ($key == 'customsql') {
  9041. $sqlwhere[] = $value;
  9042. } elseif (strpos($value, '%') === false) {
  9043. $sqlwhere[] = $key." IN (".$this->db->sanitize($this->db->escape($value)).")";
  9044. } else {
  9045. $sqlwhere[] = $key." LIKE '%".$this->db->escape($value)."%'";
  9046. }
  9047. }
  9048. }
  9049. if (count($sqlwhere) > 0) {
  9050. $sql .= " AND (".implode(" ".$filtermode." ", $sqlwhere).")";
  9051. }
  9052. $resql = $this->db->query($sql);
  9053. if (!$resql) {
  9054. $this->errors[] = $this->db->lasterror();
  9055. $error++;
  9056. } else {
  9057. while ($obj = $this->db->fetch_object($resql)) {
  9058. $result = $this->fetch($obj->rowid); // @phpstan-ignore-line
  9059. if ($result < 0) {
  9060. $error++;
  9061. $this->errors[] = $this->error;
  9062. } else {
  9063. $result = $this->delete($user); // @phpstan-ignore-line
  9064. if ($result < 0) {
  9065. $error++;
  9066. $this->errors[] = $this->error;
  9067. } else {
  9068. $deleted++;
  9069. }
  9070. }
  9071. }
  9072. }
  9073. if (empty($error)) {
  9074. $this->db->commit();
  9075. return $deleted;
  9076. } else {
  9077. $this->error = implode(', ', $this->errors);
  9078. $this->db->rollback();
  9079. return $error * -1;
  9080. }
  9081. }
  9082. return $deleted;
  9083. }
  9084. /**
  9085. * Delete a line of object in database
  9086. *
  9087. * @param User $user User that delete
  9088. * @param int $idline Id of line to delete
  9089. * @param bool $notrigger false=launch triggers after, true=disable triggers
  9090. * @return int >0 if OK, <0 if KO
  9091. */
  9092. public function deleteLineCommon(User $user, $idline, $notrigger = false)
  9093. {
  9094. global $conf;
  9095. $error = 0;
  9096. $tmpforobjectclass = get_class($this);
  9097. $tmpforobjectlineclass = ucfirst($tmpforobjectclass).'Line';
  9098. $this->db->begin();
  9099. // Call trigger
  9100. $result = $this->call_trigger('LINE'.strtoupper($tmpforobjectclass).'_DELETE', $user);
  9101. if ($result < 0) {
  9102. $error++;
  9103. }
  9104. // End call triggers
  9105. if (empty($error)) {
  9106. $sql = "DELETE FROM ".$this->db->prefix().$this->table_element_line;
  9107. $sql .= " WHERE rowid = ".((int) $idline);
  9108. $resql = $this->db->query($sql);
  9109. if (!$resql) {
  9110. $this->error = "Error ".$this->db->lasterror();
  9111. $error++;
  9112. }
  9113. }
  9114. if (empty($error)) {
  9115. // Remove extrafields
  9116. $tmpobjectline = new $tmpforobjectlineclass($this->db);
  9117. if (!isset($tmpobjectline->isextrafieldmanaged) || !empty($tmpobjectline->isextrafieldmanaged)) {
  9118. $tmpobjectline->id = $idline;
  9119. $result = $tmpobjectline->deleteExtraFields();
  9120. if ($result < 0) {
  9121. $error++;
  9122. $this->error = "Error ".get_class($this)."::deleteLineCommon deleteExtraFields error -4 ".$tmpobjectline->error;
  9123. }
  9124. }
  9125. }
  9126. if (empty($error)) {
  9127. $this->db->commit();
  9128. return 1;
  9129. } else {
  9130. dol_syslog(get_class($this)."::deleteLineCommon ERROR:".$this->error, LOG_ERR);
  9131. $this->db->rollback();
  9132. return -1;
  9133. }
  9134. }
  9135. /**
  9136. * Set to a status
  9137. *
  9138. * @param User $user Object user that modify
  9139. * @param int $status New status to set (often a constant like self::STATUS_XXX)
  9140. * @param int $notrigger 1=Does not execute triggers, 0=Execute triggers
  9141. * @param string $triggercode Trigger code to use
  9142. * @return int Return integer <0 if KO, >0 if OK
  9143. */
  9144. public function setStatusCommon($user, $status, $notrigger = 0, $triggercode = '')
  9145. {
  9146. $error = 0;
  9147. $this->db->begin();
  9148. $statusfield = 'status';
  9149. if (in_array($this->element, array('don', 'donation', 'shipping'))) {
  9150. $statusfield = 'fk_statut';
  9151. }
  9152. $sql = "UPDATE ".$this->db->prefix().$this->table_element;
  9153. $sql .= " SET ".$statusfield." = ".((int) $status);
  9154. $sql .= " WHERE rowid = ".((int) $this->id);
  9155. if ($this->db->query($sql)) {
  9156. if (!$error) {
  9157. $this->oldcopy = clone $this;
  9158. }
  9159. if (!$error && !$notrigger) {
  9160. // Call trigger
  9161. $result = $this->call_trigger($triggercode, $user);
  9162. if ($result < 0) {
  9163. $error++;
  9164. }
  9165. }
  9166. if (!$error) {
  9167. $this->status = $status;
  9168. $this->db->commit();
  9169. return 1;
  9170. } else {
  9171. $this->db->rollback();
  9172. return -1;
  9173. }
  9174. } else {
  9175. $this->error = $this->db->error();
  9176. $this->db->rollback();
  9177. return -1;
  9178. }
  9179. }
  9180. /**
  9181. * Initialise object with example values
  9182. * Id must be 0 if object instance is a specimen
  9183. *
  9184. * @return int
  9185. */
  9186. public function initAsSpecimenCommon()
  9187. {
  9188. global $user;
  9189. $this->id = 0;
  9190. $this->specimen = 1;
  9191. $fields = array(
  9192. 'label' => 'This is label',
  9193. 'ref' => 'ABCD1234',
  9194. 'description' => 'This is a description',
  9195. 'qty' => 123.12,
  9196. 'note_public' => 'Public note',
  9197. 'note_private' => 'Private note',
  9198. 'date_creation' => (dol_now() - 3600 * 48),
  9199. 'date_modification' => (dol_now() - 3600 * 24),
  9200. 'fk_user_creat' => $user->id,
  9201. 'fk_user_modif' => $user->id,
  9202. 'date' => dol_now(),
  9203. );
  9204. foreach ($fields as $key => $value) {
  9205. if (array_key_exists($key, $this->fields)) {
  9206. $this->{$key} = $value; // @phpstan-ignore-line
  9207. }
  9208. }
  9209. // Force values to default values when known
  9210. if (property_exists($this, 'fields')) {
  9211. foreach ($this->fields as $key => $value) {
  9212. // If fields are already set, do nothing
  9213. if (array_key_exists($key, $fields)) {
  9214. continue;
  9215. }
  9216. if (!empty($value['default'])) {
  9217. $this->$key = $value['default'];
  9218. }
  9219. }
  9220. }
  9221. return 1;
  9222. }
  9223. /* Part for comments */
  9224. /**
  9225. * Load comments linked with current task
  9226. * @return boolean 1 if ok
  9227. */
  9228. public function fetchComments()
  9229. {
  9230. require_once DOL_DOCUMENT_ROOT.'/core/class/comment.class.php';
  9231. $comment = new Comment($this->db);
  9232. $result = $comment->fetchAllFor($this->element, $this->id);
  9233. if ($result < 0) {
  9234. $this->errors = array_merge($this->errors, $comment->errors);
  9235. return -1;
  9236. } else {
  9237. $this->comments = $comment->comments;
  9238. }
  9239. return count($this->comments);
  9240. }
  9241. /**
  9242. * Return nb comments already posted
  9243. *
  9244. * @return int
  9245. */
  9246. public function getNbComments()
  9247. {
  9248. return count($this->comments);
  9249. }
  9250. /**
  9251. * Trim object parameters
  9252. *
  9253. * @param string[] $parameters array of parameters to trim
  9254. * @return void
  9255. */
  9256. public function trimParameters($parameters)
  9257. {
  9258. if (!is_array($parameters)) {
  9259. return;
  9260. }
  9261. foreach ($parameters as $parameter) {
  9262. if (isset($this->$parameter)) {
  9263. $this->$parameter = trim($this->$parameter);
  9264. }
  9265. }
  9266. }
  9267. /* Part for categories/tags */
  9268. /**
  9269. * Sets object to given categories.
  9270. *
  9271. * Deletes object from existing categories not supplied.
  9272. * Adds it to non existing supplied categories.
  9273. * Existing categories are left untouch.
  9274. *
  9275. * @param string $type_categ Category type ('customer', 'supplier', 'website_page', ...)
  9276. * @return int Array of category objects or < 0 if KO
  9277. */
  9278. public function getCategoriesCommon($type_categ)
  9279. {
  9280. require_once DOL_DOCUMENT_ROOT.'/categories/class/categorie.class.php';
  9281. // Get current categories
  9282. $c = new Categorie($this->db);
  9283. $existing = $c->containing($this->id, $type_categ, 'id');
  9284. return $existing;
  9285. }
  9286. /**
  9287. * Sets object to given categories.
  9288. *
  9289. * Adds it to non existing supplied categories.
  9290. * Deletes object from existing categories not supplied (if remove_existing==true).
  9291. * Existing categories are left untouch.
  9292. *
  9293. * @param int[]|int $categories Category ID or array of Categories IDs
  9294. * @param string $type_categ Category type ('customer', 'supplier', 'website_page', ...) definied into const class Categorie type
  9295. * @param boolean $remove_existing True: Remove existings categories from Object if not supplies by $categories, False: let them
  9296. * @return int Return integer <0 if KO, >0 if OK
  9297. */
  9298. public function setCategoriesCommon($categories, $type_categ = '', $remove_existing = true)
  9299. {
  9300. // Handle single category
  9301. if (!is_array($categories)) {
  9302. $categories = array($categories);
  9303. }
  9304. dol_syslog(get_class($this)."::setCategoriesCommon Oject Id:".$this->id.' type_categ:'.$type_categ.' nb tag add:'.count($categories), LOG_DEBUG);
  9305. require_once DOL_DOCUMENT_ROOT.'/categories/class/categorie.class.php';
  9306. if (empty($type_categ)) {
  9307. dol_syslog(__METHOD__.': Type '.$type_categ.'is an unknown category type. Done nothing.', LOG_ERR);
  9308. return -1;
  9309. }
  9310. // Get current categories
  9311. $c = new Categorie($this->db);
  9312. $existing = $c->containing($this->id, $type_categ, 'id');
  9313. if ($remove_existing) {
  9314. // Diff
  9315. if (is_array($existing)) {
  9316. $to_del = array_diff($existing, $categories);
  9317. $to_add = array_diff($categories, $existing);
  9318. } else {
  9319. $to_del = array(); // Nothing to delete
  9320. $to_add = $categories;
  9321. }
  9322. } else {
  9323. $to_del = array(); // Nothing to delete
  9324. $to_add = array_diff($categories, $existing);
  9325. }
  9326. $error = 0;
  9327. $ok = 0;
  9328. // Process
  9329. foreach ($to_del as $del) {
  9330. if ($c->fetch($del) > 0) {
  9331. $result=$c->del_type($this, $type_categ);
  9332. if ($result < 0) {
  9333. $error++;
  9334. $this->error = $c->error;
  9335. $this->errors = $c->errors;
  9336. break;
  9337. } else {
  9338. $ok += $result;
  9339. }
  9340. }
  9341. }
  9342. foreach ($to_add as $add) {
  9343. if ($c->fetch($add) > 0) {
  9344. $result = $c->add_type($this, $type_categ);
  9345. if ($result < 0) {
  9346. $error++;
  9347. $this->error = $c->error;
  9348. $this->errors = $c->errors;
  9349. break;
  9350. } else {
  9351. $ok += $result;
  9352. }
  9353. }
  9354. }
  9355. return $error ? (-1 * $error) : $ok;
  9356. }
  9357. /**
  9358. * Copy related categories to another object
  9359. *
  9360. * @param int $fromId Id object source
  9361. * @param int $toId Id object cible
  9362. * @param string $type Type of category ('product', ...)
  9363. * @return int Return integer < 0 if error, > 0 if ok
  9364. */
  9365. public function cloneCategories($fromId, $toId, $type = '')
  9366. {
  9367. $this->db->begin();
  9368. if (empty($type)) {
  9369. $type = $this->table_element;
  9370. }
  9371. require_once DOL_DOCUMENT_ROOT.'/categories/class/categorie.class.php';
  9372. $categorystatic = new Categorie($this->db);
  9373. $sql = "INSERT INTO ".$this->db->prefix()."categorie_".(empty($categorystatic->MAP_CAT_TABLE[$type]) ? $type : $categorystatic->MAP_CAT_TABLE[$type])." (fk_categorie, fk_product)";
  9374. $sql .= " SELECT fk_categorie, $toId FROM ".$this->db->prefix()."categorie_".(empty($categorystatic->MAP_CAT_TABLE[$type]) ? $type : $categorystatic->MAP_CAT_TABLE[$type]);
  9375. $sql .= " WHERE fk_product = ".((int) $fromId);
  9376. if (!$this->db->query($sql)) {
  9377. $this->error = $this->db->lasterror();
  9378. $this->db->rollback();
  9379. return -1;
  9380. }
  9381. $this->db->commit();
  9382. return 1;
  9383. }
  9384. /**
  9385. * Delete related files of object in database
  9386. *
  9387. * @param integer $mode 0=Use path to find record, 1=Use src_object_xxx fields (Mode 1 is recommanded for new objects)
  9388. * @return bool True if OK, False if KO
  9389. */
  9390. public function deleteEcmFiles($mode = 0)
  9391. {
  9392. global $conf;
  9393. $this->db->begin();
  9394. // Delete in database with mode 0
  9395. if ($mode == 0) {
  9396. switch ($this->element) {
  9397. case 'propal':
  9398. $element = 'propale';
  9399. break;
  9400. case 'product':
  9401. $element = 'produit';
  9402. break;
  9403. case 'order_supplier':
  9404. $element = 'fournisseur/commande';
  9405. break;
  9406. case 'invoice_supplier':
  9407. // Special cases that need to use get_exdir to get real dir of object
  9408. // In future, all object should use this to define path of documents.
  9409. $element = 'fournisseur/facture/'.get_exdir($this->id, 2, 0, 1, $this, 'invoice_supplier');
  9410. break;
  9411. case 'shipping':
  9412. $element = 'expedition/sending';
  9413. break;
  9414. case 'task':
  9415. case 'project_task':
  9416. require_once DOL_DOCUMENT_ROOT.'/projet/class/task.class.php';
  9417. $project_result = $this->fetch_projet();
  9418. if ($project_result >= 0) {
  9419. $element = 'projet/'.dol_sanitizeFileName($this->project->ref).'/';
  9420. }
  9421. // no break
  9422. default:
  9423. $element = $this->element;
  9424. }
  9425. // Delete ecm_files_extrafields with mode 0 (using name)
  9426. $sql = "DELETE FROM ".$this->db->prefix()."ecm_files_extrafields WHERE fk_object IN (";
  9427. $sql .= " SELECT rowid FROM ".$this->db->prefix()."ecm_files WHERE filename LIKE '".$this->db->escape($this->ref)."%'";
  9428. $sql .= " AND filepath = '".$this->db->escape($element)."/".$this->db->escape($this->ref)."' AND entity = ".((int) $conf->entity); // No need of getEntity here
  9429. $sql .= ")";
  9430. if (!$this->db->query($sql)) {
  9431. $this->error = $this->db->lasterror();
  9432. $this->db->rollback();
  9433. return false;
  9434. }
  9435. // Delete ecm_files with mode 0 (using name)
  9436. $sql = "DELETE FROM ".$this->db->prefix()."ecm_files";
  9437. $sql .= " WHERE filename LIKE '".$this->db->escape($this->ref)."%'";
  9438. $sql .= " AND filepath = '".$this->db->escape($element)."/".$this->db->escape($this->ref)."' AND entity = ".((int) $conf->entity); // No need of getEntity here
  9439. if (!$this->db->query($sql)) {
  9440. $this->error = $this->db->lasterror();
  9441. $this->db->rollback();
  9442. return false;
  9443. }
  9444. }
  9445. // Delete in database with mode 1
  9446. if ($mode == 1) {
  9447. $sql = 'DELETE FROM '.$this->db->prefix()."ecm_files_extrafields";
  9448. $sql .= " WHERE fk_object IN (SELECT rowid FROM ".$this->db->prefix()."ecm_files WHERE src_object_type = '".$this->db->escape($this->table_element.(empty($this->module) ? "" : "@".$this->module))."' AND src_object_id = ".((int) $this->id).")";
  9449. $resql = $this->db->query($sql);
  9450. if (!$resql) {
  9451. $this->error = $this->db->lasterror();
  9452. $this->db->rollback();
  9453. return false;
  9454. }
  9455. $sql = 'DELETE FROM '.$this->db->prefix()."ecm_files";
  9456. $sql .= " WHERE src_object_type = '".$this->db->escape($this->table_element.(empty($this->module) ? "" : "@".$this->module))."' AND src_object_id = ".((int) $this->id);
  9457. $resql = $this->db->query($sql);
  9458. if (!$resql) {
  9459. $this->error = $this->db->lasterror();
  9460. $this->db->rollback();
  9461. return false;
  9462. }
  9463. }
  9464. $this->db->commit();
  9465. return true;
  9466. }
  9467. }