Copybara bot | be50d49 | 2023-11-30 00:16:42 +0100 | [diff] [blame] | 1 | <?php |
Adrià Vilanova Martínez | 5af8651 | 2023-12-02 20:44:16 +0100 | [diff] [blame] | 2 | /* |
| 3 | * hores |
| 4 | * Copyright (c) 2023 Adrià Vilanova Martínez |
| 5 | * |
| 6 | * This program is free software: you can redistribute it and/or modify |
| 7 | * it under the terms of the GNU Affero General Public License as |
| 8 | * published by the Free Software Foundation, either version 3 of the |
| 9 | * License, or (at your option) any later version. |
| 10 | * |
| 11 | * This program is distributed in the hope that it will be useful, |
| 12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of |
| 13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
| 14 | * GNU Affero General Public License for more details. |
| 15 | * |
| 16 | * You should have received a copy of the GNU Affero General Public |
| 17 | * License along with this program. |
| 18 | * If not, see http://www.gnu.org/licenses/. |
| 19 | */ |
| 20 | |
Copybara bot | be50d49 | 2023-11-30 00:16:42 +0100 | [diff] [blame] | 21 | require_once("core.php"); |
| 22 | security::checkType(security::WORKER); |
| 23 | security::checkWorkerUIEnabled(); |
| 24 | |
| 25 | $isAdmin = security::isAllowed(security::ADMIN); |
| 26 | |
| 27 | $mainURL = (security::isAdminView() ? "export.php" : "export4worker.php"); |
| 28 | |
| 29 | if (!security::checkParams("GET", [ |
| 30 | ["begins", security::PARAM_ISDATE], |
| 31 | ["ends", security::PARAM_ISDATE], |
| 32 | ["workers", security::PARAM_ISARRAY], |
| 33 | ["format", security::PARAM_ISINT] |
| 34 | ])) { |
| 35 | security::go($mainURL."?msg=empty"); |
| 36 | } |
| 37 | |
| 38 | $ignoreempty = (isset($_GET["ignoreempty"]) && $_GET["ignoreempty"] == 1); |
| 39 | $labelinvalid = (isset($_GET["labelinvalid"]) && $_GET["labelinvalid"] == 1); |
| 40 | $showvalidated = (isset($_GET["showvalidated"]) && $_GET["showvalidated"] == 1); |
| 41 | $shownotvalidated = (isset($_GET["shownotvalidated"]) && $_GET["shownotvalidated"] == 1); |
| 42 | |
| 43 | $begins = new DateTime($_GET["begins"]); |
| 44 | $ends = new DateTime($_GET["ends"]); |
| 45 | $interval = new DateInterval("P1D"); |
| 46 | |
| 47 | if (!intervals::wellFormed([$begins, $ends])) { |
| 48 | security::go($mainURL."?msg=inverted"); |
| 49 | } |
| 50 | |
| 51 | $date = new DateTime(date("Y-m-d")."T00:00:00"); |
| 52 | $interval = new DateInterval("P1D"); |
| 53 | $date->sub($interval); |
| 54 | |
| 55 | if ($ends->diff($date)->invert !== 0) { |
| 56 | security::go($mainURL."?msg=forecastingthefutureisimpossible"); |
| 57 | } |
| 58 | |
| 59 | $companies = companies::getAll(false, true); |
| 60 | if ($companies === false) { |
| 61 | security::go($mainURL."?msg=unexpected"); |
| 62 | } |
| 63 | |
| 64 | $filenameDate = $begins->format("Ymd")."_".$ends->format("Ymd"); |
| 65 | |
| 66 | if (!$isAdmin && !in_array($_GET["format"], export::$workerFormats)) { |
| 67 | echo "Todavía no implementado.\n"; |
| 68 | exit(); |
| 69 | } |
| 70 | |
| 71 | switch ($_GET["format"]) { |
| 72 | case export::FORMAT_PDF: |
| 73 | case export::FORMAT_DETAILEDPDF: |
| 74 | require_once("lib/fpdf/fpdf.php"); |
| 75 | require_once("lib/fpdf-easytable/exfpdf.php"); |
| 76 | require_once("lib/fpdf-easytable/easyTable.php"); |
| 77 | |
| 78 | $actualTime = time(); |
| 79 | |
| 80 | class PDF extends EXFPDF { |
| 81 | function Footer() { |
| 82 | global $actualTime; |
| 83 | |
| 84 | $this->SetFont('Arial','I',10); |
| 85 | $this->SetY(-20); |
Adrià Vilanova Martínez | eaee4a9 | 2023-12-03 21:07:21 +0100 | [diff] [blame^] | 86 | $this->Cell(0, 10, export::convert("Generado: ".date::getShortDateWithTime($actualTime)), 0, 0, 'L'); |
Copybara bot | be50d49 | 2023-11-30 00:16:42 +0100 | [diff] [blame] | 87 | $this->SetY(-20); |
| 88 | $this->Cell(0, 10, $this->PageNo().'/{nb}', 0, 0, 'R'); |
| 89 | } |
| 90 | } |
| 91 | |
| 92 | $pdf = new PDF(); |
| 93 | $pdf->SetCompression(true); |
| 94 | $pdf->SetCreator(export::convert($conf["appName"])); |
| 95 | $pdf->SetTitle(export::convert("Registro".($showvalidated && !$shownotvalidated ? " (validados)" : "").(!$showvalidated && $shownotvalidated ? " (no validados)" : ""))); |
| 96 | $pdf->SetAutoPageBreak(false, 22); |
| 97 | $pdf->AliasNbPages(); |
| 98 | $pdf->SetFont('Arial','',12); |
| 99 | |
| 100 | $headerCells = ["Fecha", "Inicio jornada", "Fin jornada", "Desayuno", "Comida", ""]; |
| 101 | $headerCells = array_map(function($value) { |
| 102 | return export::convert($value); |
| 103 | }, $headerCells); |
| 104 | $spacingDayRow = "%{18, 18, 18, 19, 19, 8}"; |
| 105 | |
| 106 | foreach ($_GET["workers"] as $workerid) { |
| 107 | $worker = workers::get($workerid); |
| 108 | if ($worker === false || (!$isAdmin && $worker["person"] != $_SESSION["id"])) continue; |
| 109 | |
| 110 | $days = export::getDays($worker["id"], $begins->getTimestamp(), $ends->getTimestamp(), $showvalidated, $shownotvalidated); |
| 111 | |
| 112 | if (count($days) || !$ignoreempty) { |
| 113 | $pdf->AddPage(); |
| 114 | $pdf->SetMargins(17, 17); |
| 115 | $pdf->SetY(17); |
| 116 | |
| 117 | $header = new easyTable($pdf, 1, 'border-width: 0.01; border: 0; align: L;'); |
| 118 | $header->easyCell(export::convert($worker["name"].(!empty($worker["dni"]) ? " (".$worker["dni"].")" : "")), 'font-style:B;'); |
| 119 | $header->printRow(); |
| 120 | $header->easyCell(export::convert($worker["companyname"].(!empty($companies[$worker["company"]]["cif"]) ? " (CIF: ".$companies[$worker["company"]]["cif"].")" : ""))); |
| 121 | $header->printRow(); |
| 122 | $header->endTable(6); |
| 123 | } |
| 124 | |
| 125 | if (count($days)) { |
| 126 | $totalEffectiveTime = 0; |
| 127 | $totalWorkTime = 0; |
| 128 | $incidentsTime = []; |
| 129 | |
| 130 | $table = new easyTable($pdf, $spacingDayRow, 'border: "TB"; border-width: 0.15;'); |
| 131 | foreach ($headerCells as $i => $cell) { |
| 132 | $table->easyCell($cell, 'font-style: B;'.($i == 0 ? "" : " align: C;")); |
| 133 | } |
| 134 | $table->printRow(true); |
| 135 | $table->endTable(0); |
| 136 | |
| 137 | foreach ($days as $timestamp => $day) { |
| 138 | $issetSchedule = isset($day["schedule"]); |
| 139 | $showSchedule = $issetSchedule; |
| 140 | if ($showSchedule && isset($day["incidents"])) { |
| 141 | foreach ($day["incidents"] as $incident) { |
| 142 | if ($incident["allday"] == true && $incident["typepresent"] == 0) { |
| 143 | $showSchedule = false; |
| 144 | break; |
| 145 | } |
| 146 | } |
| 147 | } |
| 148 | |
| 149 | $workInt = ($issetSchedule ? [$day["schedule"]["beginswork"], $day["schedule"]["endswork"]] : [incidents::STARTOFDAY, incidents::STARTOFDAY]); |
| 150 | $notWorkIntA = ($issetSchedule ? [incidents::STARTOFDAY, $day["schedule"]["beginswork"]] : [incidents::STARTOFDAY, incidents::ENDOFDAY]); |
| 151 | $notWorkIntB = ($issetSchedule ? [$day["schedule"]["endswork"], incidents::ENDOFDAY] : [incidents::ENDOFDAY, incidents::ENDOFDAY]); |
| 152 | $breakfastInt = ($issetSchedule ? [$day["schedule"]["beginsbreakfast"], $day["schedule"]["endsbreakfast"]] : [incidents::STARTOFDAY, incidents::STARTOFDAY]); |
| 153 | $lunchInt = ($issetSchedule ? [$day["schedule"]["beginslunch"], $day["schedule"]["endslunch"]] : [incidents::STARTOFDAY, incidents::STARTOFDAY]); |
| 154 | |
| 155 | $effectiveTime = intervals::measure($workInt) - (intervals::measure($breakfastInt) + intervals::measure($lunchInt)); |
| 156 | |
| 157 | $totalWorkTime += $effectiveTime; |
| 158 | |
| 159 | if (isset($day["incidents"])) { |
| 160 | foreach ($day["incidents"] as &$incident) { |
| 161 | $incidentInt = [$incident["begins"], $incident["ends"]]; |
| 162 | |
| 163 | $incidentTime = 0; |
| 164 | |
| 165 | if ($incident["typepresent"] == 1) { |
| 166 | $incidentTime = intervals::measureIntersection($incidentInt, $notWorkIntA) + intervals::measureIntersection($incidentInt, $notWorkIntB) + intervals::measureIntersection($incidentInt, $breakfastInt) + intervals::measureIntersection($incidentInt, $lunchInt); |
| 167 | $effectiveTime = $effectiveTime + $incidentTime; |
| 168 | |
| 169 | $incidentTime = -$incidentTime; |
| 170 | } else { |
| 171 | $incidentTime = intervals::measureIntersection($incidentInt, $workInt) - ($conf["pdfs"]["workersAlwaysHaveBreakfastAndLunch"] ? 0 : (intervals::measureIntersection($incidentInt, $breakfastInt) + intervals::measureIntersection($incidentInt, $lunchInt))); |
| 172 | $effectiveTime = $effectiveTime - $incidentTime; |
| 173 | } |
| 174 | |
| 175 | $incidentsTime[$incident["typename"]] = (isset($incidentsTime[$incident["typename"]]) ? $incidentsTime[$incident["typename"]] : 0) + $incidentTime; |
| 176 | } |
| 177 | } |
| 178 | |
| 179 | $effectiveTime = max(incidents::STARTOFDAY, min($effectiveTime, incidents::ENDOFDAY)); |
| 180 | $totalEffectiveTime += $effectiveTime; |
| 181 | |
| 182 | $labelAsInvalid = ($shownotvalidated && isset($day["schedule"]) && $day["schedule"]["state"] === registry::STATE_REGISTERED); |
| 183 | |
| 184 | $table = new easyTable($pdf, $spacingDayRow, 'border: "BT";'.(isset($day["incidents"]) ? ' border-width: 0.07; border-color: #555;' : ' border-width: 0.15;').($labelAsInvalid && $labelinvalid ? ' font-color: #F00;' : '')); |
| 185 | $table->easyCell(export::convert(date("d/m/Y", $timestamp).($labelAsInvalid ? " (nv)" : ""))); |
| 186 | $table->easyCell(export::convert(($showSchedule ? schedules::sec2time($day["schedule"]["beginswork"]) : "-")), 'align: C;'); |
| 187 | $table->easyCell(export::convert(($showSchedule ? schedules::sec2time($day["schedule"]["endswork"]) : "-")), 'align: C;'); |
| 188 | $table->easyCell(export::convert(($showSchedule ? (intervals::measure($breakfastInt) == 0 ? "-" : (!$conf["pdfs"]["showExactTimeForBreakfastAndLunch"] ? export::sec2hours(intervals::measure($breakfastInt)) : schedules::sec2time($day["schedule"]["beginsbreakfast"])." - ".schedules::sec2time($day["schedule"]["endsbreakfast"]))) : "-")), 'align: C;'); |
| 189 | $table->easyCell(export::convert(($showSchedule ? (intervals::measure($lunchInt) == 0 ? "-" : (!$conf["pdfs"]["showExactTimeForBreakfastAndLunch"] ? export::sec2hours(intervals::measure($lunchInt)) : schedules::sec2time($day["schedule"]["beginslunch"])." - ".schedules::sec2time($day["schedule"]["endslunch"]))) : "-")), 'align: C;'); |
| 190 | $table->easyCell(export::convert(export::sec2hours($effectiveTime)), 'font-style: B; align: R;'); |
| 191 | $table->printRow(); |
| 192 | $table->endTable(0); |
| 193 | |
| 194 | if (isset($day["incidents"])) { |
| 195 | $incidentstable = new easyTable($pdf, "%{7, 15, 20, 20, 38}", 'border: "BT"; border-width: 0.15; font-color: #555;'); |
| 196 | foreach ($day["incidents"] as &$incident) { |
| 197 | $labelAsInvalid = ($incident["state"] === incidents::STATE_REGISTERED); |
| 198 | |
| 199 | if ($labelAsInvalid && $labelinvalid) $incidentstable->rowStyle('font-color: #F00;'); |
| 200 | $incidentstable->easyCell(""); |
| 201 | $incidentstable->easyCell(export::convert("Incidencia:"), 'font-style: B;'); |
| 202 | $incidentstable->easyCell(export::convert($incident["typename"]), 'align: C; font-size: 11;'); |
| 203 | $incidentstable->easyCell(export::convert("(".(($incident["begins"] == 0 && $incident["ends"] == incidents::ENDOFDAY) ? "todo el día" : schedules::sec2time($incident["begins"])." - ".schedules::sec2time($incident["ends"])).")"), 'align: C;'); |
| 204 | $incidentstable->easyCell(export::convert((!empty($incident["details"]) ? "Obs.: ".$incident["details"] : "").($labelAsInvalid ? "\n(No validada)" : "")), 'font-size: 10;'); |
| 205 | $incidentstable->printRow(); |
| 206 | } |
| 207 | $incidentstable->endTable(0); |
| 208 | } |
| 209 | } |
| 210 | |
| 211 | if ($_GET["format"] == export::FORMAT_DETAILEDPDF) { |
| 212 | $pdf->Ln(); |
| 213 | $pdf->Ln(); |
| 214 | $table = new easyTable($pdf, "%{77, 23}", 'align: R; width: 100; border: "BT"; border-width: 0.15;'); |
| 215 | |
| 216 | $table->easyCell(export::convert("Tiempo de trabajo programado")); |
| 217 | $table->easyCell(export::convert(export::sec2hours($totalWorkTime)), 'align: R;'); |
| 218 | $table->printRow(); |
| 219 | |
| 220 | foreach ($incidentsTime as $incidentName => $incidentTime) { |
| 221 | $table->easyCell(export::convert($incidentName)); |
| 222 | $table->easyCell(export::convert(($incidentTime <= 0 ? "+" : "\u{2013}")." ".export::sec2hours(abs($incidentTime))), 'align: R;'); |
| 223 | $table->printRow(); |
| 224 | } |
| 225 | |
| 226 | $table->easyCell(export::convert("Tiempo trabajado"), 'font-style: B;'); |
| 227 | $table->easyCell(export::convert(export::sec2hours($totalEffectiveTime)), 'align: R; font-style: B;'); |
| 228 | $table->printRow(); |
| 229 | |
| 230 | $table->endTable(0); |
| 231 | } |
| 232 | } elseif (!$ignoreempty) { |
| 233 | $pdf->Cell(0, 0, "No hay datos para este trabajador.", 0, 0, 'C'); |
| 234 | } |
| 235 | } |
| 236 | |
| 237 | $pdf->Output("I", "registrohorario_".$filenameDate.".pdf"); |
| 238 | break; |
| 239 | |
| 240 | case export::FORMAT_CSV_SCHEDULES: |
| 241 | case export::FORMAT_CSV_INCIDENTS: |
| 242 | $isSchedules = ($_GET["format"] == export::FORMAT_CSV_SCHEDULES); |
| 243 | $field = ($isSchedules ? "schedule" : "incidents"); |
| 244 | header("Content-Type: text/csv"); |
| 245 | header("Content-Disposition: attachment;filename=".($isSchedules ? "schedules_".$filenameDate.".csv" : "incidents_".$filenameDate.".csv")); |
| 246 | |
| 247 | $array = []; |
| 248 | |
| 249 | $array[] = ($isSchedules ? export::$schedulesFields : export::$incidentsFields); |
| 250 | |
| 251 | foreach ($_GET["workers"] as $workerid) { |
| 252 | $worker = workers::get($workerid); |
| 253 | if ($worker === false || (!$isAdmin && $worker["person"] != $_SESSION["id"])) continue; |
| 254 | |
| 255 | $days = export::getDays($worker["id"], $begins->getTimestamp(), $ends->getTimestamp(), true, true); |
| 256 | |
| 257 | foreach ($days as &$day) { |
| 258 | if (isset($day[$field])) { |
| 259 | if ($isSchedules) { |
| 260 | $schedule = []; |
| 261 | foreach (export::$schedulesFields as $i => $key) { |
| 262 | $types = ["breakfast", "lunch"]; |
| 263 | $typesNotDefined = []; |
| 264 | foreach ($types as $type) { |
| 265 | if (intervals::measure([$day[$field]["begins".$type], $day[$field]["ends".$type]]) == 0) { |
| 266 | $typesNotDefined[] = "begins".$type; |
| 267 | $typesNotDefined[] = "ends".$type; |
| 268 | } |
| 269 | } |
| 270 | |
| 271 | switch ($key) { |
| 272 | case "worker": |
| 273 | $schedule[$i] = $worker["name"]; |
| 274 | break; |
| 275 | |
| 276 | case "company": |
| 277 | $schedule[$i] = $worker["companyname"]; |
| 278 | break; |
| 279 | |
| 280 | case "workerid": |
| 281 | $schedule[$i] = $worker["id"]; |
| 282 | break; |
| 283 | |
| 284 | case "dni": |
| 285 | $schedule[$i] = $worker["dni"]; |
| 286 | break; |
| 287 | |
| 288 | case "day": |
| 289 | $schedule[$i] = date("d/m/Y", $day[$field][$key]); |
| 290 | break; |
| 291 | |
| 292 | case "state": |
| 293 | $schedule[$i] = (registry::$stateTooltips[$day[$field][$key]]); |
| 294 | break; |
| 295 | |
| 296 | case "beginswork": |
| 297 | case "endswork": |
| 298 | case "beginsbreakfast": |
| 299 | case "endsbreakfast": |
| 300 | case "beginslunch": |
| 301 | case "endslunch": |
| 302 | $schedule[$i] = (in_array($key, $typesNotDefined) ? "-" : schedules::sec2time($day[$field][$key])); |
| 303 | break; |
| 304 | |
| 305 | default: |
| 306 | $schedule[$i] = $day[$field][$key]; |
| 307 | } |
| 308 | } |
| 309 | $array[] = $schedule; |
| 310 | } else { |
| 311 | foreach ($day[$field] as &$incident) { |
| 312 | $convIncident = []; |
| 313 | foreach (export::$incidentsFields as $i => $key) { |
| 314 | switch ($key) { |
| 315 | case "worker": |
| 316 | $convIncident[$i] = $worker["name"]; |
| 317 | break; |
| 318 | |
| 319 | case "company": |
| 320 | $convIncident[$i] = $worker["companyname"]; |
| 321 | break; |
| 322 | |
| 323 | case "workerid": |
| 324 | $convIncident[$i] = $worker["id"]; |
| 325 | break; |
| 326 | |
| 327 | case "dni": |
| 328 | $convIncident[$i] = $worker["dni"]; |
| 329 | break; |
| 330 | |
| 331 | case "creator": |
| 332 | case "updatedby": |
| 333 | case "confirmedby": |
| 334 | $convIncident[$i] = (($key == "updatedby" && $incident["updatestate"] != 1) ? "-" : people::userData("name", $incident[$key])); |
| 335 | break; |
| 336 | |
| 337 | case "day": |
| 338 | $convIncident[$i] = date("d/m/Y", $incident[$key]); |
| 339 | break; |
| 340 | |
| 341 | case "begins": |
| 342 | case "ends": |
| 343 | $convIncident[$i] = ($incident["allday"] == 1 ? "-" : schedules::sec2time($incident[$key])); |
| 344 | break; |
| 345 | |
| 346 | case "updated": |
| 347 | case "verified": |
| 348 | case "typepresent": |
| 349 | case "typepaid": |
| 350 | if ($key == "updated") $key = "updatestate"; |
| 351 | $convIncident[$i] = ($incident[$key] == -1 ? "-" : ($incident[$key] == 1 ? visual::YES : visual::NO)); |
| 352 | break; |
| 353 | |
| 354 | case "allday": |
| 355 | $convIncident[$i] = ($incident[$key] == 1 ? visual::YES : visual::NO); |
| 356 | break; |
| 357 | |
| 358 | case "state": |
| 359 | $convIncident[$i] = (incidents::$stateTooltips[$incident[$key]]); |
| 360 | break; |
| 361 | |
| 362 | case "type": |
| 363 | $convIncident[$i] = $incident["typename"]; |
| 364 | break; |
| 365 | |
| 366 | default: |
| 367 | $convIncident[$i] = $incident[$key]; |
| 368 | } |
| 369 | } |
| 370 | $array[] = $convIncident; |
| 371 | } |
| 372 | } |
| 373 | } |
| 374 | } |
| 375 | } |
| 376 | |
| 377 | if (!count($array)) exit(); |
| 378 | |
| 379 | $df = fopen("php://output", 'w'); |
| 380 | |
| 381 | foreach ($array as $row) fputcsv($df, $row); |
| 382 | |
| 383 | fclose($df); |
| 384 | break; |
| 385 | |
| 386 | default: |
| 387 | header("Content-Type: text/plain;"); |
| 388 | echo "Todavía no implementado.\n"; |
| 389 | } |