api.salary.php 120 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558155915601561156215631564156515661567156815691570157115721573157415751576157715781579158015811582158315841585158615871588158915901591159215931594159515961597159815991600160116021603160416051606160716081609161016111612161316141615161616171618161916201621162216231624162516261627162816291630163116321633163416351636163716381639164016411642164316441645164616471648164916501651165216531654165516561657165816591660166116621663166416651666166716681669167016711672167316741675167616771678167916801681168216831684168516861687168816891690169116921693169416951696169716981699170017011702170317041705170617071708170917101711171217131714171517161717171817191720172117221723172417251726172717281729173017311732173317341735173617371738173917401741174217431744174517461747174817491750175117521753175417551756175717581759176017611762176317641765176617671768176917701771177217731774177517761777177817791780178117821783178417851786178717881789179017911792179317941795179617971798179918001801180218031804180518061807180818091810181118121813181418151816181718181819182018211822182318241825182618271828182918301831183218331834183518361837183818391840184118421843184418451846184718481849185018511852185318541855185618571858185918601861186218631864186518661867186818691870187118721873187418751876187718781879188018811882188318841885188618871888188918901891189218931894189518961897189818991900190119021903190419051906190719081909191019111912191319141915191619171918191919201921192219231924192519261927192819291930193119321933193419351936193719381939194019411942194319441945194619471948194919501951195219531954195519561957195819591960196119621963196419651966196719681969197019711972197319741975197619771978197919801981198219831984198519861987198819891990199119921993199419951996199719981999200020012002200320042005200620072008200920102011201220132014201520162017201820192020202120222023202420252026202720282029203020312032203320342035203620372038203920402041204220432044204520462047204820492050205120522053205420552056205720582059206020612062206320642065206620672068206920702071207220732074207520762077207820792080208120822083208420852086208720882089209020912092209320942095209620972098209921002101210221032104210521062107210821092110211121122113211421152116211721182119212021212122212321242125212621272128212921302131213221332134213521362137213821392140214121422143214421452146214721482149215021512152215321542155215621572158215921602161216221632164216521662167216821692170217121722173217421752176217721782179218021812182218321842185218621872188218921902191219221932194219521962197219821992200220122022203220422052206220722082209221022112212221322142215221622172218221922202221222222232224222522262227222822292230223122322233223422352236223722382239224022412242224322442245224622472248224922502251225222532254225522562257225822592260226122622263226422652266226722682269227022712272227322742275227622772278227922802281228222832284228522862287228822892290229122922293229422952296229722982299230023012302230323042305230623072308230923102311231223132314231523162317231823192320232123222323232423252326232723282329233023312332233323342335233623372338233923402341234223432344234523462347234823492350235123522353235423552356235723582359236023612362236323642365236623672368236923702371237223732374237523762377237823792380238123822383238423852386238723882389239023912392239323942395239623972398239924002401240224032404240524062407240824092410241124122413241424152416241724182419242024212422242324242425242624272428242924302431243224332434243524362437243824392440244124422443244424452446244724482449245024512452245324542455245624572458245924602461246224632464246524662467246824692470247124722473247424752476247724782479248024812482248324842485248624872488248924902491249224932494249524962497249824992500250125022503250425052506250725082509251025112512251325142515251625172518251925202521252225232524252525262527252825292530253125322533253425352536253725382539254025412542254325442545254625472548254925502551255225532554255525562557255825592560256125622563256425652566256725682569257025712572257325742575257625772578257925802581258225832584258525862587258825892590259125922593259425952596259725982599260026012602260326042605260626072608260926102611261226132614261526162617261826192620262126222623262426252626262726282629263026312632263326342635263626372638263926402641264226432644264526462647264826492650265126522653265426552656265726582659266026612662266326642665266626672668266926702671267226732674267526762677267826792680268126822683268426852686268726882689269026912692269326942695269626972698269927002701270227032704270527062707270827092710271127122713271427152716271727182719272027212722272327242725272627272728272927302731273227332734273527362737273827392740274127422743274427452746274727482749275027512752275327542755275627572758275927602761276227632764276527662767276827692770277127722773277427752776277727782779278027812782278327842785278627872788278927902791279227932794279527962797279827992800280128022803280428052806280728082809281028112812281328142815281628172818281928202821282228232824282528262827282828292830283128322833283428352836283728382839284028412842284328442845284628472848284928502851285228532854285528562857285828592860286128622863286428652866286728682869287028712872287328742875287628772878287928802881288228832884288528862887288828892890289128922893289428952896289728982899290029012902290329042905290629072908290929102911291229132914291529162917291829192920292129222923292429252926292729282929293029312932293329342935293629372938293929402941294229432944294529462947294829492950295129522953295429552956295729582959296029612962296329642965296629672968296929702971297229732974297529762977297829792980298129822983298429852986298729882989299029912992299329942995299629972998299930003001300230033004300530063007300830093010301130123013301430153016301730183019302030213022302330243025302630273028302930303031303230333034303530363037303830393040304130423043304430453046304730483049305030513052305330543055305630573058305930603061306230633064306530663067306830693070307130723073307430753076307730783079308030813082308330843085308630873088308930903091309230933094309530963097309830993100310131023103310431053106
  1. <?php
  2. /**
  3. * Employee salary accounting implementation
  4. */
  5. class Salary {
  6. /**
  7. * System alter.ini config stored as array key=>value
  8. *
  9. * @var array
  10. */
  11. protected $altCfg = array();
  12. /**
  13. * Available active employee as employeeid=>name
  14. *
  15. * @var array
  16. */
  17. protected $allEmployee = array();
  18. /**
  19. * Available active and inactive employee
  20. *
  21. * @var array
  22. */
  23. protected $allEmployeeRaw = array();
  24. /**
  25. * Contains available employee telegram chatid data as id=>chatid
  26. *
  27. * @var array
  28. */
  29. protected $allEmployeeTelegram = array();
  30. /**
  31. * Contains all available employee realnames as login=>name
  32. *
  33. * @var array
  34. */
  35. protected $allEmployeeLogins = array();
  36. /**
  37. * Available jobtypes as jobtypeid=>name
  38. *
  39. * @var array
  40. */
  41. protected $allJobtypes = array();
  42. /**
  43. * Typical jobtypes required time in minutes as jobtypeid=>time
  44. *
  45. * @var array
  46. */
  47. protected $allJobTimes = array();
  48. /**
  49. * Default jobtype pricing as jobtypeid=>price
  50. *
  51. * @var array
  52. */
  53. protected $allJobPrices = array();
  54. /**
  55. * Available jobtype units as jobtypeid=>unit
  56. *
  57. * @var string
  58. */
  59. protected $allJobUnits = array();
  60. /**
  61. * Available employee wages, bounty and work day length
  62. *
  63. * @var string
  64. */
  65. protected $allWages = array();
  66. /**
  67. * Available unit types as unittype=>localized name
  68. *
  69. * @var array
  70. */
  71. protected $unitTypes = array();
  72. /**
  73. * All available salary jobs as id=>jobdata
  74. *
  75. * @var array
  76. */
  77. protected $allJobs = array();
  78. /**
  79. * Alredy paid jobs as array jobid=>paid data
  80. *
  81. * @var array
  82. */
  83. protected $allPaid = array();
  84. /**
  85. * All available timesheets as array id=>timesheetdata
  86. *
  87. * @var array
  88. */
  89. protected $allTimesheets = array();
  90. /**
  91. * Timesheets dates as date=>timesheet count
  92. *
  93. * @var array
  94. */
  95. protected $allTimesheetDates = array();
  96. /**
  97. * Contains all employee appointments as employeeid=>appointment
  98. *
  99. * @var string
  100. */
  101. protected $allAppointments = array();
  102. /**
  103. * Default factor value for newly created salary jobs
  104. *
  105. * @var int
  106. */
  107. protected $defaultFactor = 0;
  108. /**
  109. * Contains previously detected tasks jobs mappings
  110. *
  111. * @var array
  112. */
  113. protected $taskJobsCache = array();
  114. /**
  115. * System caching object placeholder
  116. *
  117. * @var object
  118. */
  119. protected $cache = '';
  120. /**
  121. * System telegram object placeholder
  122. *
  123. * @var object
  124. */
  125. protected $telegram = '';
  126. /**
  127. * Telegram force notification flag
  128. *
  129. * @var bool
  130. */
  131. protected $telegramNotify = false;
  132. /**
  133. * Contains start date that large data must be loaded
  134. *
  135. * @var string
  136. */
  137. protected $dateFrom = '';
  138. /**
  139. * Contains end date that large data must be loaded to
  140. *
  141. * @var string
  142. */
  143. protected $dateTo = '';
  144. const URL_ME = '?module=salary';
  145. const URL_TS = '?module=taskman&edittask=';
  146. const URL_JOBPRICES = 'jobprices=true';
  147. const URL_WAGES = 'employeewages=true';
  148. const URL_PAYROLL = 'payroll=true';
  149. const URL_FACONTROL = 'factorcontrol=true';
  150. const URL_TWJ = 'twjreport=true';
  151. const URL_LTR = 'ltreport=true';
  152. const URL_TSHEETS = 'timesheets=true';
  153. const URL_YRREP = 'yearreport=true';
  154. const CACHE_TIMEOUT = 2592000;
  155. /**
  156. * Creates new Salary instance
  157. *
  158. * @param int $taskid Existing taskId for jobs loading
  159. *
  160. * @return void
  161. */
  162. public function __construct($taskid = '') {
  163. $this->loadAltCfg();
  164. $this->setOptions();
  165. $this->setDates();
  166. $this->catchDateOffsets();
  167. $this->setUnitTypes();
  168. $this->loadEmployeeData();
  169. $this->loadJobtypes();
  170. $this->loadJobprices();
  171. $this->loadWages();
  172. $this->loadSalaryJobs($taskid);
  173. $this->loadPaid();
  174. $this->initTelegram();
  175. $this->initCache();
  176. $this->loadTaskJobsCache();
  177. if (empty($taskid)) {
  178. $this->loadTimesheets();
  179. }
  180. }
  181. /**
  182. * Loads system alter config
  183. *
  184. * @global object $ubillingConfig
  185. *
  186. * @return void
  187. */
  188. protected function loadAltCfg() {
  189. global $ubillingConfig;
  190. $this->altCfg = $ubillingConfig->getAlter();
  191. }
  192. /**
  193. * Sets some config based options
  194. *
  195. * @return void
  196. */
  197. protected function setOptions() {
  198. if (isset($this->altCfg['SALARY_TELEGRAM']) and $this->altCfg['SALARY_TELEGRAM']) {
  199. $this->telegramNotify = true;
  200. }
  201. if (isset($this->altCfg['SALARY_FACTOR_DEFAULT']) and $this->altCfg['SALARY_FACTOR_DEFAULT']) {
  202. $this->defaultFactor = $this->altCfg['SALARY_FACTOR_DEFAULT'];
  203. }
  204. }
  205. /**
  206. * Catches some date offsets and points internal props into required values
  207. *
  208. * @return void
  209. */
  210. protected function catchDateOffsets() {
  211. //payroll dates
  212. if (ubRouting::checkPost(array('prdatefrom', 'prdateto'))) {
  213. $this->setDates(ubRouting::post('prdatefrom', 'mres'), ubRouting::post('prdateto', 'mres'));
  214. }
  215. //payroll processing
  216. if (ubRouting::checkPost('prstateprocessing')) {
  217. $curYear = curyear();
  218. $prevYear = $curYear - 1;
  219. $this->setDates($prevYear . date("-m-d"));
  220. }
  221. //timesheets for previous year too
  222. if (ubRouting::checkGet('timesheets') or ubRouting::get('module') == 'salary_timesheets') {
  223. $curYear = date("Y");
  224. $prevYear = $curYear - 1;
  225. $this->setDates($prevYear . date("-m-d"));
  226. }
  227. //tasks without jobs report
  228. if (ubRouting::checkPost(array('twfdatefrom', 'twfdateto'))) {
  229. $this->setDates(ubRouting::post('twfdatefrom', 'mres'), ubRouting::post('twfdateto', 'mres'));
  230. }
  231. //ltreport
  232. if (ubRouting::checkPost(array('datefrom', 'dateto'))) {
  233. $this->setDates(ubRouting::post('datefrom', 'mres'), ubRouting::post('dateto', 'mres'));
  234. }
  235. //payroll printing
  236. if (ubRouting::checkGet(array('df', 'dt', 'print'))) {
  237. $this->setDates(ubRouting::get('df', 'mres'), ubRouting::get('dt', 'mres'));
  238. }
  239. //year payments report
  240. if (ubRouting::checkGet(array('yearreport'))) {
  241. if (ubRouting::checkPost('showyear')) {
  242. $this->setDates(ubRouting::post('showyear', 'int') . '-01-01');
  243. } else {
  244. //begin of current year
  245. $curYear = curyear();
  246. $this->setDates($curYear . '-01-01');
  247. }
  248. }
  249. //tsheets printing
  250. if (ubRouting::checkPost(array('tsheetprintmonth', 'tsheetprintyear'))) {
  251. $this->setDates(ubRouting::post('tsheetprintyear', 'int') . '-' . ubRouting::post('tsheetprintmonth') . '-01');
  252. }
  253. }
  254. /**
  255. * Sets start and end dates if requred.
  256. * Default on begin of month and end of current month.
  257. *
  258. * @param string $dateFrom
  259. * @param string $dateTo
  260. *
  261. * @return void
  262. */
  263. protected function setDates($dateFrom = '', $dateTo = '') {
  264. $startTime = '00:00:00';
  265. $endTime = '23:56:59';
  266. if (empty($dateFrom)) {
  267. $this->dateFrom = date("Y-m") . '-01' . ' ' . $startTime;
  268. } else {
  269. $this->dateFrom = $dateFrom . ' ' . $startTime;
  270. }
  271. if (empty($dateTo)) {
  272. $this->dateTo = date("Y-m-d") . ' ' . $endTime;
  273. } else {
  274. $this->dateTo = $dateTo . ' ' . $endTime;
  275. }
  276. }
  277. /**
  278. * Loads active employees from database
  279. *
  280. * @return void
  281. */
  282. protected function loadEmployeeData() {
  283. $query = "SELECT * from `employee`";
  284. $all = simple_queryall($query);
  285. if (!empty($all)) {
  286. foreach ($all as $io => $each) {
  287. $this->allEmployeeRaw[$each['id']] = $each['name'];
  288. if ($each['active']) {
  289. $this->allEmployee[$each['id']] = $each['name'];
  290. }
  291. if (!empty($each['admlogin'])) {
  292. $this->allEmployeeLogins[$each['admlogin']] = $each['name'];
  293. }
  294. $this->allEmployeeTelegram[$each['id']] = $each['telegram'];
  295. }
  296. }
  297. }
  298. /**
  299. * Loads available jobtypes from database
  300. *
  301. * @return void
  302. */
  303. protected function loadJobtypes() {
  304. $this->allJobtypes = ts_GetAllJobtypes();
  305. }
  306. /**
  307. * Sets default unit types
  308. *
  309. * @return void
  310. */
  311. protected function setUnitTypes() {
  312. $this->unitTypes['quantity'] = __('quantity');
  313. $this->unitTypes['meter'] = __('meter');
  314. $this->unitTypes['kilometer'] = __('kilometer');
  315. $this->unitTypes['money'] = __('money');
  316. $this->unitTypes['time'] = __('time');
  317. $this->unitTypes['litre'] = __('litre');
  318. $this->unitTypes['pieces'] = __('pieces');
  319. }
  320. /**
  321. * Loads existing job prices from database
  322. *
  323. * @return void
  324. */
  325. protected function loadJobprices() {
  326. $query = "SELECT * from `salary_jobprices`";
  327. $all = simple_queryall($query);
  328. if (!empty($all)) {
  329. foreach ($all as $io => $each) {
  330. $this->allJobPrices[$each['jobtypeid']] = $each['price'];
  331. $this->allJobUnits[$each['jobtypeid']] = $each['unit'];
  332. $this->allJobTimes[$each['jobtypeid']] = $each['time'];
  333. }
  334. }
  335. }
  336. /**
  337. * Loads existing employee wages from database
  338. *
  339. * @return void
  340. */
  341. protected function loadWages() {
  342. $query = "SELECT * from `salary_wages`";
  343. $all = simple_queryall($query);
  344. if (!empty($all)) {
  345. foreach ($all as $io => $each) {
  346. $this->allWages[$each['employeeid']]['wage'] = $each['wage'];
  347. $this->allWages[$each['employeeid']]['bounty'] = $each['bounty'];
  348. $this->allWages[$each['employeeid']]['worktime'] = $each['worktime'];
  349. }
  350. }
  351. }
  352. /**
  353. * Loads paid jobs log from database into private property
  354. *
  355. * @return void
  356. */
  357. protected function loadPaid() {
  358. $where = "WHERE `date` BETWEEN '" . $this->dateFrom . "' AND '" . $this->dateTo . "'";
  359. $query = "SELECT * from `salary_paid` " . $where;
  360. $all = simple_queryall($query);
  361. if (!empty($all)) {
  362. foreach ($all as $io => $each) {
  363. $this->allPaid[$each['jobid']] = $each;
  364. }
  365. }
  366. }
  367. /**
  368. * Loads all existing timesheets from database into protected property
  369. *
  370. * @return void
  371. */
  372. protected function loadTimesheets() {
  373. $where = "WHERE `date` BETWEEN '" . $this->dateFrom . "' AND '" . $this->dateTo . "'";
  374. $query = "SELECT * from `salary_timesheets` " . $where . " ORDER BY `id` DESC";
  375. $all = simple_queryall($query);
  376. if (!empty($all)) {
  377. foreach ($all as $io => $each) {
  378. $this->allTimesheets[$each['id']] = $each;
  379. if (isset($this->allTimesheetDates[$each['date']])) {
  380. $this->allTimesheetDates[$each['date']]++;
  381. } else {
  382. $this->allTimesheetDates[$each['date']] = 1;
  383. }
  384. }
  385. }
  386. }
  387. /**
  388. * Loads all employee appointments from database
  389. *
  390. * @return void
  391. */
  392. protected function loadAppointments() {
  393. $query = "SELECT `id`,`appointment` FROM `employee`";
  394. $all = simple_queryall($query);
  395. if (!empty($all)) {
  396. foreach ($all as $io => $each) {
  397. $this->allAppointments[$each['id']] = $each['appointment'];
  398. }
  399. }
  400. }
  401. /**
  402. * Renders job price creation form
  403. *
  404. * @return string
  405. */
  406. public function jobPricesCreateForm() {
  407. $result = '';
  408. if (!empty($this->allJobtypes)) {
  409. $jobTypeParams = array();
  410. if (!empty($this->allJobtypes)) {
  411. foreach ($this->allJobtypes as $io => $each) {
  412. if (!isset($this->allJobPrices[$io])) {
  413. $jobTypeParams[$io] = $each;
  414. }
  415. }
  416. }
  417. $inputs = wf_Selector('newjobtypepriceid', $jobTypeParams, __('Job type'), '', true) . ' ';
  418. $inputs .= wf_Selector('newjobtypepriceunit', $this->unitTypes, __('Units'), '', true) . ' ';
  419. $inputs .= wf_TextInput('newjobtypeprice', __('Price'), '', true, 5, 'float') . ' ';
  420. $inputs .= wf_TextInput('newjobtypepricetime', __('Typical execution time') . ' (' . __('minutes') . ')', '', true, 5, 'float') . ' ';
  421. $inputs .= wf_Submit(__('Create'));
  422. $result = wf_Form('', 'POST', $inputs, 'glamour');
  423. $result .= wf_CleanDiv();
  424. }
  425. return ($result);
  426. }
  427. /**
  428. * Inits telegram object as protected instance for further usage
  429. *
  430. * @return void
  431. */
  432. protected function initTelegram() {
  433. if ($this->altCfg['SENDDOG_ENABLED']) {
  434. $this->telegram = new UbillingTelegram();
  435. }
  436. }
  437. /**
  438. * Inits system cache for further usage
  439. *
  440. * @return void
  441. */
  442. protected function initCache() {
  443. $this->cache = new UbillingCache();
  444. }
  445. /**
  446. * Loads tasks=>jobs cache
  447. *
  448. * @return void
  449. */
  450. protected function loadTaskJobsCache() {
  451. $this->taskJobsCache = $this->cache->get('TASKSJOBS', self::CACHE_TIMEOUT);
  452. if (empty($this->taskJobsCache)) {
  453. $this->taskJobsCache = array();
  454. }
  455. }
  456. /**
  457. * Renders job price editing form
  458. *
  459. * @param int $jobtypeid
  460. *
  461. * @return string
  462. */
  463. protected function jobPricesEditForm($jobtypeid) {
  464. $result = '';
  465. if (isset($this->allJobPrices[$jobtypeid])) {
  466. $inputs = wf_HiddenInput('editjobtypepriceid', $jobtypeid);
  467. $inputs .= wf_Selector('editjobtypepriceunit', $this->unitTypes, __('Units'), $this->allJobUnits[$jobtypeid], true);
  468. $inputs .= wf_TextInput('editjobtypeprice', __('Price'), $this->allJobPrices[$jobtypeid], true, 5, 'float');
  469. $inputs .= wf_TextInput('editjobtypepricetime', __('Minutes'), $this->allJobTimes[$jobtypeid], true, 5, 'float') . ' ';
  470. $inputs .= wf_Submit(__('Save'));
  471. $result = wf_Form('', 'POST', $inputs, 'glamour');
  472. }
  473. return ($result);
  474. }
  475. /**
  476. * Creates job type pricing database record
  477. *
  478. * @param int $jobtypeid
  479. * @param float $price
  480. * @param string $unit
  481. * @param int $time
  482. *
  483. * @return void
  484. */
  485. public function jobPriceCreate($jobtypeid, $price, $unit, $time) {
  486. $jobtypeid = vf($jobtypeid, 3);
  487. $price = str_replace(',', '.', $price);
  488. $time = vf($time);
  489. $time = str_replace(',', '.', $time);
  490. if (!isset($this->allJobPrices[$jobtypeid])) {
  491. $priceF = mysql_real_escape_string($price);
  492. $unit = mysql_real_escape_string($unit);
  493. $query = "INSERT INTO `salary_jobprices` (`id`, `jobtypeid`, `price`, `unit`,`time`) VALUES (NULL ,'" . $jobtypeid . "', '" . $priceF . "', '" . $unit . "', '" . $time . "');";
  494. nr_query($query);
  495. log_register('SALARY CREATE JOBPRICE JOBID [' . $jobtypeid . '] PRICE `' . $price . '` TIME `' . $time . '`');
  496. } else {
  497. log_register('SALARY CREATE JOBPRICE FAIL EXIST JOBID [' . $jobtypeid . ']');
  498. }
  499. }
  500. /**
  501. * Stores Telegram message for some employee
  502. *
  503. * @param int $employeeid
  504. * @param string $message
  505. *
  506. * @return void
  507. */
  508. protected function sendTelegram($employeeId, $message) {
  509. if ($this->altCfg['SENDDOG_ENABLED']) {
  510. $chatId = @$this->allEmployeeTelegram[$employeeId];
  511. if (!empty($chatId)) {
  512. $this->telegram->sendMessage($chatId, $message, false, 'SALARY');
  513. }
  514. }
  515. }
  516. /**
  517. * Sends some notificaton about salary job creation to employee
  518. *
  519. * @param int $JobId
  520. * @param int $taskid
  521. * @param int $employeeid
  522. * @param int $jobtypeid
  523. * @param float $factor
  524. * @param float $overprice
  525. * @param string $notes
  526. *
  527. * @return void
  528. */
  529. protected function salaryCreationNotify($jobId, $taskid, $employeeid, $jobtypeid, $factor, $overprice, $notes) {
  530. if ($this->telegramNotify) {
  531. $taskData = ts_GetTaskData($taskid);
  532. $message = '';
  533. $jobName = @$this->allJobtypes[$jobtypeid];
  534. $jobPrice = 0;
  535. if (empty($overprice)) {
  536. if (isset($this->allJobPrices[$jobtypeid])) {
  537. $jobPrice = $this->allJobPrices[$jobtypeid] * $factor;
  538. }
  539. } else {
  540. $jobPrice = $overprice . ' (' . __('Price override') . ')';
  541. }
  542. $unitType = @$this->allJobUnits[$jobtypeid];
  543. // manually calculate jobtyme instead of jus using getJobTime
  544. // because allJobs property not updated yet at this moment
  545. $jobTime = 0;
  546. if (isset($this->allJobTimes[$jobtypeid])) {
  547. $jobTime = $this->allJobTimes[$jobtypeid] * $factor;
  548. }
  549. $message .= '🔥 ' . __('Job added on') . ' ' . @$taskData['address'] . '\r\n ';
  550. $message .= __('Job type') . ': ' . $jobName . '\r\n ';
  551. $message .= __('Factor') . ': ' . $factor . ' / ' . __($unitType) . '\r\n ';
  552. $message .= __('Job price') . ': ' . $jobPrice . '\r\n ';
  553. $message .= __('Labor time') . ': ' . $jobTime . ' ' . __('minutes') . '\r\n ';
  554. $this->sendTelegram($employeeid, $message);
  555. }
  556. }
  557. /**
  558. * Sends notification for jobs created by current day
  559. *
  560. * @return void
  561. */
  562. public function telegramDailyNotify() {
  563. if (!empty($this->allJobs)) {
  564. if ($this->altCfg['SENDDOG_ENABLED']) {
  565. $curdate = curdate();
  566. $sendTmp = array(); //employeeid => text aggregated
  567. $employeeSumm = array(); //employeeid => summ
  568. $employeeTime = array(); //employeeid=>time in minutes
  569. foreach ($this->allJobs as $io => $eachJob) {
  570. if (ispos($eachJob['date'], $curdate)) {
  571. $employeeId = $eachJob['employeeid'];
  572. $chatId = @$this->allEmployeeTelegram[$employeeId];
  573. $factor = $eachJob['factor'];
  574. $jobtypeid = $eachJob['jobtypeid'];
  575. $overprice = $eachJob['overprice'];
  576. $priceOverrided = false;
  577. if (!empty($chatId)) {
  578. if (!isset($sendTmp[$employeeId])) {
  579. $sendTmp[$employeeId] = '';
  580. }
  581. $message = '';
  582. $taskid = $eachJob['taskid'];
  583. $taskData = ts_GetTaskData($taskid);
  584. $jobName = @$this->allJobtypes[$jobtypeid];
  585. $jobPrice = 0;
  586. if (empty($overprice)) {
  587. if (isset($this->allJobPrices[$jobtypeid])) {
  588. $jobPrice = $this->allJobPrices[$jobtypeid] * $factor;
  589. }
  590. } else {
  591. $priceOverrided = true;
  592. $jobPrice = $overprice;
  593. }
  594. $jobTime = $this->getJobTime($eachJob['id']);
  595. //per day summary
  596. if (isset($employeeSumm[$employeeId])) {
  597. $employeeSumm[$employeeId] += $jobPrice;
  598. } else {
  599. $employeeSumm[$employeeId] = $jobPrice;
  600. }
  601. if (isset($employeeTime[$employeeId])) {
  602. $employeeTime[$employeeId] += $jobTime;
  603. } else {
  604. $employeeTime[$employeeId] = $jobTime;
  605. }
  606. $unitType = @$this->allJobUnits[$jobtypeid];
  607. $overLabel = ($priceOverrided) ? ' (' . __('Price override') . ')' : '';
  608. $message .= __('Job added on') . ' ' . @$taskData['address'] . '\r\n ';
  609. $message .= __('Job type') . ': ' . $jobName . '\r\n ';
  610. $message .= __('Factor') . ': ' . $factor . ' / ' . __($unitType) . '\r\n ';
  611. $message .= __('Spent time') . ': ' . $jobTime . ' ' . __('minutes') . '\r\n ';
  612. $message .= __('Job price') . ': ' . $jobPrice . $overLabel . '\r\n ';
  613. $message .= '💵💵💵' . '\r\n '; // vsrate emoji
  614. $message .= '' . '\r\n ';
  615. $sendTmp[$employeeId] .= $message;
  616. }
  617. }
  618. }
  619. //appending daily time to each employee message
  620. if (!empty($employeeTime)) {
  621. foreach ($employeeTime as $io => $eachDayTime) {
  622. $sendTmp[$io] .= '⏱️ '; //another vsrate emoji
  623. $sendTmp[$io] .= __('Labor time') . ': ' . zb_formatTime(($eachDayTime * 60)) . '\r\n ';
  624. }
  625. }
  626. //appending daily summ to each employee message
  627. if (!empty($employeeSumm)) {
  628. foreach ($employeeSumm as $io => $eachDaySumm) {
  629. if (isset($sendTmp[$io])) {
  630. $sendTmp[$io] .= '💰 '; //very vsrate emoji
  631. $sendTmp[$io] .= __('Total money') . ': ' . $eachDaySumm . '\r\n ';
  632. }
  633. }
  634. }
  635. //sending prepared messages for all employee with jobs today
  636. if (!empty($sendTmp)) {
  637. foreach ($sendTmp as $io => $eachMessage) {
  638. $this->sendTelegram($io, $eachMessage);
  639. }
  640. }
  641. }
  642. }
  643. }
  644. /**
  645. * Renders job prices list with required controls
  646. *
  647. * @return string
  648. */
  649. public function jobPricesRender() {
  650. $result = '';
  651. $messages = new UbillingMessageHelper();
  652. $cells = wf_TableCell(__('Job type'));
  653. $cells .= wf_TableCell(__('Units'));
  654. $cells .= wf_TableCell(__('Price'));
  655. $cells .= wf_TableCell(__('Minutes'));
  656. $cells .= wf_TableCell(__('Actions'));
  657. $rows = wf_TableRow($cells, 'row1');
  658. if (!empty($this->allJobPrices)) {
  659. foreach ($this->allJobPrices as $jobtypeid => $eachprice) {
  660. $cells = wf_TableCell(@$this->allJobtypes[$jobtypeid]);
  661. $cells .= wf_TableCell(__($this->allJobUnits[$jobtypeid]));
  662. $cells .= wf_TableCell($eachprice);
  663. $cells .= wf_TableCell($this->allJobTimes[$jobtypeid]);
  664. $actLinks = wf_JSAlert('?module=salary&deletejobprice=' . $jobtypeid, web_delete_icon(), $messages->getDeleteAlert());
  665. $actLinks .= wf_modalAuto(web_edit_icon(), __('Edit'), $this->jobPricesEditForm($jobtypeid));
  666. $cells .= wf_TableCell($actLinks);
  667. $rows .= wf_TableRow($cells, 'row5');
  668. }
  669. }
  670. $result = wf_TableBody($rows, '100%', 0, 'sortable');
  671. return ($result);
  672. }
  673. /**
  674. * Deletes jobprice by jobtype id from database
  675. *
  676. * @param int $jobtypeid
  677. *
  678. * @return void
  679. */
  680. public function jobPriceDelete($jobtypeid) {
  681. $jobtypeid = vf($jobtypeid, 3);
  682. $query = "DELETE from `salary_jobprices` WHERE `jobtypeid`='" . $jobtypeid . "';";
  683. nr_query($query);
  684. log_register('SALARY DELETE JOBPRICE JOBID [' . $jobtypeid . ']');
  685. }
  686. /**
  687. * Edits existing job price in database
  688. *
  689. * @param int $jobtypeid
  690. *
  691. * @return void
  692. */
  693. public function jobPriceEdit($jobtypeid) {
  694. $jobtypeid = vf($jobtypeid, 3);
  695. $price = str_replace(',', '.', $_POST['editjobtypeprice']);
  696. $time = ubRouting::filters($_POST['editjobtypepricetime'], 'mres');
  697. $time = str_replace(',', '.', $time);
  698. $where = " WHERE `jobtypeid`='" . $jobtypeid . "';";
  699. simple_update_field('salary_jobprices', 'price', $price, $where);
  700. simple_update_field('salary_jobprices', 'unit', $_POST['editjobtypepriceunit'], $where);
  701. simple_update_field('salary_jobprices', 'time', $time, $where);
  702. log_register('SALARY EDIT JOBPRICE JOBID [' . $jobtypeid . '] PRICE `' . $_POST['editjobtypeprice'] . '` UNIT `' . $_POST['editjobtypepriceunit'] . '` TIME `' . $time . '`');
  703. }
  704. /**
  705. * Renders controls panel
  706. *
  707. * @return string
  708. */
  709. public function renderControls() {
  710. $result = '';
  711. $result .= wf_Link(self::URL_ME . '&' . self::URL_PAYROLL, wf_img('skins/ukv/dollar.png') . ' ' . __('Payroll'), false, 'ubButton');
  712. $result .= wf_Link(self::URL_ME . '&' . self::URL_TSHEETS, wf_img('skins/icon_calendar.gif') . ' ' . __('Timesheet'), false, 'ubButton');
  713. $directoriesControls = wf_Link(self::URL_ME . '&' . self::URL_JOBPRICES, wf_img('skins/shovel.png') . ' ' . __('Job types'), false, 'ubButton');
  714. $directoriesControls .= wf_Link(self::URL_ME . '&' . self::URL_WAGES, wf_img('skins/icon_user.gif') . ' ' . __('Employee wages'), false, 'ubButton');
  715. $result .= wf_modalAuto(web_icon_extended() . ' ' . __('Directories'), __('Directories'), $directoriesControls, 'ubButton');
  716. $reports = wf_Link(self::URL_ME . '&' . self::URL_FACONTROL, wf_img('skins/factorcontrol.png') . ' ' . __('Factor control'), false, 'ubButton');
  717. $reports .= wf_Link(self::URL_ME . '&' . self::URL_TWJ, wf_img('skins/question.png') . ' ' . __('Tasks without jobs'), false, 'ubButton');
  718. $reports .= wf_Link(self::URL_ME . '&' . self::URL_LTR, wf_img('skins/clock.png') . ' ' . __('Labor time'), false, 'ubButton');
  719. $reports .= wf_Link(self::URL_ME . '&' . self::URL_YRREP, wf_img('skins/icon_table.png') . ' ' . __('Year salary reports'), false, 'ubButton');
  720. $result .= wf_modalAuto(wf_img('skins/ukv/report.png') . ' ' . __('Reports'), __('Reports'), $reports, 'ubButton');
  721. return ($result);
  722. }
  723. /**
  724. * Returns job for task creation form
  725. *
  726. * @param int $taskid
  727. * @return string
  728. */
  729. public function taskJobCreateForm($taskid) {
  730. $taskid = vf($taskid, 3);
  731. $result = '';
  732. $jobtypes = array();
  733. $employeeTmp = array();
  734. $noPriceHideFlag = (@$this->altCfg['SALARY_HIDE_NOPRICE']) ? true : false;
  735. if (cfr('SALARYTASKSVIEW')) {
  736. $result .= $this->renderTaskJobs($taskid);
  737. }
  738. if (cfr('SALARYTASKS')) {
  739. if (!empty($this->allJobPrices)) {
  740. if (!empty($this->allJobtypes)) {
  741. foreach ($this->allJobtypes as $io => $each) {
  742. if (isset($this->allJobUnits[$io])) {
  743. $jobUnit = __($this->allJobUnits[$io]);
  744. $jobtypes[$io] = $each . ' (' . $jobUnit . ')';
  745. } else {
  746. if (!$noPriceHideFlag) {
  747. $jobUnit = '?';
  748. $jobtypes[$io] = $each . ' (' . $jobUnit . ')';
  749. }
  750. }
  751. }
  752. }
  753. if (!empty($this->allEmployee)) {
  754. foreach ($this->allEmployee as $empid => $empname) {
  755. if ($this->checkEmployeeWage($empid)) {
  756. $employeeTmp[$empid] = $empname;
  757. }
  758. }
  759. }
  760. if (@$this->altCfg['SALARY_EMPLOYEE_PRESET']) {
  761. $taskData = ts_GetTaskData($taskid);
  762. $taskEmployeeId = @$taskData['employee'];
  763. if (isset($employeeTmp[$taskEmployeeId])) {
  764. $defaultEmployeeSelected = $taskEmployeeId;
  765. } else {
  766. $defaultEmployeeSelected = '';
  767. }
  768. } else {
  769. $defaultEmployeeSelected = '';
  770. }
  771. if (@$this->altCfg['SALARY_JOBTYPE_PRESET']) {
  772. if (empty($taskData)) {
  773. $taskData = ts_GetTaskData($taskid);
  774. }
  775. $taskJobtypeId = @$taskData['jobtype'];
  776. if (isset($jobtypes[$taskJobtypeId])) {
  777. $defaultJobtypeSelected = $taskJobtypeId;
  778. } else {
  779. $defaultJobtypeSelected = '';
  780. }
  781. } else {
  782. $defaultJobtypeSelected = '';
  783. }
  784. $inputs = zb_JSHider();
  785. $inputs .= wf_HiddenInput('newsalarytaskid', $taskid);
  786. $inputs .= wf_Selector('newsalaryemployeeid', $employeeTmp, __('Worker'), $defaultEmployeeSelected, true);
  787. $inputs .= wf_Selector('newsalaryjobtypeid', $jobtypes, __('Job type'), $defaultJobtypeSelected, true);
  788. $inputs .= wf_TextInput('newsalaryfactor', __('Factor'), $this->defaultFactor, true, 4);
  789. $inputs .= wf_tag('input', false, '', 'type="checkbox" id="overpricebox" name="overpricebox" onclick="showhide(\'overpricecontainer\');" ');
  790. $inputs .= wf_tag('label', false, '', 'for="overpricebox"') . __('Price override') . wf_tag('label', true);
  791. $inputs .= wf_tag('span', false, '', 'id="overpricecontainer" style="display:none;"') . ' ';
  792. $inputs .= wf_TextInput('newsalaryoverprice', '', '', false, 4);
  793. $inputs .= wf_tag('span', true);
  794. $inputs .= wf_tag('br');
  795. $inputs .= wf_TextInput('newsalarynotes', __('Notes'), '', true, 25);
  796. $inputs .= wf_Submit(__('Save'));
  797. $result .= wf_modalAuto(wf_img('skins/icon_ok.gif') . ' ' . __('Create new job'), __('Create new job'), wf_Form('', 'POST', $inputs, 'glamour'), 'ubButton');
  798. $result .= wf_CleanDiv();
  799. }
  800. }
  801. return ($result);
  802. }
  803. /**
  804. * Loads all available salary jobs from database
  805. *
  806. * @param int $taskid existing task ID for jobs loading
  807. *
  808. * @return void
  809. */
  810. protected function loadSalaryJobs($taskid = '') {
  811. $taskid = vf($taskid, 3);
  812. $where = '';
  813. if (!empty($taskid)) {
  814. $where = "WHERE `taskid`='" . $taskid . "'";
  815. } else {
  816. $where = "WHERE `date` BETWEEN '" . $this->dateFrom . "' AND '" . $this->dateTo . "'";
  817. }
  818. $query = "SELECT * from `salary_jobs` " . $where . " ORDER BY `id` ASC";
  819. $all = simple_queryall($query);
  820. if (!empty($all)) {
  821. foreach ($all as $io => $each) {
  822. $this->allJobs[$each['id']] = $each;
  823. }
  824. }
  825. }
  826. /**
  827. * Creates new salary job for some task
  828. *
  829. * @param int $taskid
  830. * @param int $employeeid
  831. * @param int $jobtypeid
  832. * @param float $factor
  833. * @param float $overprice
  834. * @param string $notes
  835. *
  836. * @return void
  837. */
  838. public function createSalaryJob($taskid, $employeeid, $jobtypeid, $factor, $overprice, $notes) {
  839. $taskid = vf($taskid, 3);
  840. $employeeid = vf($employeeid, 3);
  841. $jobtypeid = vf($jobtypeid, 3);
  842. $factor = str_replace(',', '.', $factor);
  843. $overprice = str_replace(',', '.', $overprice);
  844. $notes = mysql_real_escape_string($notes);
  845. $overprice = mysql_real_escape_string($overprice);
  846. $date = curdatetime();
  847. $state = 0;
  848. $query = "INSERT INTO `salary_jobs` (`id`, `date`, `state` ,`taskid`, `employeeid`, `jobtypeid`, `factor`, `overprice`, `note`)"
  849. . " VALUES (NULL, '" . $date . "', '" . $state . "' ,'" . $taskid . "', '" . $employeeid . "', '" . $jobtypeid . "', '" . $factor . "', '" . $overprice . "', '" . $notes . "');";
  850. nr_query($query);
  851. $newId = simple_get_lastid('salary_jobs');
  852. log_register('SALARY CREATE JOB [' . $newId . '] TASK [' . $taskid . '] EMPLOYEE [' . $employeeid . '] JOBTYPE [' . $jobtypeid . '] FACTOR `' . $factor . '` OVERPRICE `' . $overprice . '`');
  853. //some telegram notification
  854. $this->salaryCreationNotify($newId, $taskid, $employeeid, $jobtypeid, $factor, $overprice, $notes);
  855. }
  856. /**
  857. * Filters available jobs for some task
  858. *
  859. * @param int $taskid
  860. * @return array
  861. */
  862. protected function filterTaskJobs($taskid) {
  863. $taskid = vf($taskid, 3);
  864. $result = array();
  865. if (!empty($this->allJobs)) {
  866. foreach ($this->allJobs as $io => $each) {
  867. if ($each['taskid'] == $taskid) {
  868. $result[$each['id']] = $each;
  869. }
  870. }
  871. }
  872. return ($result);
  873. }
  874. /**
  875. * Filters available jobs for some task
  876. *
  877. * @param int $taskid
  878. * @return array
  879. */
  880. protected function filterTaskJobsCached($taskid) {
  881. $taskid = vf($taskid, 3);
  882. $result = array();
  883. if (!empty($this->allJobs)) {
  884. if (!isset($this->taskJobsCache[$taskid])) {
  885. foreach ($this->allJobs as $io => $each) {
  886. if ($each['taskid'] == $taskid) {
  887. $result[$each['id']] = $each;
  888. }
  889. }
  890. $this->taskJobsCache[$taskid] = $result;
  891. $this->cache->set('TASKSJOBS', $this->taskJobsCache, self::CACHE_TIMEOUT);
  892. } else {
  893. $result = $this->taskJobsCache[$taskid];
  894. }
  895. }
  896. return ($result);
  897. }
  898. /**
  899. * Checks is employee active for timesheets and salary accounting or not
  900. *
  901. * @param int $employeeId
  902. *
  903. * @return bool
  904. */
  905. protected function checkEmployeeWage($employeeId) {
  906. if (isset($this->allWages[$employeeId])) {
  907. $result = true;
  908. } else {
  909. $result = false;
  910. }
  911. return ($result);
  912. }
  913. /**
  914. * Filters available jobs by date
  915. *
  916. * @param string $date
  917. * @return array
  918. */
  919. protected function jobsFilterDate($date) {
  920. $result = array();
  921. if (!empty($this->allJobs)) {
  922. foreach ($this->allJobs as $io => $each) {
  923. if (ispos($each['date'], $date)) {
  924. $result[$each['id']] = $each;
  925. }
  926. }
  927. }
  928. return ($result);
  929. }
  930. /**
  931. * Returns job salary by its factor/overprice
  932. *
  933. * @param int $jobid
  934. * @return float
  935. */
  936. protected function getJobPrice($jobid) {
  937. $jobid = vf($jobid, 3);
  938. $result = 0;
  939. if (isset($this->allJobs[$jobid])) {
  940. if (empty($this->allJobs[$jobid]['overprice'])) {
  941. if (isset($this->allJobPrices[$this->allJobs[$jobid]['jobtypeid']])) {
  942. $result = $this->allJobPrices[$this->allJobs[$jobid]['jobtypeid']] * $this->allJobs[$jobid]['factor'];
  943. }
  944. } else {
  945. $result = $this->allJobs[$jobid]['overprice'];
  946. }
  947. }
  948. return ($result);
  949. }
  950. /**
  951. * Returns all available job labor times in minutes as jobtypeId=>time
  952. *
  953. * @return array
  954. */
  955. public function getAllJobTimes() {
  956. return ($this->allJobTimes);
  957. }
  958. /**
  959. * Returns time in minutes spent to perform some job
  960. *
  961. * @param int $jobid
  962. *
  963. * @return int
  964. */
  965. protected function getJobTime($jobid) {
  966. $result = 0;
  967. if (isset($this->allJobs[$jobid])) {
  968. $jobTypeId = $this->allJobs[$jobid]['jobtypeid'];
  969. if (isset($this->allJobTimes[$jobTypeId])) {
  970. $result = $this->allJobTimes[$jobTypeId] * $this->allJobs[$jobid]['factor'];
  971. }
  972. }
  973. return ($result);
  974. }
  975. /**
  976. * Returns existing job editing form
  977. *
  978. * @param int $jobid
  979. * @return string
  980. */
  981. protected function jobEditForm($jobid) {
  982. $jobid = vf($jobid, 3);
  983. $result = '';
  984. if (isset($this->allJobs[$jobid])) {
  985. $inputs = wf_HiddenInput('editsalaryjobid', $jobid);
  986. $inputs .= wf_Selector('editsalaryemployeeid', $this->allEmployee, __('Worker'), $this->allJobs[$jobid]['employeeid'], true);
  987. $inputs .= wf_Selector('editsalaryjobtypeid', $this->allJobtypes, __('Job type'), $this->allJobs[$jobid]['jobtypeid'], true);
  988. $inputs .= wf_TextInput('editsalaryfactor', __('Factor'), $this->allJobs[$jobid]['factor'], true, 4);
  989. $inputs .= wf_TextInput('editsalaryoverprice', __('Price override'), $this->allJobs[$jobid]['overprice'], true, 4);
  990. $inputs .= wf_TextInput('editsalarynotes', __('Notes'), $this->allJobs[$jobid]['note'], true, 25);
  991. $inputs .= wf_Submit(__('Save'));
  992. $result = wf_Form('', 'POST', $inputs, 'glamour');
  993. } else {
  994. $result = __('Strange exeption') . ': NOT_EXISTING_JOBID';
  995. }
  996. return ($result);
  997. }
  998. /**
  999. * Edits some existing job in database
  1000. *
  1001. * @param int $jobid
  1002. * @param int $employeeid
  1003. * @param int $jobtypeid
  1004. * @param float $factor
  1005. * @param float $overprice
  1006. * @param string $notes
  1007. *
  1008. * @return void
  1009. */
  1010. public function jobEdit($jobid, $employeeid, $jobtypeid, $factor, $overprice, $notes) {
  1011. $jobid = vf($jobid, 3);
  1012. $factor = str_replace(',', '.', $factor);
  1013. $overprice = str_replace(',', '.', $overprice);
  1014. if (isset($this->allJobs[$jobid])) {
  1015. $where = " WHERE `id`='" . $jobid . "';";
  1016. simple_update_field('salary_jobs', 'employeeid', $employeeid, $where);
  1017. simple_update_field('salary_jobs', 'jobtypeid', $jobtypeid, $where);
  1018. simple_update_field('salary_jobs', 'factor', $factor, $where);
  1019. simple_update_field('salary_jobs', 'overprice', $overprice, $where);
  1020. simple_update_field('salary_jobs', 'note', $notes, $where);
  1021. log_register('SALARY EDIT JOB [' . $jobid . '] EMPLOYEE [' . $employeeid . '] JOBTYPE [' . $jobtypeid . '] FACTOR `' . $factor . '` OVERPRICE `' . $overprice . '`');
  1022. }
  1023. }
  1024. /**
  1025. * Renders jobs list for some task
  1026. *
  1027. * @param int $taskid
  1028. * @return string
  1029. */
  1030. protected function renderTaskJobs($taskid) {
  1031. $taskid = vf($taskid, 3);
  1032. $result = '';
  1033. $totalSumm = 0;
  1034. $totalTime = 0;
  1035. $messages = new UbillingMessageHelper();
  1036. $all = $this->filterTaskJobs($taskid);
  1037. $cells = wf_TableCell(__('Date'));
  1038. $cells .= wf_TableCell(__('Paid'));
  1039. $cells .= wf_TableCell(__('Worker'));
  1040. $cells .= wf_TableCell(__('Job type'));
  1041. $cells .= wf_TableCell(__('Factor'));
  1042. $cells .= wf_TableCell(__('Price override'));
  1043. $cells .= wf_TableCell(__('Notes'));
  1044. $cells .= wf_TableCell(__('Cash'));
  1045. $cells .= wf_TableCell(__('Time') . ' (' . __('minutes') . ')');
  1046. $cells .= wf_TableCell(__('Actions'));
  1047. $rows = wf_TableRow($cells, 'row1');
  1048. if (!empty($all)) {
  1049. foreach ($all as $io => $each) {
  1050. $factor = $each['factor'];
  1051. if (isset($this->allJobUnits[$each['jobtypeid']])) {
  1052. $unit = $this->unitTypes[$this->allJobUnits[$each['jobtypeid']]];
  1053. } else {
  1054. $unit = __('No');
  1055. }
  1056. $cells = wf_TableCell($each['date']);
  1057. $cells .= wf_TableCell($this->renderPaidDataLed($each['id']));
  1058. $cells .= wf_TableCell(@$this->allEmployeeRaw[$each['employeeid']]);
  1059. $cells .= wf_TableCell(@$this->allJobtypes[$each['jobtypeid']]);
  1060. $cells .= wf_TableCell($factor . ' / ' . $unit);
  1061. $cells .= wf_TableCell($each['overprice']);
  1062. $cells .= wf_TableCell($each['note']);
  1063. $jobPrice = $this->getJobPrice($each['id']);
  1064. $cells .= wf_TableCell($jobPrice);
  1065. $jobTime = $this->getJobTime($each['id']);
  1066. $totalTime += $jobTime;
  1067. $cells .= wf_TableCell($jobTime);
  1068. if (cfr('SALARYTASKS')) {
  1069. $actLinks = wf_JSAlert(self::URL_TS . $taskid . '&deletejobid=' . $each['id'], web_delete_icon(), $messages->getDeleteAlert());
  1070. $actLinks .= wf_modalAuto(web_edit_icon(), __('Edit'), $this->jobEditForm($each['id']));
  1071. } else {
  1072. $actLinks = '';
  1073. }
  1074. $cells .= wf_TableCell($actLinks);
  1075. $rows .= wf_TableRow($cells, 'row3');
  1076. $totalSumm = $totalSumm + $jobPrice;
  1077. }
  1078. $cells = wf_TableCell(__('Total'));
  1079. $cells .= wf_TableCell('');
  1080. $cells .= wf_TableCell('');
  1081. $cells .= wf_TableCell('');
  1082. $cells .= wf_TableCell('');
  1083. $cells .= wf_TableCell('');
  1084. $cells .= wf_TableCell('');
  1085. $cells .= wf_TableCell($totalSumm);
  1086. $timeNorm = zb_formatTime(($totalTime * 60));
  1087. $cells .= wf_TableCell($timeNorm);
  1088. $cells .= wf_TableCell('');
  1089. $rows .= wf_TableRow($cells, 'row2');
  1090. }
  1091. $result = wf_TableBody($rows, '100%', 0, '');
  1092. return ($result);
  1093. }
  1094. /**
  1095. * Renders jobs total price for some task
  1096. *
  1097. * @param int $taskid
  1098. * @return float
  1099. */
  1100. public function getTaskPrice($taskid) {
  1101. $taskid = vf($taskid, 3);
  1102. $result = '';
  1103. $totalSumm = 0;
  1104. $all = $this->filterTaskJobsCached($taskid);
  1105. if (!empty($all)) {
  1106. foreach ($all as $io => $each) {
  1107. $jobPrice = $this->getJobPrice($each['id']);
  1108. $totalSumm = $totalSumm + $jobPrice;
  1109. }
  1110. }
  1111. $result = $totalSumm;
  1112. return ($result);
  1113. }
  1114. /**
  1115. * Deletes existing job from database by ID
  1116. *
  1117. * @param int $jobid
  1118. *
  1119. * @return void
  1120. */
  1121. public function deleteJob($jobid) {
  1122. $jobid = vf($jobid, 3);
  1123. if (isset($this->allJobs[$jobid])) {
  1124. $jobData = $this->allJobs[$jobid];
  1125. $query = "DELETE from `salary_jobs` WHERE `id`='" . $jobid . "';";
  1126. nr_query($query);
  1127. log_register('SALARY DELETE JOB TASK [' . $jobData['taskid'] . '] EMPLOYEE [' . $jobData['employeeid'] . '] JOBTYPE [' . $jobData['jobtypeid'] . '] FACTOR `' . $jobData['factor'] . '` OVERPRICE `' . $jobData['overprice'] . '`');
  1128. }
  1129. }
  1130. /**
  1131. All we do is run in circles
  1132. We’ll run until my voice will disappear
  1133. Until my sound will break the silence
  1134. And in the world will be no violence
  1135. */
  1136. /**
  1137. * Creates new employee wage record
  1138. *
  1139. * @param int $employeeid
  1140. * @param float $wage
  1141. * @param float $bounty
  1142. * @param int $worktime
  1143. *
  1144. * @return void
  1145. */
  1146. public function employeeWageCreate($employeeid, $wage, $bounty, $worktime) {
  1147. $employeeid = vf($employeeid, 3);
  1148. $worktime = vf($worktime);
  1149. if (!isset($this->allWages[$employeeid])) {
  1150. $wage = str_replace(',', '.', $wage);
  1151. $bounty = str_replace(',', '.', $bounty);
  1152. $wageF = mysql_real_escape_string($wage);
  1153. $bountyF = mysql_real_escape_string($bounty);
  1154. $query = "INSERT INTO `salary_wages` (`id`, `employeeid`, `wage`, `bounty`,`worktime`) VALUES (NULL, '" . $employeeid . "', '" . $wage . "', '" . $bounty . "','" . $worktime . "');";
  1155. nr_query($query);
  1156. log_register('SALARY CREATE WAGE EMPLOYEE [' . $employeeid . '] WAGE `' . $wageF . '` BOUNTY `' . $bountyF . '` WORKTIME `' . $worktime . '`');
  1157. } else {
  1158. log_register('SALARY CREATE WAGE FAIL EXISTS EMPLOYEE [' . $employeeid . ']');
  1159. }
  1160. }
  1161. /**
  1162. * Deletes existing employee wage from database
  1163. *
  1164. * @param int $employeeid
  1165. *
  1166. * @return void
  1167. */
  1168. public function employeeWageDelete($employeeid) {
  1169. $employeeid = vf($employeeid, 3);
  1170. $query = "DELETE from `salary_wages` WHERE `employeeid`='" . $employeeid . "';";
  1171. nr_query($query);
  1172. log_register('SALARY DELETE WAGE EMPLOYEE [' . $employeeid . ']');
  1173. }
  1174. /**
  1175. * Returns employee wage creation form
  1176. *
  1177. * @return string
  1178. */
  1179. public function employeeWageCreateForm() {
  1180. $result = '';
  1181. $employeeTmp = array();
  1182. if (!empty($this->allEmployee)) {
  1183. foreach ($this->allEmployee as $io => $each) {
  1184. if (!$this->checkEmployeeWage($io)) {
  1185. $employeeTmp[$io] = $each;
  1186. }
  1187. }
  1188. }
  1189. if (!empty($this->allEmployee)) {
  1190. $inputs = wf_Selector('newemployeewageemployeeid', $employeeTmp, __('Worker'), '', true) . ' ';
  1191. $inputs .= wf_TextInput('newemployeewage', __('Wage'), '', true, 5) . ' ';
  1192. $inputs .= wf_TextInput('newemployeewagebounty', __('Bounty'), '', true, 5) . ' ';
  1193. $inputs .= wf_TextInput('newemployeewageworktime', __('Work hours'), '', true, 5);
  1194. $inputs .= wf_Submit(__('Create'));
  1195. $result = wf_Form('', 'POST', $inputs, 'glamour');
  1196. $result .= wf_CleanDiv();
  1197. }
  1198. return ($result);
  1199. }
  1200. /**
  1201. * Returns existing employee wage editing form
  1202. *
  1203. * @param int $employeeid
  1204. * @return string
  1205. */
  1206. protected function employeeWageEditForm($employeeid) {
  1207. $employeeid = vf($employeeid, 3);
  1208. $result = '';
  1209. if (isset($this->allWages[$employeeid])) {
  1210. $inputs = wf_HiddenInput('editemployeewageemployeeid', $employeeid);
  1211. $inputs .= wf_TextInput('editemployeewage', __('Wage'), $this->allWages[$employeeid]['wage'], true, 5) . ' ';
  1212. $inputs .= wf_TextInput('editemployeewagebounty', __('Bounty'), $this->allWages[$employeeid]['bounty'], true, 5) . ' ';
  1213. $inputs .= wf_TextInput('editemployeewageworktime', __('Work hours'), $this->allWages[$employeeid]['worktime'], true, 5) . ' ';
  1214. $inputs .= wf_Submit(__('Save'));
  1215. $result = wf_Form('', 'POST', $inputs, 'glamour');
  1216. $result .= wf_CleanDiv();
  1217. } else {
  1218. $result = __('Strange exeption') . ': NOT_EXISTING_EMPLOYEID';
  1219. }
  1220. return ($result);
  1221. }
  1222. /**
  1223. * Changes existing employee wage in database
  1224. *
  1225. * @param int $employeeid
  1226. * @param float $wage
  1227. * @param float $bounty
  1228. * @param int $worktime
  1229. *
  1230. * @return void
  1231. */
  1232. public function employeeWageEdit($employeeid, $wage, $bounty, $worktime) {
  1233. $employeeid = vf($employeeid, 3);
  1234. $wage = str_replace(',', '.', $wage);
  1235. $bounty = str_replace(',', '.', $bounty);
  1236. $worktime = vf($worktime, 3);
  1237. $where = " WHERE `employeeid`='" . $employeeid . "'";
  1238. simple_update_field('salary_wages', 'wage', $wage, $where);
  1239. simple_update_field('salary_wages', 'bounty', $bounty, $where);
  1240. simple_update_field('salary_wages', 'worktime', $worktime, $where);
  1241. log_register('SALARY EDIT WAGE EMPLOYEE [' . $employeeid . '] WAGE `' . $wage . '` BOUNTY `' . $bounty . '` WORKTIME `' . $worktime . '`');
  1242. }
  1243. /**
  1244. * Renders available employee wages list with some controls
  1245. *
  1246. * @return string
  1247. */
  1248. public function employeeWagesRender() {
  1249. $result = '';
  1250. $messages = new UbillingMessageHelper();
  1251. $cells = wf_TableCell(__('Employee'));
  1252. $cells .= wf_TableCell(__('Wage'));
  1253. $cells .= wf_TableCell(__('Bounty'));
  1254. $cells .= wf_TableCell(__('Work hours'));
  1255. $cells .= wf_TableCell(__('Actions'));
  1256. $rows = wf_TableRow($cells, 'row1');
  1257. if (!empty($this->allWages)) {
  1258. foreach ($this->allWages as $io => $each) {
  1259. $rowClass = (isset($this->allEmployee[$io])) ? 'row3' : 'sigdeleteduser';
  1260. $cells = wf_TableCell(@$this->allEmployeeRaw[$io]);
  1261. $cells .= wf_TableCell($this->allWages[$io]['wage']);
  1262. $cells .= wf_TableCell($this->allWages[$io]['bounty']);
  1263. $cells .= wf_TableCell($this->allWages[$io]['worktime']);
  1264. $actlinks = wf_JSAlertStyled('?module=salary&employeewages=true&deletewage=' . $io, web_delete_icon(), $messages->getDeleteAlert());
  1265. $actlinks .= wf_modalAuto(web_edit_icon(), __('Edit'), $this->employeeWageEditForm($io));
  1266. $cells .= wf_TableCell($actlinks);
  1267. $rows .= wf_TableRow($cells, $rowClass);
  1268. }
  1269. }
  1270. $result = wf_TableBody($rows, '100%', 0, 'sortable');
  1271. return ($result);
  1272. }
  1273. /**
  1274. * Renders payroll report search form
  1275. *
  1276. * @return string
  1277. */
  1278. public function payrollRenderSearchForm() {
  1279. $result = '';
  1280. $empParams = array('' => __('Any'));
  1281. if (!empty($this->allEmployee)) {
  1282. foreach ($this->allEmployee as $io => $each) {
  1283. if ($this->checkEmployeeWage($io)) {
  1284. $empParams[$io] = $each;
  1285. }
  1286. }
  1287. }
  1288. $jobtypeParams = array('' => __('Any'));
  1289. $jobtypeParams += $this->allJobtypes;
  1290. $fromDate = (wf_CheckPost(array('prdatefrom'))) ? $_POST['prdatefrom'] : curdate();
  1291. $toDate = (wf_CheckPost(array('prdateto'))) ? $_POST['prdateto'] : curdate();
  1292. $currentEmployee = (wf_CheckPost(array('premployeeid'))) ? $_POST['premployeeid'] : '';
  1293. $currentJobTypeId = (wf_CheckPost(array('prjobtypeid'))) ? $_POST['prjobtypeid'] : '';
  1294. $currentChartsFlag = (wf_CheckPost(array('prnocharts'))) ? true : false;
  1295. $inputs = wf_DatePickerPreset('prdatefrom', $fromDate, true) . ' ';
  1296. $inputs .= wf_DatePickerPreset('prdateto', $toDate, true) . ' ';
  1297. $inputs .= wf_Selector('premployeeid', $empParams, __('Worker'), $currentEmployee, false);
  1298. $inputs .= wf_Selector('prjobtypeid', $jobtypeParams, __('Job type'), $currentJobTypeId, false, false);
  1299. $inputs .= wf_CheckInput('prnocharts', __('No charts'), false, $currentChartsFlag);
  1300. $inputs .= wf_Submit(__('Show'));
  1301. $result = wf_Form('', 'POST', $inputs, 'glamour');
  1302. return ($result);
  1303. }
  1304. /**
  1305. * Renders payroll report search results
  1306. *
  1307. * @param string $datefrom
  1308. * @param string $dateto
  1309. * @param int $employeeid
  1310. * @param bool $controls
  1311. *
  1312. * @return string
  1313. */
  1314. public function payrollRenderSearch($datefrom, $dateto, $employeeid, $jobtypeid = '', $controls = true) {
  1315. $datefrom = mysql_real_escape_string($datefrom);
  1316. $dateto = mysql_real_escape_string($dateto);
  1317. $jobtypeid = vf($jobtypeid, 3);
  1318. $employeeid = vf($employeeid, 3);
  1319. $allTasks = ts_GetAllTasksQuickData();
  1320. $totalTimeSpent = 0; //in minutes
  1321. $timeSheetsTimeSpent = 0; // in minutes
  1322. $rangeTimesheets = $this->timesheetFilterDateRange($datefrom, $dateto);
  1323. $currentChartsFlag = (wf_CheckPost(array('prnocharts'))) ? true : false;
  1324. if (wf_CheckGet(array('nc'))) {
  1325. $currentChartsFlag = true;
  1326. }
  1327. $chartData = array();
  1328. $chartDataCash = array();
  1329. $timeChartData = array();
  1330. $result = '';
  1331. $totalSum = 0;
  1332. $payedSum = 0;
  1333. $jobCount = 0;
  1334. $litreCountUnpaid = 0;
  1335. $litreCountPaid = 0;
  1336. $jobtypeFilter = (!empty($jobtypeid)) ? "AND `jobtypeid`='" . $jobtypeid . "'" : '';
  1337. $query = "SELECT * from `salary_jobs` WHERE CAST(`date` AS DATE) BETWEEN '" . $datefrom . "' AND '" . $dateto . "' AND `employeeid`='" . $employeeid . "' " . $jobtypeFilter . ";";
  1338. $all = simple_queryall($query);
  1339. $selectAllControl = wf_CheckInput('selectallbears', __('All'), false, false, '', 'gummybear');
  1340. $cells = wf_TableCell(__('Date'));
  1341. $cells .= wf_TableCell(__('Task'));
  1342. $cells .= wf_TableCell(__('Job type'));
  1343. $cells .= wf_TableCell(__('Factor'));
  1344. $cells .= wf_TableCell(__('Time'));
  1345. $cells .= wf_TableCell(__('Price override'));
  1346. $cells .= wf_TableCell(__('Notes'));
  1347. $cells .= wf_TableCell(__('Paid'));
  1348. $cells .= wf_TableCell(__('Money'));
  1349. if ($controls) {
  1350. $cells .= wf_TableCell($selectAllControl);
  1351. }
  1352. $rows = wf_TableRow($cells, 'row1');
  1353. if (!empty($all)) {
  1354. foreach ($all as $io => $each) {
  1355. $jobName = @$this->allJobtypes[$each['jobtypeid']];
  1356. $jobPrice = $this->getJobPrice($each['id']);
  1357. $unitType = @$this->allJobUnits[$each['jobtypeid']];
  1358. if (!empty($jobName)) {
  1359. if (isset($chartData[$jobName])) {
  1360. $chartData[$jobName]['count'] += $each['factor'];
  1361. if ($each['state'] == 0) {
  1362. $chartData[$jobName]['unpaid'] += $each['factor'];
  1363. } else {
  1364. $chartData[$jobName]['paid'] += $each['factor'];
  1365. }
  1366. $chartDataCash[$jobName]['cash'] = $chartDataCash[$jobName]['cash'] + $jobPrice;
  1367. if ($each['state'] == 0) {
  1368. $chartDataCash[$jobName]['unpaid'] += $jobPrice;
  1369. } else {
  1370. $chartDataCash[$jobName]['paid'] += $jobPrice;
  1371. }
  1372. } else {
  1373. $chartData[$jobName]['count'] = $each['factor'];
  1374. if ($each['state'] == 0) {
  1375. $chartData[$jobName]['unpaid'] = $each['factor'];
  1376. $chartData[$jobName]['paid'] = 0;
  1377. } else {
  1378. $chartData[$jobName]['unpaid'] = 0;
  1379. $chartData[$jobName]['paid'] = $each['factor'];
  1380. }
  1381. $chartDataCash[$jobName]['cash'] = $jobPrice;
  1382. if ($each['state'] == 0) {
  1383. $chartDataCash[$jobName]['unpaid'] = $jobPrice;
  1384. $chartDataCash[$jobName]['paid'] = 0;
  1385. } else {
  1386. $chartDataCash[$jobName]['unpaid'] = 0;
  1387. $chartDataCash[$jobName]['paid'] = $jobPrice;
  1388. }
  1389. }
  1390. }
  1391. if (isset($this->allJobUnits[$each['jobtypeid']])) {
  1392. $unit = $this->unitTypes[$unitType];
  1393. } else {
  1394. $unit = __('No');
  1395. }
  1396. //job time spent collecting
  1397. $jobTimeSpent = 0;
  1398. if (isset($this->allJobTimes[$each['jobtypeid']])) {
  1399. $jobFactor = $each['factor'];
  1400. $jobTimePlanned = $this->allJobTimes[$each['jobtypeid']];
  1401. $jobTimeSpent = $jobFactor * $jobTimePlanned;
  1402. $totalTimeSpent += $jobTimeSpent;
  1403. $jobTypeName = $this->allJobtypes[$each['jobtypeid']];
  1404. if (!empty($jobTypeName)) {
  1405. if (isset($timeChartData[$jobTypeName])) {
  1406. $timeChartData[$jobTypeName] += $jobTimeSpent;
  1407. } else {
  1408. $timeChartData[$jobTypeName] = $jobTimeSpent;
  1409. }
  1410. }
  1411. }
  1412. $cells = wf_TableCell($each['date']);
  1413. $cells .= wf_TableCell(wf_Link(self::URL_TS . $each['taskid'], $each['taskid']) . ' ' . @$allTasks[$each['taskid']]['address']);
  1414. $cells .= wf_TableCell($jobName);
  1415. $cells .= wf_TableCell($each['factor'] . ' / ' . $unit);
  1416. $cells .= wf_TableCell($this->formatTime($jobTimeSpent * 60));
  1417. $cells .= wf_TableCell($each['overprice']);
  1418. $cells .= wf_TableCell($each['note']);
  1419. $cells .= wf_TableCell($this->renderPaidDataLed($each['id']));
  1420. $cells .= wf_TableCell($jobPrice);
  1421. if (!$each['state']) {
  1422. $actControls = wf_CheckInput('_prstatecheck[' . $each['id'] . ']', '', true, false, '', 'someonelikeyou');
  1423. } else {
  1424. $actControls = '';
  1425. }
  1426. if ($controls) {
  1427. $cells .= wf_TableCell($actControls);
  1428. }
  1429. $rows .= wf_TableRow($cells, 'row3');
  1430. if ($each['state'] == 0) {
  1431. if ($unitType != 'litre') {
  1432. $totalSum = $totalSum + $jobPrice;
  1433. } else {
  1434. $litreCountUnpaid += $jobPrice;
  1435. }
  1436. $jobCount++;
  1437. } else {
  1438. if ($unitType != 'litre') {
  1439. $payedSum = $payedSum + $jobPrice;
  1440. } else {
  1441. $litreCountPaid += $jobPrice;
  1442. }
  1443. }
  1444. }
  1445. }
  1446. //timesheets processing
  1447. if (!empty($rangeTimesheets)) {
  1448. foreach ($rangeTimesheets as $io => $each) {
  1449. if ($each['employeeid'] == $employeeid) {
  1450. $timeSheetsTimeSpent += $each['hours'] * 60; // rly in minutes
  1451. }
  1452. }
  1453. }
  1454. $result .= wf_TableBody($rows, '100%', 0, '');
  1455. $result .= wf_HiddenInput('prstateprocessing', 'true');
  1456. $result .= wf_tag('script', false);
  1457. $result .= '
  1458. var checkboxes = document.querySelectorAll(".someonelikeyou");
  1459. //Never mind Ill find someone like you
  1460. //I wish nothing but the best for you too
  1461. function selectCheckbox() {
  1462. var newstate=$(".gummybear").is(\':checked\');
  1463. checkboxes.forEach(function(checkbox) {
  1464. checkbox.checked = newstate;
  1465. })
  1466. }
  1467. ';
  1468. $result .= wf_tag('script', true);
  1469. if ($jobCount > 0) {
  1470. if ($controls) {
  1471. $result .= wf_Submit(__('Processing'), 'payrollsubmitid') . wf_delimiter();
  1472. }
  1473. }
  1474. $result = wf_Form('', 'POST', $result, '');
  1475. $result .= __('Not paid money') . ': ' . $totalSum . wf_tag('br');
  1476. $result .= __('Not paid fuel') . ': ' . $litreCountUnpaid . ' ' . __('litre') . wf_tag('br');
  1477. $result .= __('Paid money') . ': ' . $payedSum . wf_tag('br');
  1478. $result .= __('Paid fuel') . ': ' . $litreCountPaid . ' ' . __('litre') . wf_tag('br');
  1479. $result .= __('Total money') . ': ' . ($payedSum + $totalSum) . wf_tag('br');
  1480. $result .= __('Total') . ' ' . __('time') . ': ' . $this->formatTime($totalTimeSpent * 60) . wf_tag('br');
  1481. $result .= __('Total') . ' ' . __('Work hours') . ': ' . $this->formatTime($timeSheetsTimeSpent * 60);
  1482. if (!empty($chartData)) {
  1483. $result .= wf_CleanDiv();
  1484. //chart data postprocessing
  1485. if (!empty($timeChartData)) {
  1486. foreach ($timeChartData as $io => $each) {
  1487. $timeChartData[$io . ' ' . $each] = $each;
  1488. unset($timeChartData[$io]);
  1489. }
  1490. }
  1491. if (!empty($chartData)) {
  1492. foreach ($chartData as $io => $each) {
  1493. $chartData[$io . ' ' . $each['count'] . ' (' . $each['paid'] . '/' . $each['unpaid'] . ')'] = $each['count'];
  1494. unset($chartData[$io]);
  1495. }
  1496. }
  1497. if (!empty($chartDataCash)) {
  1498. foreach ($chartDataCash as $io => $each) {
  1499. $chartDataCash[$io . ' ' . $each['cash'] . ' (' . $each['paid'] . '/' . $each['unpaid'] . ')'] = $each['cash'];
  1500. unset($chartDataCash[$io]);
  1501. }
  1502. }
  1503. $result .= wf_tag('div', false, '', 'style="page-break-after: always;"') . wf_tag('div', true);
  1504. if (!$currentChartsFlag) {
  1505. $chartOpts = "chartArea: { width: '100%', height: '80%' }, legend : {position: 'right', textStyle: {fontSize: 12 }}, pieSliceText: 'value-and-percentage',";
  1506. $chartCells = wf_TableCell(wf_gcharts3DPie($timeChartData, __('Time') . ' (' . __('hours') . ')', '600px', '400px', $chartOpts));
  1507. $chartCells .= wf_TableCell(wf_gcharts3DPie($chartData, __('Job types') . ' (' . __('Paid') . '/' . __('Unpaid') . ')', '600px', '400px', $chartOpts));
  1508. $chartRows = wf_TableRow($chartCells);
  1509. $chartCells = wf_TableCell(wf_gcharts3DPie($chartDataCash, __('Money') . ' (' . __('Paid') . '/' . __('Unpaid') . ')', '600px', '400px', $chartOpts));
  1510. $chartRows .= wf_TableRow($chartCells);
  1511. $result .= wf_TableBody($chartRows, '100%', 0, '');
  1512. } else {
  1513. $result .= wf_tag('br');
  1514. $result .= $this->renderTableViewStats($timeChartData, __('Time') . ' (' . __('hours') . ')', true);
  1515. $result .= $this->renderTableViewStats($chartData, __('Job types') . ' (' . __('Paid') . '/' . __('Unpaid') . ')', true, true);
  1516. $result .= $this->renderTableViewStats($chartDataCash, __('Money') . ' (' . __('Paid') . '/' . __('Unpaid') . ')', true, true);
  1517. }
  1518. }
  1519. return ($result);
  1520. }
  1521. /**
  1522. * Renders default pie-chart data as table summary
  1523. *
  1524. * @param array $chartData
  1525. * @param string $title
  1526. * @param bool $filterType
  1527. * @param boolt $replaceValue
  1528. * @return string
  1529. */
  1530. protected function renderTableViewStats($chartData, $title = '', $filterType = true, $replaceValue = false) {
  1531. $result = '';
  1532. $result .= wf_tag('b') . __($title) . wf_tag('b', true);
  1533. if (!empty($chartData)) {
  1534. $cells = wf_TableCell(__('Type'));
  1535. $cells .= wf_TableCell(__('Value'));
  1536. $rows = wf_TableRow($cells, 'row1');
  1537. foreach ($chartData as $io => $each) {
  1538. $type = $io;
  1539. $value = $each;
  1540. if ($replaceValue) {
  1541. if (preg_match('/\([^)]+\)/', $type, $match)) {
  1542. $value = $match[0];
  1543. $value = str_replace('(', '', $value);
  1544. $value = str_replace(')', '', $value);
  1545. }
  1546. }
  1547. if ($filterType) {
  1548. $type = str_replace($each, '', $type);
  1549. $type = preg_replace("/\([^)]+\)/", '', $type);
  1550. }
  1551. $cells = wf_TableCell($type);
  1552. $cells .= wf_TableCell($value);
  1553. $rows .= wf_TableRow($cells, 'row3');
  1554. }
  1555. $result .= wf_TableBody($rows, '100%', 0, 'sortable');
  1556. }
  1557. return ($result);
  1558. }
  1559. /**
  1560. * Filters available timesheets by date range
  1561. *
  1562. * @param string $datefrom
  1563. * @param string $dateto
  1564. *
  1565. * @return array
  1566. */
  1567. protected function timesheetFilterDateRange($datefrom, $dateto) {
  1568. $result = array();
  1569. $datefrom = strtotime($datefrom);
  1570. $dateto = strtotime($dateto);
  1571. if (!empty($this->allTimesheets)) {
  1572. foreach ($this->allTimesheets as $io => $each) {
  1573. $timesheetDate = strtotime($each['date']);
  1574. if (($timesheetDate >= $datefrom) and ($timesheetDate <= $dateto)) {
  1575. $result[$each['id']] = $each;
  1576. }
  1577. }
  1578. }
  1579. return ($result);
  1580. }
  1581. /**
  1582. * Renders payroll report search results for all employee
  1583. *
  1584. * @param string $datefrom
  1585. * @param string $dateto
  1586. * @param int $jobtypeid
  1587. * @return string
  1588. */
  1589. public function payrollRenderSearchDate($datefrom, $dateto, $jobtypeid = '') {
  1590. $datefrom = mysql_real_escape_string($datefrom);
  1591. $dateto = mysql_real_escape_string($dateto);
  1592. $jobtypeid = vf($jobtypeid, 3);
  1593. $currentChartsFlag = (wf_CheckPost(array('prnocharts'))) ? true : false;
  1594. if (wf_CheckGet(array('nc'))) {
  1595. $currentChartsFlag = true;
  1596. }
  1597. $result = '';
  1598. $totalSum = 0;
  1599. $totalPayedSum = 0;
  1600. $totalWage = 0;
  1601. $totalBounty = 0;
  1602. $totalWorkTime = 0;
  1603. $jobCount = 0;
  1604. $jobsTmp = array();
  1605. $employeeCharts = array();
  1606. $employeeChartsMoney = array();
  1607. $perEmployeeTimesheets = array();
  1608. $rangeTimesheets = $this->timesheetFilterDateRange($datefrom, $dateto);
  1609. if (!empty($rangeTimesheets)) {
  1610. foreach ($rangeTimesheets as $io => $each) {
  1611. if (isset($perEmployeeTimesheets[$each['employeeid']])) {
  1612. $perEmployeeTimesheets[$each['employeeid']] += $each['hours'];
  1613. } else {
  1614. $perEmployeeTimesheets[$each['employeeid']] = $each['hours'];
  1615. }
  1616. }
  1617. }
  1618. $jobtypeFilter = (!empty($jobtypeid)) ? "AND `jobtypeid`='" . $jobtypeid . "'" : '';
  1619. $query = "SELECT * from `salary_jobs` WHERE CAST(`date` AS DATE) BETWEEN '" . $datefrom . "' AND '" . $dateto . "' " . $jobtypeFilter . ";";
  1620. $all = simple_queryall($query);
  1621. //jobs preprocessing
  1622. if (!empty($all)) {
  1623. foreach ($all as $io => $each) {
  1624. $jobPrice = $this->getJobPrice($each['id']);
  1625. $jobTime = (isset($this->allJobTimes[$each['jobtypeid']])) ? $this->allJobTimes[$each['jobtypeid']] * $each['factor'] : 0;
  1626. if (!isset($jobsTmp[$each['employeeid']])) {
  1627. $payedSum = ($each['state']) ? $jobPrice : 0;
  1628. $jobsTmp[$each['employeeid']]['count'] = 1;
  1629. $jobsTmp[$each['employeeid']]['sum'] = $jobPrice;
  1630. $jobsTmp[$each['employeeid']]['payed'] = $payedSum;
  1631. $jobsTmp[$each['employeeid']]['time'] = $jobTime;
  1632. } else {
  1633. $payedSum = ($each['state']) ? $jobPrice : 0;
  1634. $jobsTmp[$each['employeeid']]['count']++;
  1635. $jobsTmp[$each['employeeid']]['sum'] += $jobPrice;
  1636. $jobsTmp[$each['employeeid']]['payed'] += $payedSum;
  1637. $jobsTmp[$each['employeeid']]['time'] += $jobTime;
  1638. }
  1639. $totalPayedSum += $payedSum;
  1640. $totalSum += $jobPrice;
  1641. }
  1642. }
  1643. $cells = wf_TableCell(__('Worker'));
  1644. $cells .= wf_TableCell(__('Wage'));
  1645. $cells .= wf_TableCell(__('Bounty'));
  1646. $cells .= wf_TableCell(__('Work hours'));
  1647. $cells .= wf_TableCell(__('Jobs'));
  1648. $cells .= wf_TableCell(__('Spent time') . ' (' . __('hours') . ')');
  1649. $cells .= wf_TableCell(__('Earned money'));
  1650. $cells .= wf_TableCell(__('Paid'));
  1651. $rows = wf_TableRow($cells, 'row1');
  1652. if (!empty($this->allEmployee)) {
  1653. foreach ($this->allEmployee as $io => $each) {
  1654. if ($this->checkEmployeeWage($io)) {
  1655. $cells = wf_TableCell($each);
  1656. $wage = (isset($this->allWages[$io]['wage'])) ? $this->allWages[$io]['wage'] : __('No');
  1657. $bounty = (isset($this->allWages[$io]['bounty'])) ? $this->allWages[$io]['bounty'] : __('No');
  1658. $worktime = (isset($this->allWages[$io]['worktime'])) ? $this->allWages[$io]['worktime'] : __('No');
  1659. $workerJobsData = (isset($jobsTmp[$io])) ? $jobsTmp[$io] : array('count' => 0, 'sum' => 0, 'payed' => 0, 'time' => 0);
  1660. $cells .= wf_TableCell($wage);
  1661. $cells .= wf_TableCell($bounty);
  1662. $cells .= wf_TableCell(@$perEmployeeTimesheets[$io]);
  1663. $cells .= wf_TableCell($workerJobsData['count']);
  1664. $cells .= wf_TableCell(round(($workerJobsData['time'] / 60), 2));
  1665. $cells .= wf_TableCell($workerJobsData['sum']);
  1666. $cells .= wf_TableCell($workerJobsData['payed']);
  1667. $rows .= wf_TableRow($cells, 'row3');
  1668. $totalWage += $wage;
  1669. $totalBounty += $bounty;
  1670. $totalWorkTime += $workerJobsData['time'];
  1671. $jobCount += $workerJobsData['count'];
  1672. $employeeCharts[$each] = $workerJobsData['count'];
  1673. $employeeChartsMoney[$each] = $workerJobsData['sum'];
  1674. }
  1675. }
  1676. }
  1677. $cells = wf_TableCell(__('Total'));
  1678. $cells .= wf_TableCell($totalWage);
  1679. $cells .= wf_TableCell($totalBounty);
  1680. $cells .= wf_TableCell('');
  1681. $cells .= wf_TableCell($jobCount);
  1682. $cells .= wf_TableCell(round(($totalWorkTime / 60), 2));
  1683. $cells .= wf_TableCell($totalSum);
  1684. $cells .= wf_TableCell($totalPayedSum);
  1685. $rows .= wf_TableRow($cells, 'row2');
  1686. $result = wf_TableBody($rows, '100%', 0, '');
  1687. $result .= wf_delimiter();
  1688. //charts
  1689. $sumCharts = array(__('Earned money') => $totalSum - $totalPayedSum, __('Paid') => $totalPayedSum);
  1690. if (!$currentChartsFlag) {
  1691. $chartOpts = "chartArea: { width: '100%', height: '80%' }, legend : {position: 'right', textStyle: {fontSize: 12 }}, pieSliceText: 'value-and-percentage',";
  1692. $cells = wf_TableCell(wf_gcharts3DPie($sumCharts, __('Money'), '400px', '400px', $chartOpts));
  1693. $cells .= wf_TableCell(wf_gcharts3DPie($employeeChartsMoney, __('Money') . ' / ' . __('Worker'), '400px', '400px', $chartOpts));
  1694. $rows = wf_TableRow($cells);
  1695. $cells = wf_TableCell(wf_gcharts3DPie($employeeCharts, __('Jobs'), '400px', '400px', $chartOpts));
  1696. $cells .= wf_TableCell('');
  1697. $rows .= wf_TableRow($cells);
  1698. $result .= wf_TableBody($rows, '100%', 0, '');
  1699. } else {
  1700. $result .= $this->renderTableViewStats($sumCharts, __('Money'), true);
  1701. $result .= $this->renderTableViewStats($employeeChartsMoney, __('Money') . ' / ' . __('Worker'), true);
  1702. $result .= $this->renderTableViewStats($employeeCharts, __('Jobs'), true);
  1703. }
  1704. return ($result);
  1705. }
  1706. /**
  1707. * Renders available tasks list as human-readable table
  1708. *
  1709. * @param array $taskArr
  1710. *
  1711. * @return string
  1712. */
  1713. protected function renderJobList($taskArr) {
  1714. $result = '';
  1715. $totalSum = 0;
  1716. $payedSum = 0;
  1717. $cells = wf_TableCell(__('Date'));
  1718. $cells .= wf_TableCell(__('Task'));
  1719. $cells .= wf_TableCell(__('Job type'));
  1720. $cells .= wf_TableCell(__('Factor'));
  1721. $cells .= wf_TableCell(__('Price override'));
  1722. $cells .= wf_TableCell(__('Notes'));
  1723. $cells .= wf_TableCell(__('Paid'));
  1724. $cells .= wf_TableCell(__('Money'));
  1725. $rows = wf_TableRow($cells, 'row1');
  1726. if (!empty($taskArr)) {
  1727. foreach ($taskArr as $io => $each) {
  1728. if (isset($this->allJobs[$io])) {
  1729. $jobData = $this->allJobs[$io];
  1730. if (isset($this->allJobUnits[$jobData['jobtypeid']])) {
  1731. $unit = $this->unitTypes[$this->allJobUnits[$jobData['jobtypeid']]];
  1732. } else {
  1733. $unit = __('No');
  1734. }
  1735. $cells = wf_TableCell($jobData['date']);
  1736. $cells .= wf_TableCell($jobData['taskid']);
  1737. $cells .= wf_TableCell(@$this->allJobtypes[$jobData['jobtypeid']]);
  1738. $cells .= wf_TableCell($jobData['factor'] . ' / ' . $unit);
  1739. $cells .= wf_TableCell($jobData['overprice']);
  1740. $cells .= wf_TableCell($jobData['note']);
  1741. $jobPrice = $this->getJobPrice($jobData['id']);
  1742. $cells .= wf_TableCell(web_bool_led($jobData['state']));
  1743. $cells .= wf_TableCell($jobPrice);
  1744. $rows .= wf_TableRow($cells, 'row3');
  1745. if (!$jobData['state']) {
  1746. $totalSum = $totalSum + $jobPrice;
  1747. } else {
  1748. $payedSum = $payedSum + $jobPrice;
  1749. }
  1750. } else {
  1751. show_error(__('Job') . ' [' . $io . '] ' . __('is not loaded'));
  1752. }
  1753. }
  1754. }
  1755. $result = wf_TableBody($rows, '100%', 0, 'sortable');
  1756. $result .= __('Total') . ' ' . __('money') . ': ' . $totalSum . wf_tag('br');
  1757. $result .= __('Processed') . ' ' . __('money') . ': ' . $payedSum;
  1758. return ($result);
  1759. }
  1760. /**
  1761. * Performs job states processing agreement form
  1762. *
  1763. * @return string
  1764. */
  1765. public function payrollStateProcessingForm() {
  1766. $result = '';
  1767. $result .= wf_HiddenInput('prstateprocessingconfirmed', 'true');
  1768. $tmpArr = array();
  1769. if (wf_CheckPost(array('_prstatecheck'))) {
  1770. if (!empty($_POST['_prstatecheck'])) {
  1771. $checksRaw = $_POST['_prstatecheck'];
  1772. foreach ($checksRaw as $io => $each) {
  1773. $tmpArr[$io] = $each;
  1774. $result .= wf_HiddenInput('_prstatecheck[' . $io . ']', 'on');
  1775. }
  1776. $result .= $this->renderJobList($tmpArr);
  1777. $result .= wf_delimiter();
  1778. $result .= wf_Submit(__('Payment confirmation'));
  1779. $result = wf_Form('', 'POST', $result, '');
  1780. }
  1781. }
  1782. return ($result);
  1783. }
  1784. /**
  1785. * Performs job states processing
  1786. *
  1787. * @return void
  1788. */
  1789. public function payrollStateProcessing() {
  1790. $jobCount = 0;
  1791. if (wf_CheckPost(array('_prstatecheck'))) {
  1792. $checksRaw = $_POST['_prstatecheck'];
  1793. if (!empty($checksRaw)) {
  1794. foreach ($checksRaw as $io => $each) {
  1795. $jobId = vf($io, 3);
  1796. simple_update_field('salary_jobs', 'state', '1', " WHERE `id`='" . $jobId . "';");
  1797. $this->pushPaid($jobId);
  1798. $jobCount++;
  1799. }
  1800. show_success(__('Job payment processing finished'));
  1801. log_register('SALARY JOBS PROCESSED `' . $jobCount . '`');
  1802. } else {
  1803. log_register('SALARY JOBS PROCESSING FAIL EMPTY_JOBIDS');
  1804. }
  1805. }
  1806. }
  1807. /**
  1808. * Returns existing employee name
  1809. *
  1810. * @param int $employeeid
  1811. * @return string
  1812. */
  1813. public function getEmployeeName($employeeid) {
  1814. $result = '';
  1815. if (isset($this->allEmployee[$employeeid])) {
  1816. $result = $this->allEmployee[$employeeid];
  1817. }
  1818. return ($result);
  1819. }
  1820. /**
  1821. * Renders factor control search form :P
  1822. *
  1823. * @return string
  1824. */
  1825. public function facontrolRenderSearchForm() {
  1826. $result = '';
  1827. if (!empty($this->allJobtypes)) {
  1828. $inputs = wf_Selector('facontroljobtypeid', $this->allJobtypes, __('Job type'), '', false);
  1829. $inputs .= wf_TextInput('facontrolmaxfactor', '> ' . __('Factor'), '', false, '4');
  1830. $inputs .= wf_Submit(__('Show'));
  1831. $result = wf_Form('', 'POST', $inputs, 'glamour');
  1832. }
  1833. return ($result);
  1834. }
  1835. /**
  1836. * Renders factor control report search results
  1837. *
  1838. * @param int $jobtypeid
  1839. * @param float $factor
  1840. *
  1841. * @return string
  1842. */
  1843. public function facontrolRenderSearch($jobtypeid, $factor) {
  1844. $result = '';
  1845. $jobtypeid = vf($jobtypeid, 3);
  1846. $messages = new UbillingMessageHelper();
  1847. $tmpArr = array();
  1848. $allTasks = ts_GetAllTasksQuickData();
  1849. if (!empty($this->allJobs)) {
  1850. foreach ($this->allJobs as $io => $each) {
  1851. if ($jobtypeid == $each['jobtypeid']) {
  1852. if (isset($tmpArr[$each['taskid']])) {
  1853. $tmpArr[$each['taskid']] += $each['factor'];
  1854. } else {
  1855. $tmpArr[$each['taskid']] = $each['factor'];
  1856. }
  1857. }
  1858. }
  1859. }
  1860. if (!empty($tmpArr)) {
  1861. if (isset($this->allJobUnits[$jobtypeid])) {
  1862. $unit = $this->unitTypes[$this->allJobUnits[$jobtypeid]];
  1863. } else {
  1864. $unit = __('No');
  1865. }
  1866. $cells = wf_TableCell(__('Task'));
  1867. $cells .= wf_TableCell(__('Address'));
  1868. $cells .= wf_TableCell(__('Target date'));
  1869. $cells .= wf_TableCell(__('Job type'));
  1870. $cells .= wf_TableCell(__('Who should do'));
  1871. $cells .= wf_TableCell(__('Factor') . ' (' . $unit . ')');
  1872. $rows = wf_TableRow($cells, 'row1');
  1873. foreach ($tmpArr as $taskid => $factorOverflow) {
  1874. if ($factorOverflow > $factor) {
  1875. $cells = wf_TableCell(wf_Link(self::URL_TS . $taskid, $taskid));
  1876. $cells .= wf_TableCell(@$allTasks[$taskid]['address']);
  1877. $cells .= wf_TableCell(@$allTasks[$taskid]['startdate']);
  1878. $cells .= wf_TableCell(@$this->allJobtypes[$jobtypeid]);
  1879. $cells .= wf_TableCell(@$this->allEmployeeRaw[$allTasks[$taskid]['employee']]);
  1880. $cells .= wf_TableCell($factorOverflow);
  1881. $rows .= wf_TableRow($cells, 'row3');
  1882. }
  1883. }
  1884. $result .= wf_TableBody($rows, '100%', 0, 'sortable');
  1885. } else {
  1886. $result = $messages->getStyledMessage(__('Nothing found'), 'info');
  1887. }
  1888. return ($result);
  1889. }
  1890. /**
  1891. * Renders tasks without jobs report search form
  1892. *
  1893. * @return string
  1894. */
  1895. public function twjReportSearchForm() {
  1896. $result = '';
  1897. $curdate = curdate();
  1898. $inputs = wf_DatePickerPreset('twfdatefrom', $curdate, true);
  1899. $inputs .= wf_DatePickerPreset('twfdateto', $curdate, true);
  1900. $inputs .= wf_Submit(__('Show'));
  1901. $result = wf_Form('', 'POST', $inputs, 'glamour');
  1902. return ($result);
  1903. }
  1904. /**
  1905. * Renders tasks without jobs report
  1906. *
  1907. * @param string $datefrom
  1908. * @param string $dateto
  1909. *
  1910. * @return string
  1911. */
  1912. public function twjReportSearch($datefrom, $dateto) {
  1913. $datefrom = mysql_real_escape_string($datefrom);
  1914. $dateto = mysql_real_escape_string($dateto);
  1915. $result = '';
  1916. $tmpArr = array();
  1917. $messages = new UbillingMessageHelper();
  1918. $query = "SELECT * from `taskman` WHERE CAST(`startdate` AS DATE) BETWEEN '" . $datefrom . "' AND '" . $dateto . "';";
  1919. $allTasks = simple_queryall($query);
  1920. if (!empty($allTasks)) {
  1921. foreach ($allTasks as $io => $eachTask) {
  1922. $taskJobs = $this->filterTaskJobs($eachTask['id']);
  1923. if (empty($taskJobs)) {
  1924. $tmpArr[$eachTask['id']] = $eachTask;
  1925. }
  1926. }
  1927. if (!empty($tmpArr)) {
  1928. $cells = wf_TableCell(__('Task'));
  1929. $cells .= wf_TableCell(__('Address'));
  1930. $cells .= wf_TableCell(__('Target date'));
  1931. $cells .= wf_TableCell(__('Job type'));
  1932. $cells .= wf_TableCell(__('Who should do'));
  1933. $cells .= wf_TableCell(__('Done'));
  1934. $rows = wf_TableRow($cells, 'row1');
  1935. foreach ($tmpArr as $io => $eachTask) {
  1936. $taskid = $eachTask['id'];
  1937. $cells = wf_TableCell(wf_Link(self::URL_TS . $taskid, $taskid));
  1938. $cells .= wf_TableCell(@$eachTask['address']);
  1939. $cells .= wf_TableCell(@$eachTask['startdate']);
  1940. $cells .= wf_TableCell(@$this->allJobtypes[$eachTask['jobtype']]);
  1941. $cells .= wf_TableCell(@$this->allEmployeeRaw[$eachTask['employee']]);
  1942. $cells .= wf_TableCell(web_bool_led($eachTask['status']));
  1943. $rows .= wf_TableRow($cells, 'row3');
  1944. }
  1945. $result .= wf_TableBody($rows, '100%', 0, 'sortable');
  1946. } else {
  1947. $result = $messages->getStyledMessage(__('Nothing found'), 'info');
  1948. }
  1949. } else {
  1950. $result = $messages->getStyledMessage(__('Nothing found'), 'info');
  1951. }
  1952. return ($result);
  1953. }
  1954. /**
  1955. Far across the distance
  1956. And spaces between us
  1957. You have come to show you go on
  1958. */
  1959. /**
  1960. * Pushes payment action for some processed salary job
  1961. *
  1962. * @param int $jobid
  1963. *
  1964. * @return void
  1965. */
  1966. protected function pushPaid($jobid) {
  1967. $jobid = vf($jobid, 3);
  1968. $date = curdatetime();
  1969. if (isset($this->allJobs[$jobid])) {
  1970. $jobData = $this->allJobs[$jobid];
  1971. if ($jobData['state'] == 0) {
  1972. $cash = $this->getJobPrice($jobid);
  1973. $employeeid = $jobData['employeeid'];
  1974. $query = "INSERT INTO `salary_paid` (`id`, `jobid`, `employeeid`, `paid`, `date`) VALUES (NULL, '" . $jobid . "', '" . $employeeid . "', '" . $cash . "', '" . $date . "');";
  1975. nr_query($query);
  1976. } else {
  1977. log_register('SALARY JOB PROCESSING FAIL [' . $jobid . '] DUPLICATE');
  1978. }
  1979. } else {
  1980. log_register('SALARY JOB PROCESSING FAIL [' . $jobid . '] NOT_EXIST');
  1981. }
  1982. }
  1983. /**
  1984. * Returns paid Data for some paid job
  1985. *
  1986. * @param int $jobid
  1987. *
  1988. * @return array
  1989. */
  1990. protected function getPaidData($jobid) {
  1991. $result = array();
  1992. if (isset($this->allPaid[$jobid])) {
  1993. $result = $this->allPaid[$jobid];
  1994. }
  1995. return ($result);
  1996. }
  1997. /**
  1998. * Returns some human-readable paid indication
  1999. *
  2000. * @param int $jobid
  2001. *
  2002. * @return string
  2003. */
  2004. protected function renderPaidDataLed($jobid) {
  2005. $result = '';
  2006. if (isset($this->allJobs[$jobid])) {
  2007. if ($this->allJobs[$jobid]['state']) {
  2008. $paidData = $this->getPaidData($jobid);
  2009. if (!empty($paidData)) {
  2010. $title = $paidData['paid'] . ' ' . __('money') . ' - ' . @$this->allEmployee[$paidData['employeeid']] . ', ' . $paidData['date'];
  2011. $result = wf_tag('abbr', false, '', 'title="' . $title . '"') . web_bool_led($this->allJobs[$jobid]['state']) . wf_tag('abbr', true);
  2012. } else {
  2013. $result = wf_img('skins/yellow_led.png');
  2014. }
  2015. } else {
  2016. $result = web_bool_led(0);
  2017. }
  2018. }
  2019. return ($result);
  2020. }
  2021. /**
  2022. * shows printable report content
  2023. *
  2024. * @param $title report title
  2025. * @param $data report data to printable transform
  2026. *
  2027. * @return void
  2028. */
  2029. public function reportPrintable($title, $data) {
  2030. $style = file_get_contents(CONFIG_PATH . "ukvprintable.css");
  2031. $header = wf_tag('!DOCTYPE', false, '', 'html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"');
  2032. $header .= wf_tag('html', false, '', 'xmlns="http://www.w3.org/1999/xhtml" xml:lang="ru" lang="ru"');
  2033. $header .= wf_tag('head', false);
  2034. $header .= wf_tag('title') . $title . wf_tag('title', true);
  2035. $header .= wf_tag('meta', false, '', 'http-equiv="Content-Type" content="text/html; charset=UTF-8" /');
  2036. $header .= wf_tag('style', false, '', 'type="text/css"');
  2037. $header .= $style;
  2038. $header .= wf_tag('style', true);
  2039. $header .= wf_tag('script', false, '', 'src="modules/jsc/sorttable.js" language="javascript"') . wf_tag('script', true);
  2040. $header .= wf_tag('head', true);
  2041. $header .= wf_tag('body', false);
  2042. $footer = wf_tag('body', true);
  2043. $footer .= wf_tag('html', true);
  2044. $title = (!empty($title)) ? wf_tag('h2') . $title . wf_tag('h2', true) : '';
  2045. $data = $header . $title . $data . $footer;
  2046. $payedIconMask = web_bool_led(1);
  2047. $unpayedIconMask = web_bool_led(0);
  2048. $submitInputMask = wf_Submit(__('Processing'), 'payrollsubmitid');
  2049. $data = str_replace($payedIconMask, __('Paid'), $data);
  2050. $data = str_replace($unpayedIconMask, __('Not paid'), $data);
  2051. $data = str_replace($submitInputMask, '', $data);
  2052. die($data);
  2053. }
  2054. /**
  2055. * Renders timesheet create form
  2056. *
  2057. * @return string
  2058. */
  2059. public function timesheetCreateForm() {
  2060. $result = '';
  2061. if (!empty($this->allEmployee)) {
  2062. $result .= '<!--ugly hack to prevent datepicker autoopen --> <input type="text" name="shittyhack" style="width: 0; height: 0; top: -100px; position: absolute;"/>';
  2063. $result .= wf_HiddenInput('newtimesheet', 'true');
  2064. $result .= wf_DatePickerPreset('newtimesheetdate', curdate(), false);
  2065. $headers = wf_TableCell(__('Worker'));
  2066. $headers .= wf_TableCell(__('Hours'));
  2067. $headers .= wf_TableCell(__('Hospitalized'));
  2068. $headers .= wf_TableCell(__('Holidays'));
  2069. $rows = wf_TableRow($headers, 'row1');
  2070. foreach ($this->allEmployee as $employeeid => $employeename) {
  2071. if ($this->checkEmployeeWage($employeeid)) {
  2072. $defaultWorkTime = (isset($this->allWages[$employeeid]['worktime'])) ? $this->allWages[$employeeid]['worktime'] : 0;
  2073. $cells = wf_TableCell($employeename);
  2074. $cells .= wf_TableCell(wf_TextInput('_employeehours[' . $employeeid . ']', '', $defaultWorkTime, false, '2'));
  2075. $cells .= wf_TableCell(wf_CheckInput('_hospital[' . $employeeid . ']', '', false, false));
  2076. $cells .= wf_TableCell(wf_CheckInput('_holidays[' . $employeeid . ']', '', false, false));
  2077. $rows .= wf_TableRow($cells, 'row3');
  2078. }
  2079. }
  2080. $result .= wf_TableBody($rows, '100%', '0', '');
  2081. $result .= wf_tag('br', false);
  2082. $result .= wf_Submit(__('Create'));
  2083. $result = wf_Form('', 'POST', $result, '');
  2084. }
  2085. return ($result);
  2086. }
  2087. /**
  2088. * Checks is timesheet protected?
  2089. *
  2090. * @param string $date
  2091. * @return bool
  2092. */
  2093. protected function timesheetProtected($date) {
  2094. if (isset($this->allTimesheetDates[$date])) {
  2095. $result = true;
  2096. } else {
  2097. $result = false;
  2098. }
  2099. return ($result);
  2100. }
  2101. /**
  2102. * Creates new timesheet if date is unique
  2103. *
  2104. * @return int
  2105. */
  2106. public function timesheetCreate() {
  2107. $result = 0;
  2108. if (wf_CheckPost(array('newtimesheet', 'newtimesheetdate', '_employeehours'))) {
  2109. $date = $_POST['newtimesheetdate'];
  2110. $dateF = mysql_real_escape_string($_POST['newtimesheetdate']);
  2111. if (!$this->timesheetProtected($date)) {
  2112. $counter = 0;
  2113. $employeeHours = $_POST['_employeehours'];
  2114. $hospitalArr = (isset($_POST['_hospital'])) ? $_POST['_hospital'] : array();
  2115. $holidaysArr = (isset($_POST['_holidays'])) ? $_POST['_holidays'] : array();
  2116. if (!empty($employeeHours)) {
  2117. foreach ($employeeHours as $employeeId => $hours) {
  2118. $hospitalFlag = (isset($hospitalArr[$employeeId])) ? 1 : 0;
  2119. $holidaysFlag = (isset($holidaysArr[$employeeId])) ? 1 : 0;
  2120. $query = "INSERT INTO `salary_timesheets` (`id`,`date`,`employeeid`,`hours`,`holiday`,`hospital`) VALUES "
  2121. . "(NULL, '" . $dateF . "','" . $employeeId . "','" . $hours . "','" . $holidaysFlag . "','" . $hospitalFlag . "');";
  2122. nr_query($query);
  2123. }
  2124. }
  2125. log_register('SALARY CREATE TIMESHEET EMPLOYEECOUNT `' . $counter . '`');
  2126. } else {
  2127. $result = 1;
  2128. log_register('SALARY CREATE TIMESHEET FAIL EXISTING DATE `' . $date . '`');
  2129. }
  2130. }
  2131. return ($result);
  2132. }
  2133. /**
  2134. * Renders list of timesheets
  2135. *
  2136. * @param string $baseUrl alternative link destination for viewing timesheet
  2137. *
  2138. * @return string
  2139. */
  2140. public function timesheetsListRender($baseUrl = '') {
  2141. $result = '';
  2142. $tableData = new wf_JqDtHelper();
  2143. $linkBase = (empty($baseUrl)) ? self::URL_ME . '&' . self::URL_TSHEETS : $baseUrl;
  2144. $columns = array(__('Date'), __('Rows'));
  2145. if (wf_CheckGet(array('ajaxtimesheetsdata'))) {
  2146. if (!empty($this->allTimesheetDates)) {
  2147. foreach ($this->allTimesheetDates as $date => $count) {
  2148. $rawData[] = wf_Link($linkBase . '&showdate=' . $date, $date);
  2149. $rawData[] = $count;
  2150. $tableData->addRow($rawData);
  2151. unset($rawData);
  2152. }
  2153. }
  2154. $tableData->getJson();
  2155. } else {
  2156. $opts = '"order": [[ 0, "desc" ]]';
  2157. $result = wf_JqDtLoader($columns, $linkBase . '&ajaxtimesheetsdata=true', false, __('Timesheets'), 100, $opts);
  2158. }
  2159. return ($result);
  2160. }
  2161. /**
  2162. * Returns array of timesheet records filtered by date
  2163. *
  2164. * @param string $date
  2165. *
  2166. * @return array
  2167. */
  2168. protected function timesheetFilterDate($date) {
  2169. $result = array();
  2170. if (!empty($this->allTimesheets)) {
  2171. foreach ($this->allTimesheets as $io => $each) {
  2172. if ($each['date'] == $date) {
  2173. $result[$each['id']] = $each;
  2174. }
  2175. }
  2176. }
  2177. return ($result);
  2178. }
  2179. /**
  2180. * Returns array of timesheet records filtered by Year/month in MySQL date format Y-m
  2181. *
  2182. * @param string $yearMonth
  2183. *
  2184. * @return array
  2185. */
  2186. protected function timesheetFilterMonth($yearMonth) {
  2187. $result = array();
  2188. if (!empty($this->allTimesheets)) {
  2189. foreach ($this->allTimesheets as $io => $each) {
  2190. if (ispos($each['date'], $yearMonth)) {
  2191. $result[$each['id']] = $each;
  2192. }
  2193. }
  2194. }
  2195. return ($result);
  2196. }
  2197. /**
  2198. * Renders timesheet editing form
  2199. *
  2200. * @param string $timesheetDate
  2201. *
  2202. * @return string
  2203. */
  2204. public function timesheetEditForm($timesheetDate) {
  2205. $result = '';
  2206. $timesheetData = $this->timesheetFilterDate($timesheetDate);
  2207. if (!empty($timesheetData)) {
  2208. $headers = wf_TableCell(__('Worker'));
  2209. $headers .= wf_TableCell(__('Hours'));
  2210. $headers .= wf_TableCell(__('Hospitalized'));
  2211. $headers .= wf_TableCell(__('Holidays'));
  2212. $headers .= wf_TableCell(__('Actions'));
  2213. $rows = wf_TableRow($headers, 'row1');
  2214. foreach ($timesheetData as $io => $each) {
  2215. $hospitalFlag = ($each['hospital']) ? true : false;
  2216. $holidayFlag = ($each['holiday']) ? true : false;
  2217. $cells = wf_TableCell(@$this->allEmployeeRaw[$each['employeeid']]);
  2218. $cells .= wf_TableCell(wf_TextInput('editemployeehours', '', $each['hours'], false, '2'));
  2219. $cells .= wf_TableCell(wf_CheckInput('edithospital', '', false, $hospitalFlag));
  2220. $cells .= wf_TableCell(wf_CheckInput('editholiday', '', false, $holidayFlag));
  2221. $cells .= wf_TableCell(wf_HiddenInput('edittimesheetid', $each['id']) . wf_Submit(__('Save')));
  2222. $cells = wf_Form('', 'POST', $cells, '');
  2223. $rows .= wf_TableRow($cells, 'row3');
  2224. }
  2225. $result .= wf_TableBody($rows, '100%', '0', '');
  2226. $result .= wf_tag('br', false);
  2227. }
  2228. return ($result);
  2229. }
  2230. /**
  2231. * Saves timesheet editing results into database
  2232. *
  2233. * @return void
  2234. */
  2235. public function timesheetSaveChanges() {
  2236. if (wf_CheckPost(array('edittimesheetid'))) {
  2237. $id = vf($_POST['edittimesheetid'], 3);
  2238. if (isset($this->allTimesheets[$id])) {
  2239. $timesheetData = $this->allTimesheets[$id];
  2240. $timesheetDate = $timesheetData['date'];
  2241. $employee = $timesheetData['employeeid'];
  2242. $hours = vf($_POST['editemployeehours'], 3);
  2243. $hospitalFlag = (wf_CheckPost(array('edithospital'))) ? 1 : 0;
  2244. $holidayFlag = (wf_CheckPost(array('editholiday'))) ? 1 : 0;
  2245. $where = " WHERE `id`='" . $id . "';";
  2246. if ($timesheetData['hours'] != $hours) {
  2247. simple_update_field('salary_timesheets', 'hours', $hours, $where);
  2248. }
  2249. if ($timesheetData['holiday'] != $holidayFlag) {
  2250. simple_update_field('salary_timesheets', 'holiday', $holidayFlag, $where);
  2251. }
  2252. if ($timesheetData['hospital'] != $hospitalFlag) {
  2253. simple_update_field('salary_timesheets', 'hospital', $hospitalFlag, $where);
  2254. }
  2255. log_register('SALARY EDIT TIMESHEET [' . $id . '] `' . $timesheetDate . '` EMPLOYEE [' . $employee . '] HOURS `' . $hours . '` HOSPITAL `' . $hospitalFlag . '` HOLIDAY `' . $holidayFlag . '`');
  2256. } else {
  2257. log_register('SALARY EDIT FAIL TIMESHEET [' . $id . '] NOT_EXISTING_ID');
  2258. }
  2259. }
  2260. }
  2261. /**
  2262. * Returns
  2263. *
  2264. * @return string
  2265. */
  2266. public function timesheetRenderPrintableForm() {
  2267. $result = '';
  2268. $inputs = wf_YearSelector('tsheetprintyear', __('Year') . ' ', false);
  2269. $inputs .= wf_MonthSelector('tsheetprintmonth', __('Month') . ' ', date('m'), false);
  2270. $inputs .= wf_Submit(__('Show'));
  2271. $result = wf_Form('', 'POST', $inputs, 'glamour', '', '', '_BLANK');
  2272. return ($result);
  2273. }
  2274. /**
  2275. * Renders printable timesheets by some month
  2276. *
  2277. * @param int $year
  2278. * @param string $month
  2279. *
  2280. * @return string
  2281. */
  2282. public function timesheetRenderPrintable($year, $month) {
  2283. $result = '';
  2284. $dateOffset = $year . '-' . $month;
  2285. $allTimesheets = $this->timesheetFilterMonth($dateOffset);
  2286. $this->loadAppointments();
  2287. $tmpArr = array();
  2288. $cells = wf_TableCell(__('Worker'));
  2289. $cells .= wf_TableCell(__('Appointment'));
  2290. for ($i = 1; $i <= 31; $i++) {
  2291. $dayCellHeader = ($i < 10) ? '0' . $i : $i;
  2292. $cells .= wf_TableCell($dayCellHeader);
  2293. }
  2294. $cells .= wf_TableCell(__('Total') . ' ' . __('days'));
  2295. $cells .= wf_TableCell(__('Holidays'));
  2296. $cells .= wf_TableCell(__('Total') . ' ' . __('hours'));
  2297. $rows = wf_TableRow($cells, 'row1');
  2298. if (!empty($allTimesheets)) {
  2299. foreach ($allTimesheets as $io => $each) {
  2300. $timestamp = strtotime($each['date']);
  2301. $dayNum = date('j', $timestamp);
  2302. if (!isset($tmpArr[$each['employeeid']])) {
  2303. $tmpArr[$each['employeeid']]['totalhours'] = $each['hours'];
  2304. $tmpArr[$each['employeeid']]['holidays'] = $each['holiday'];
  2305. $tmpArr[$each['employeeid']]['hospital'] = $each['hospital'];
  2306. if ($each['hours'] != 0) {
  2307. $tmpArr[$each['employeeid']]['totaldays'] = 1;
  2308. } else {
  2309. $tmpArr[$each['employeeid']]['totaldays'] = 0;
  2310. }
  2311. $tmpArr[$each['employeeid']]['day_' . $dayNum] = $each['hours'];
  2312. if ($each['hospital']) {
  2313. $tmpArr[$each['employeeid']]['dayhospital_' . $dayNum] = 1;
  2314. } else {
  2315. $tmpArr[$each['employeeid']]['dayhospital_' . $dayNum] = 0;
  2316. }
  2317. if ($each['holiday']) {
  2318. $tmpArr[$each['employeeid']]['dayholiday_' . $dayNum] = 1;
  2319. } else {
  2320. $tmpArr[$each['employeeid']]['dayholiday_' . $dayNum] = 0;
  2321. }
  2322. } else {
  2323. $tmpArr[$each['employeeid']]['totalhours'] += $each['hours'];
  2324. $tmpArr[$each['employeeid']]['holidays'] += $each['holiday'];
  2325. $tmpArr[$each['employeeid']]['hospital'] += $each['hospital'];
  2326. if ($each['hours'] != 0) {
  2327. $tmpArr[$each['employeeid']]['totaldays']++;
  2328. }
  2329. $tmpArr[$each['employeeid']]['day_' . $dayNum] = $each['hours'];
  2330. if ($each['hospital']) {
  2331. $tmpArr[$each['employeeid']]['dayhospital_' . $dayNum] = 1;
  2332. } else {
  2333. $tmpArr[$each['employeeid']]['dayhospital_' . $dayNum] = 0;
  2334. }
  2335. if ($each['holiday']) {
  2336. $tmpArr[$each['employeeid']]['dayholiday_' . $dayNum] = 1;
  2337. } else {
  2338. $tmpArr[$each['employeeid']]['dayholiday_' . $dayNum] = 0;
  2339. }
  2340. }
  2341. }
  2342. }
  2343. // print_r($tmpArr);
  2344. if (!empty($tmpArr)) {
  2345. foreach ($tmpArr as $employeeid => $each) {
  2346. $cells = wf_TableCell(@$this->allEmployeeRaw[$employeeid]);
  2347. $cells .= wf_TableCell(@$this->allAppointments[$employeeid]);
  2348. for ($i = 1; $i <= 31; $i++) {
  2349. $dayCell = (isset($each['day_' . $i])) ? $each['day_' . $i] : 0;
  2350. $dayCellSuffix = '';
  2351. if (@$each['dayholiday_' . $i]) {
  2352. $dayCellSuffix .= wf_tag('sup') . 'v' . wf_tag('sup', true);
  2353. }
  2354. if (@$each['dayhospital_' . $i]) {
  2355. $dayCellSuffix .= wf_tag('sup') . 'h' . wf_tag('sup', true);
  2356. }
  2357. $cells .= wf_TableCell($dayCell . $dayCellSuffix);
  2358. }
  2359. $cells .= wf_TableCell($each['totaldays']);
  2360. $cells .= wf_TableCell($each['holidays']);
  2361. $cells .= wf_TableCell($each['totalhours']);
  2362. $rows .= wf_TableRow($cells, 'row3');
  2363. }
  2364. }
  2365. $result .= wf_TableBody($rows, '100%', 0, 'sortable');
  2366. $result .= 'v - ' . __('Holidays') . wf_tag('br');
  2367. $result .= 'h - ' . __('Hospitalized');
  2368. $result = $this->reportPrintable(__('Timesheets') . ' ' . $dateOffset, $result);
  2369. return ($result);
  2370. }
  2371. /**
  2372. * Counts percentage between two values
  2373. *
  2374. * @param float $valueTotal
  2375. * @param float $value
  2376. *
  2377. * @return float
  2378. */
  2379. protected function percentValue($valueTotal, $value) {
  2380. $result = 0;
  2381. if ($valueTotal != 0) {
  2382. $result = round((($value * 100) / $valueTotal), 2);
  2383. }
  2384. return ($result);
  2385. }
  2386. /**
  2387. * Renders time duration in seconds into formatted human-readable view
  2388. *
  2389. * @param int $seconds
  2390. *
  2391. * @return string
  2392. */
  2393. protected function formatTime($seconds) {
  2394. $init = $seconds;
  2395. $hours = floor($seconds / 3600);
  2396. $minutes = floor(($seconds / 60) % 60);
  2397. $seconds = $seconds % 60;
  2398. if ($init < 3600) {
  2399. //less than 1 hour
  2400. if ($init < 60) {
  2401. //less than minute
  2402. $result = $seconds . ' ' . __('sec.');
  2403. } else {
  2404. //more than one minute
  2405. $result = $minutes . ' ' . __('minutes');
  2406. }
  2407. } else {
  2408. //more than hour
  2409. $result = $hours . ' ' . __('hour') . ' ' . $minutes . ' ' . __('minutes');
  2410. }
  2411. return ($result);
  2412. }
  2413. /**
  2414. * Returns labor time search form
  2415. *
  2416. * @return string
  2417. */
  2418. public function ltReportRenderForm() {
  2419. $result = '';
  2420. //getting previous state
  2421. $curdateFrom = (wf_CheckPost(array('datefrom'))) ? $_POST['datefrom'] : curdate();
  2422. $curdateTo = (wf_CheckPost(array('dateto'))) ? $_POST['dateto'] : curdate();
  2423. $curJobTypeId = (wf_CheckPost(array('jobtypeid'))) ? $_POST['jobtypeid'] : '-';
  2424. $inputs = wf_DatePickerPreset('datefrom', $curdateFrom) . ' ';
  2425. $inputs .= wf_DatePickerPreset('dateto', $curdateTo) . ' ';
  2426. $jobtypeSelectorParams = array('-' => __('Any'));
  2427. if (!empty($this->allJobtypes)) {
  2428. foreach ($this->allJobtypes as $io => $each) {
  2429. $jobtypeSelectorParams[$io] = $each;
  2430. }
  2431. }
  2432. $inputs .= wf_Selector('jobtypeid', $jobtypeSelectorParams, __('Job type'), $curJobTypeId, false);
  2433. $inputs .= wf_Submit(__('Show'));
  2434. $result = wf_Form('', 'POST', $inputs, 'glamour');
  2435. return ($result);
  2436. }
  2437. /**
  2438. * Renders labor time report results
  2439. *
  2440. * @return string
  2441. */
  2442. function ltReportRenderResults() {
  2443. $result = '';
  2444. if (wf_CheckPost(array('datefrom', 'dateto', 'jobtypeid'))) {
  2445. $messages = new UbillingMessageHelper();
  2446. $dateFrom = mysql_real_escape_string($_POST['datefrom']) . ' 00:00:00';
  2447. $dateTo = mysql_real_escape_string($_POST['dateto']) . ' 23:59:59';
  2448. $fromTimestamp = strtotime($dateFrom);
  2449. $toTimestamp = strtotime($dateTo);
  2450. $jobtypeId = mysql_real_escape_string($_POST['jobtypeid']);
  2451. //any job type
  2452. if ($jobtypeId == '-') {
  2453. $employeeJobsTmp = array();
  2454. if (!empty($this->allEmployee)) {
  2455. foreach ($this->allEmployee as $employeeId => $employeeName) {
  2456. if ($this->checkEmployeeWage($employeeId)) {
  2457. if (!empty($this->allJobs)) {
  2458. foreach ($this->allJobs as $jobId => $jobData) {
  2459. if ($jobData['employeeid'] == $employeeId) {
  2460. $jobTimestamp = strtotime($jobData['date']);
  2461. if (($jobTimestamp >= $fromTimestamp) and ($jobTimestamp <= $toTimestamp)) {
  2462. if (isset($employeeJobsTmp[$employeeId])) {
  2463. $jobFactor = $jobData['factor'];
  2464. $jobMinutes = @$this->allJobTimes[$jobData['jobtypeid']];
  2465. $jobTimeSpent = $jobFactor * $jobMinutes;
  2466. $employeeJobsTmp[$employeeId]['timespent'] += $jobTimeSpent;
  2467. $employeeJobsTmp[$employeeId]['timesheet'] = 0;
  2468. } else {
  2469. $jobFactor = $jobData['factor'];
  2470. $jobMinutes = @$this->allJobTimes[$jobData['jobtypeid']];
  2471. $jobTimeSpent = $jobFactor * $jobMinutes;
  2472. $employeeJobsTmp[$employeeId]['timespent'] = $jobTimeSpent;
  2473. $employeeJobsTmp[$employeeId]['timesheet'] = 0;
  2474. }
  2475. }
  2476. }
  2477. }
  2478. }
  2479. if (!empty($this->allTimesheets)) {
  2480. foreach ($this->allTimesheets as $timesheetId => $eachTimesheetData) {
  2481. if ($employeeId == $eachTimesheetData['employeeid']) {
  2482. $timeSheetTimestamp = strtotime($eachTimesheetData['date']);
  2483. if (($timeSheetTimestamp >= $fromTimestamp) and ($timeSheetTimestamp <= $toTimestamp)) {
  2484. if (isset($employeeJobsTmp[$employeeId])) {
  2485. $employeeJobsTmp[$employeeId]['timesheet'] += ($eachTimesheetData['hours'] * 60);
  2486. } else {
  2487. $employeeJobsTmp[$employeeId]['timesheet'] = ($eachTimesheetData['hours'] * 60);
  2488. }
  2489. }
  2490. }
  2491. }
  2492. }
  2493. }
  2494. }
  2495. }
  2496. if (!empty($employeeJobsTmp)) {
  2497. $cells = wf_TableCell(__('Employee'));
  2498. $cells .= wf_TableCell(__('Job type'));
  2499. $cells .= wf_TableCell(__('Timesheet') . ' (' . __('hours') . ')');
  2500. $cells .= wf_TableCell(__('Spent time'));
  2501. $rows = wf_TableRow($cells, 'row1');
  2502. foreach ($employeeJobsTmp as $io => $each) {
  2503. $cells = wf_TableCell(@$this->allEmployee[$io]);
  2504. $cells .= wf_TableCell(__('Any'));
  2505. $cells .= wf_TableCell(@$each['timesheet'] / 60);
  2506. $cells .= wf_TableCell(@$this->formatTime(@$each['timespent'] * 60) . ' (' . @$this->percentValue($each['timesheet'], $each['timespent']) . '%)');
  2507. $rows .= wf_TableRow($cells, 'row3');
  2508. }
  2509. $result = wf_TableBody($rows, '100%', 0, 'sortable');
  2510. } else {
  2511. $result = $messages->getStyledMessage(__('Nothing found'), 'info');
  2512. }
  2513. } else {
  2514. //some other job types
  2515. $employeeJobsTmp = array();
  2516. $totalTimeSpent = 0;
  2517. $chartData = array();
  2518. if (!empty($this->allEmployee)) {
  2519. foreach ($this->allEmployee as $employeeId => $employeeName) {
  2520. if ($this->checkEmployeeWage($employeeId)) {
  2521. if (!empty($this->allJobs)) {
  2522. foreach ($this->allJobs as $jobId => $jobData) {
  2523. if ($jobData['jobtypeid'] == $jobtypeId) {
  2524. if ($jobData['employeeid'] == $employeeId) {
  2525. $jobTimestamp = strtotime($jobData['date']);
  2526. if (($jobTimestamp >= $fromTimestamp) and ($jobTimestamp <= $toTimestamp)) {
  2527. if (isset($employeeJobsTmp[$employeeId])) {
  2528. $jobFactor = $jobData['factor'];
  2529. $jobMinutes = @$this->allJobTimes[$jobData['jobtypeid']];
  2530. $jobTimeSpent = $jobFactor * $jobMinutes;
  2531. $employeeJobsTmp[$employeeId]['timespent'] += $jobTimeSpent;
  2532. } else {
  2533. $jobFactor = $jobData['factor'];
  2534. $jobMinutes = @$this->allJobTimes[$jobData['jobtypeid']];
  2535. $jobTimeSpent = $jobFactor * $jobMinutes;
  2536. $employeeJobsTmp[$employeeId]['timespent'] = $jobTimeSpent;
  2537. }
  2538. $totalTimeSpent += $jobTimeSpent;
  2539. }
  2540. }
  2541. }
  2542. }
  2543. }
  2544. }
  2545. }
  2546. }
  2547. if (!empty($employeeJobsTmp)) {
  2548. $cells = wf_TableCell(__('Employee'));
  2549. $cells .= wf_TableCell(__('Job type'));
  2550. $cells .= wf_TableCell(__('Spent time') . ' (' . __('hours') . ')');
  2551. $cells .= wf_TableCell(__('Percent of spent time'));
  2552. $rows = wf_TableRow($cells, 'row1');
  2553. foreach ($employeeJobsTmp as $io => $each) {
  2554. $cells = wf_TableCell(@$this->allEmployee[$io]);
  2555. $cells .= wf_TableCell(@$this->allJobtypes[$jobtypeId]);
  2556. $cells .= wf_TableCell(@$this->formatTime(@$each['timespent'] * 60));
  2557. $cells .= wf_TableCell(@$this->percentValue($totalTimeSpent, $each['timespent']) . '%');
  2558. $rows .= wf_TableRow($cells, 'row3');
  2559. //chart data
  2560. $chartData[$this->allEmployee[$io]] = $each['timespent'];
  2561. }
  2562. $result = wf_TableBody($rows, '100%', 0, 'sortable');
  2563. if (!empty($chartData)) {
  2564. $chartOptions = '';
  2565. $result .= wf_gcharts3DPie($chartData, __('Stats') . ' ' . @$this->allJobtypes[$jobtypeId], '800px', '500px', $chartOptions);
  2566. }
  2567. } else {
  2568. $result = $messages->getStyledMessage(__('Nothing found'), 'info');
  2569. }
  2570. }
  2571. }
  2572. return ($result);
  2573. }
  2574. /**
  2575. * Renders per year salary report
  2576. *
  2577. * @return string
  2578. */
  2579. public function renderYearReport() {
  2580. $result = '';
  2581. $monthArr = months_array_localized();
  2582. $showYear = (ubRouting::checkPost('showyear')) ? ubRouting::post('showyear', 'int') : curyear();
  2583. $yearSummaryArr = array();
  2584. $employeSummaryArr = array();
  2585. $jobTypesSummaryArr = array();
  2586. $totalJobPrices = 0;
  2587. foreach ($monthArr as $monthNum => $monthName) {
  2588. $yearSummaryArr[$monthNum]['monthname'] = $monthName;
  2589. $yearSummaryArr[$monthNum]['paid'] = 0;
  2590. $yearSummaryArr[$monthNum]['unpaid'] = 0;
  2591. $yearSummaryArr[$monthNum]['total'] = 0;
  2592. $yearSummaryArr[$monthNum]['jobscount'] = 0;
  2593. }
  2594. $inputs = wf_YearSelectorPreset('showyear', __('Year'), false, $showYear) . ' ';
  2595. $inputs .= wf_Submit(__('Show'));
  2596. $result .= wf_Form('', 'POST', $inputs, 'glamour');
  2597. $result .= wf_delimiter();
  2598. if (!empty($this->allJobs)) {
  2599. //debarr($this->allJobs);
  2600. foreach ($this->allJobs as $io => $each) {
  2601. $timestamp = strtotime($each['date']);
  2602. $year = date("Y", $timestamp);
  2603. if ($year == $showYear) {
  2604. $month = date("m", $timestamp);
  2605. $jobPrice = $this->getJobPrice($each['id']);
  2606. ///filling year summary report
  2607. $jobPaid = ($each['state']) ? true : false;
  2608. if ($jobPaid) {
  2609. $yearSummaryArr[$month]['paid'] += $jobPrice;
  2610. } else {
  2611. $yearSummaryArr[$month]['unpaid'] += $jobPrice;
  2612. }
  2613. $yearSummaryArr[$month]['total'] += $jobPrice;
  2614. $yearSummaryArr[$month]['jobscount']++;
  2615. $totalJobPrices += $jobPrice;
  2616. //filling employee summary
  2617. if (isset($employeSummaryArr[$each['employeeid']])) {
  2618. $employeSummaryArr[$each['employeeid']][$month] += $jobPrice;
  2619. } else {
  2620. foreach ($monthArr as $monthNum => $monthName) {
  2621. $employeSummaryArr[$each['employeeid']][$monthNum] = 0;
  2622. }
  2623. $employeSummaryArr[$each['employeeid']][$month] += $jobPrice;
  2624. }
  2625. //filling jobtypes summary
  2626. if (isset($jobTypesSummaryArr[$each['jobtypeid']][$month])) {
  2627. $jobTypesSummaryArr[$each['jobtypeid']][$month] += $jobPrice;
  2628. } else {
  2629. foreach ($monthArr as $monthNum => $monthName) {
  2630. $jobTypesSummaryArr[$each['jobtypeid']][$monthNum] = 0;
  2631. }
  2632. $jobTypesSummaryArr[$each['jobtypeid']][$month] = $jobPrice;
  2633. }
  2634. }
  2635. }
  2636. //rendering year summary report
  2637. if (!empty($yearSummaryArr)) {
  2638. $result .= wf_tag('h3') . __('Employee wages') . ' ' . $showYear . wf_tag('h3', true);
  2639. $cells = wf_TableCell('');
  2640. $cells .= wf_TableCell(__('Month'));
  2641. $cells .= wf_TableCell(__('Jobs'));
  2642. $cells .= wf_TableCell(__('Paid'));
  2643. $cells .= wf_TableCell(__('Unpaid'));
  2644. $cells .= wf_TableCell(__('Total money'));
  2645. $cells .= wf_TableCell(__('Visual'), '50%');
  2646. $rows = wf_TableRow($cells, 'row1');
  2647. foreach ($yearSummaryArr as $io => $each) {
  2648. $cells = wf_TableCell($io);
  2649. $cells .= wf_TableCell($each['monthname']);
  2650. $cells .= wf_TableCell($each['jobscount']);
  2651. $cells .= wf_TableCell(zb_CashBigValueFormat($each['paid']));
  2652. $cells .= wf_TableCell(zb_CashBigValueFormat($each['unpaid']));
  2653. $cells .= wf_TableCell(zb_CashBigValueFormat($each['total']));
  2654. $cells .= wf_TableCell(web_bar($each['total'], $totalJobPrices), '', '', 'sorttable_customkey="' . $each['total'] . '"');
  2655. $rows .= wf_TableRow($cells, 'row3');
  2656. }
  2657. $result .= wf_TableBody($rows, '100%', 0, 'sortable');
  2658. }
  2659. //rendering per employee year report
  2660. if (!empty($employeSummaryArr)) {
  2661. $cells = wf_TableCell('');
  2662. foreach ($monthArr as $monthNum => $monthName) {
  2663. $cells .= wf_TableCell($monthName);
  2664. }
  2665. $cells .= wf_TableCell(__('Total'));
  2666. $rows = wf_TableRow($cells, 'row1');
  2667. foreach ($employeSummaryArr as $employeeId => $each) {
  2668. $employeeSalaryTotal = 0;
  2669. $cells = wf_TableCell(@$this->allEmployeeRaw[$employeeId]);
  2670. foreach ($monthArr as $ia => $mn) {
  2671. $cells .= wf_TableCell(zb_CashBigValueFormat($each[$ia]));
  2672. $employeeSalaryTotal += $each[$ia];
  2673. }
  2674. $cells .= wf_TableCell(zb_CashBigValueFormat($employeeSalaryTotal));
  2675. $rows .= wf_TableRow($cells, 'row5');
  2676. }
  2677. $result .= wf_tag('h3') . __('Employee') . wf_tag('h3', true);
  2678. $result .= wf_TableBody($rows, '100%', 0, 'sortable');
  2679. }
  2680. //rendering jobtypes year summary
  2681. if (!empty($jobTypesSummaryArr)) {
  2682. $cells = wf_TableCell('');
  2683. foreach ($monthArr as $monthNum => $monthName) {
  2684. $cells .= wf_TableCell($monthName);
  2685. }
  2686. $cells .= wf_TableCell(__('Total'));
  2687. $rows = wf_TableRow($cells, 'row1');
  2688. foreach ($jobTypesSummaryArr as $jobtypeId => $each) {
  2689. $jobTypePriceTotal = 0;
  2690. $jobName = (isset($this->allJobtypes[$jobtypeId])) ? $this->allJobtypes[$jobtypeId] : __('Job type') . ' ' . __('Deleted');
  2691. $cells = wf_TableCell($jobName);
  2692. foreach ($monthArr as $ia => $mn) {
  2693. $cells .= wf_TableCell(zb_CashBigValueFormat($each[$ia]));
  2694. $jobTypePriceTotal += $each[$ia];
  2695. }
  2696. $cells .= wf_TableCell(zb_CashBigValueFormat($jobTypePriceTotal));
  2697. $rows .= wf_TableRow($cells, 'row5');
  2698. }
  2699. $result .= wf_tag('h3') . __('Job types') . wf_tag('h3', true);
  2700. $result .= wf_TableBody($rows, '100%', 0, 'sortable');
  2701. //and visual charts for jobtypes
  2702. $chartsOptions = "
  2703. 'focusTarget': 'category',
  2704. 'hAxis': {
  2705. 'color': 'none',
  2706. 'baselineColor': 'none',
  2707. },
  2708. 'vAxis': {
  2709. 'color': 'none',
  2710. 'baselineColor': 'none',
  2711. },
  2712. 'curveType': 'function',
  2713. 'pointSize': 5,
  2714. 'crosshair': {
  2715. trigger: 'none'
  2716. },
  2717. legend: { position: 'right', orientation: 'vertical', aligment: 'end' },";
  2718. $charsData = array();
  2719. $columns = array();
  2720. $columns[] = __('Date');
  2721. $jobsInYear = array();
  2722. foreach ($jobTypesSummaryArr as $jobTypeId => $each) {
  2723. $jobName = (isset($this->allJobtypes[$jobTypeId])) ? $this->allJobtypes[$jobTypeId] : __('Deleted');
  2724. $columns[] = $jobName;
  2725. foreach ($each as $mn => $jtsumm) {
  2726. $jobsInYear[$showYear . '-' . $mn][$jobTypeId] = $jtsumm;
  2727. }
  2728. }
  2729. $charsData[] = $columns;
  2730. $columns = array();
  2731. if (!empty($jobsInYear)) {
  2732. foreach ($jobsInYear as $date => $jobStats) {
  2733. $columns = array();
  2734. $columns[] = $date;
  2735. foreach ($jobStats as $jobtypeId => $jtsumm) {
  2736. $columns[] = $jtsumm;
  2737. }
  2738. $charsData[] = $columns;
  2739. }
  2740. }
  2741. $result .= wf_gchartsLine($charsData, __('Job types') . ' ' . $showYear, '100%', '400px', $chartsOptions);
  2742. }
  2743. } else {
  2744. $messages = new UbillingMessageHelper();
  2745. $result .= $messages->getStyledMessage(__('Nothing to show'), 'warning');
  2746. }
  2747. return ($result);
  2748. }
  2749. /**
  2750. * Shows salary summary report
  2751. *
  2752. * @return void
  2753. */
  2754. public function summaryReport() {
  2755. $result = '';
  2756. if ($_SERVER['QUERY_STRING'] == 'module=salary') {
  2757. $messages = new UbillingMessageHelper();
  2758. if (empty($this->allEmployee)) {
  2759. $result .= $messages->getStyledMessage(__('No available workers for wage creation'), 'error');
  2760. } else {
  2761. $result .= $messages->getStyledMessage(__('Total existing employees') . ': ' . sizeof($this->allEmployee), 'info');
  2762. }
  2763. if (empty($this->allJobtypes)) {
  2764. $result .= $messages->getStyledMessage(__('No available job types for pricing'), 'error');
  2765. } else {
  2766. $result .= $messages->getStyledMessage(__('Total existing job types') . ': ' . sizeof($this->allJobtypes), 'info');
  2767. }
  2768. if (empty($this->allJobPrices)) {
  2769. $result .= $messages->getStyledMessage(__('There is no set prices for job types'), 'warning');
  2770. } else {
  2771. $result .= $messages->getStyledMessage(__('Total paid types of work') . ': ' . sizeof($this->allJobPrices), 'info');
  2772. }
  2773. if (empty($this->allWages)) {
  2774. $result .= $messages->getStyledMessage(__('There is no set wages for workers'), 'warning');
  2775. }
  2776. if (empty($this->allJobs)) {
  2777. $result .= $messages->getStyledMessage(__('Not done yet any paid work'), 'warning');
  2778. } else {
  2779. $todayJobs = $this->jobsFilterDate(curdate());
  2780. $todayJobsCount = sizeof($todayJobs);
  2781. $monthJobs = $this->jobsFilterDate(curmonth());
  2782. $monthJobsCount = sizeof($monthJobs);
  2783. $result .= $messages->getStyledMessage(__('Today performed paid work') . ': ' . $todayJobsCount, 'success');
  2784. $result .= $messages->getStyledMessage(__('Month performed paid work') . ': ' . $monthJobsCount, 'success');
  2785. $result .= $messages->getStyledMessage(__('Total performed paid work') . ': ' . sizeof($this->allJobs), 'success');
  2786. }
  2787. if (empty($this->allTimesheetDates)) {
  2788. $result .= $messages->getStyledMessage(__('No filled timesheets'), 'warning');
  2789. } else {
  2790. if (!isset($this->allTimesheetDates[curdate()])) {
  2791. $result .= $messages->getStyledMessage(__('For today is not filled timesheets'), 'warning');
  2792. } else {
  2793. $result .= $messages->getStyledMessage(__('For today timesheets is filled'), 'success');
  2794. }
  2795. $result .= $messages->getStyledMessage(__('Filled timesheets for') . ' ' . sizeof($this->allTimesheetDates) . ' ' . __('days'), 'success');
  2796. }
  2797. if (!empty($result)) {
  2798. show_window(__('Stats'), $result);
  2799. zb_BillingStats(true);
  2800. }
  2801. }
  2802. }
  2803. }