blob: 5d29327dbc252a12e35a98eec9e384ffcfcc71a5 [file] [log] [blame]
Copybara botbe50d492023-11-30 00:16:42 +01001<?php
Adrià Vilanova Martínez5af86512023-12-02 20:44:16 +01002/*
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 botbe50d492023-11-30 00:16:42 +010021require_once("core.php");
22security::checkType(security::WORKER);
23security::checkWorkerUIEnabled();
24
25$isAdmin = security::isAllowed(security::ADMIN);
26
27$mainURL = (security::isAdminView() ? "export.php" : "export4worker.php");
28
29if (!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
47if (!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
55if ($ends->diff($date)->invert !== 0) {
56 security::go($mainURL."?msg=forecastingthefutureisimpossible");
57}
58
59$companies = companies::getAll(false, true);
60if ($companies === false) {
61 security::go($mainURL."?msg=unexpected");
62}
63
64$filenameDate = $begins->format("Ymd")."_".$ends->format("Ymd");
65
66if (!$isAdmin && !in_array($_GET["format"], export::$workerFormats)) {
67 echo "Todavía no implementado.\n";
68 exit();
69}
70
71switch ($_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);
86 $this->Cell(0, 10, export::convert("Generado: ".strftime("%d %b %Y %T", $actualTime)), 0, 0, 'L');
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}