blob: a987cc4a6f11ac5fe2766297f2956833164d0e24 [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 +010021class schedules {
22 const STATUS_NO_ACTIVE_SCHEDULE = 0;
23 const STATUS_HALFWAY_CONFIGURED_SCHEDULE = 1;
24 const STATUS_ACTIVE_SCHEDULE = 2;
25
26 public static $allEvents = ["work", "breakfast", "lunch"];
27 public static $otherEvents = ["breakfast", "lunch"];
28 public static $otherEventsDescription = [
29 "breakfast" => "Desayuno",
30 "lunch" => "Comida"
31 ];
32 public static $workerScheduleStatus = [
33 0 => "No hay ningún calendario activo",
34 1 => "Hay un calendario activo pero no está completamente configurado",
35 2 => "Hay un calendario activo completamente configurado"
36 ];
37 public static $workerScheduleStatusShort = [
38 0 => "No hay un horario activo",
39 1 => "No está configurado del todo",
40 2 => "Existe uno configurado"
41 ];
42 public static $workerScheduleStatusColors = [
43 0 => "red",
44 1 => "orange",
45 2 => "green"
46 ];
47
48 public static function time2sec($time) {
49 $e = explode(":", $time);
50 return ((int)$e[0]*60 + (int)$e[1])*60;
51 }
52
53 public static function sec2time($sec, $autoShowSeconds = true) {
54 $min = floor($sec/60);
55 $secr = $sec % 60;
56 return visual::padNum(floor($min/60), 2).":".visual::padNum(($min % 60), 2).($autoShowSeconds && $secr != 0 ? ":".visual::padNum($secr, 2) : "");
57 }
58
59 // TEMPLATES:
60
61 public static function addTemplate($name, $ibegins, $iends) {
62 global $con;
63
64 $sname = db::sanitize($name);
65 $begins = new DateTime($ibegins);
66 $sbegins = (int)$begins->getTimestamp();
67 $ends = new DateTime($iends);
68 $sends = (int)$ends->getTimestamp();
69
70 if (!intervals::wellFormed([$sbegins, $sends])) return 2;
71
72 return (mysqli_query($con, "INSERT INTO scheduletemplates (name, begins, ends) VALUES ('$sname', $sbegins, $sends)") ? 0 : 1);
73 }
74
75 private static function _addDaysToReturn(&$return, $stableDays, $sfieldSchedule, $sid) {
76 global $con;
77
78 $return["days"] = [];
79
80 $query2 = mysqli_query($con, "SELECT * FROM $stableDays WHERE $sfieldSchedule = $sid ORDER BY typeday ASC, day ASC");
81
82 while ($row = mysqli_fetch_assoc($query2)) {
83 if (!isset($return["days"][$row["typeday"]]))
84 $return["days"][$row["typeday"]] = [];
85
86 $return["days"][$row["typeday"]][$row["day"]] = $row;
87 }
88 }
89
90 private static function _get($id, $table, $tableDays, $fieldSchedule) {
91 global $con;
92
93 $sid = (int)$id;
94 $stable = preg_replace("/[^A-Za-z0-9 ]/", '', $table);
95 $stableDays = preg_replace("/[^A-Za-z0-9 ]/", '', $tableDays);
96 $sfieldSchedule = preg_replace("/[^A-Za-z0-9 ]/", '', $fieldSchedule);
97
98 $query = mysqli_query($con, "SELECT * FROM $stable WHERE id = $sid");
99
100 if ($query === false || !mysqli_num_rows($query)) return false;
101
102 $return = mysqli_fetch_assoc($query);
103
104 self::_addDaysToReturn($return, $stableDays, $sfieldSchedule, $sid);
105
106 return $return;
107 }
108
109 public static function getTemplate($id) {
110 return self::_get($id, "scheduletemplates", "scheduletemplatesdays", "template");
111 }
112
113 public static function getTemplates() {
114 global $con, $conf;
115
116 $query = mysqli_query($con, "SELECT * FROM scheduletemplates ORDER BY ".($conf["debug"] ? "id" : "name")." ASC");
117
118 $templates = [];
119
120 while ($row = mysqli_fetch_assoc($query)) {
121 $templates[] = $row;
122 }
123
124 return $templates;
125 }
126
127 public static function editTemplate($id, $name, $ibegins, $iends) {
128 global $con;
129
130 $sid = (int)$id;
131 $sname = db::sanitize($name);
132 $begins = new DateTime($ibegins);
133 $sbegins = (int)$begins->getTimestamp();
134 $ends = new DateTime($iends);
135 $sends = (int)$ends->getTimestamp();
136
137 if (!intervals::wellFormed([$sbegins, $sends])) return 2;
138
139 return (mysqli_query($con, "UPDATE scheduletemplates SET name = '$sname', begins = $sbegins, ends = $sends WHERE id = $sid LIMIT 1") ? 0 : 1);
140 }
141
142 public static function _remove($id, $table, $tableDays, $fieldSchedule) {
143 global $con;
144
145 $sid = (int)$id;
146 $stable = preg_replace("/[^A-Za-z0-9 ]/", '', $table);
147 $stableDays = preg_replace("/[^A-Za-z0-9 ]/", '', $tableDays);
148 $sfieldSchedule = preg_replace("/[^A-Za-z0-9 ]/", '', $fieldSchedule);
149
150 return (mysqli_query($con, "DELETE FROM $stable WHERE id = $sid LIMIT 1") && mysqli_query($con, "DELETE FROM $stableDays WHERE $fieldSchedule = $sid"));
151 }
152
153 public static function removeTemplate($id) {
154 return self::_remove($id, "scheduletemplates", "scheduletemplatesdays", "template");
155 }
156
157 private static function _exists($id, $table) {
158 global $con;
159
160 $sid = (int)$id;
161 $stable = preg_replace("/[^A-Za-z0-9 ]/", '', $table);
162
163 $query = mysqli_query($con, "SELECT id FROM $stable WHERE id = ".(int)$id);
164
165 return (mysqli_num_rows($query) > 0);
166 }
167
168 public static function templateExists($id) {
169 return self::_exists($id, "scheduletemplates");
170 }
171
172 public static function checkAddDayGeneric($begins, $ends, $beginsb, $endsb, $beginsl, $endsl) {
173 global $con;
174
175 $times = [];
176 $times["work"] = [$begins, $ends];
177 $times["breakfast"] = [$beginsb, $endsb];
178 $times["lunch"] = [$beginsl, $endsl];
179
180 foreach ($times as $time) {
181 if (intervals::wellFormed($time) === false) return 1;
182 }
183
184 if (intervals::measure($times["work"]) == 0) return 4;
185
186 if ((!intervals::isSubset($times["breakfast"], $times["work"]) && intervals::measure($times["breakfast"]) != 0) || (!intervals::isSubset($times["lunch"], $times["work"]) && intervals::measure($times["lunch"]) != 0)) return 2;
187
188 if (intervals::overlaps($times["breakfast"], $times["lunch"]) && intervals::measure($times["breakfast"]) != 0 && intervals::measure($times["lunch"]) != 0) return 3;
189
190 return 0;
191 }
192
193 private static function _checkAddDayParticular($id, $dow, $typeday, $table, $fieldSchedule) {
194 global $con;
195
196 $sid = (int)$id;
197 $sdow = (int)$dow;
198 $stypeday = (int)$typeday;
199 $stable = preg_replace("/[^A-Za-z0-9 ]/", '', $table);
200 $sfieldSchedule = preg_replace("/[^A-Za-z0-9 ]/", '', $fieldSchedule);
201
202 $query = mysqli_query($con, "SELECT id FROM $stable WHERE $fieldSchedule = $sid AND day = $sdow AND typeday = $stypeday");
203
204 return (!mysqli_num_rows($query));
205 }
206
207 public static function checkAddDay2TemplateParticular($id, $dow, $typeday) {
208 return self::_checkAddDayParticular($id, $dow, $typeday, "scheduletemplatesdays", "template");
209 }
210
211 private static function _addDay($id, $dow, $typeday, $begins, $ends, $beginsb, $endsb, $beginsl, $endsl, $table, $fieldSchedule) {
212 global $con;
213
214 $sid = (int)$id;
215 $sdow = (int)$dow;
216 $stypeday = (int)$typeday;
217 $sbegins = (int)$begins;
218 $sends = (int)$ends;
219 $sbeginsb = (int)$beginsb;
220 $sendsb = (int)$endsb;
221 $sbeginsl = (int)$beginsl;
222 $sendsl = (int)$endsl;
223 $stable = preg_replace("/[^A-Za-z0-9 ]/", '', $table);
224 $sfieldSchedule = preg_replace("/[^A-Za-z0-9 ]/", '', $fieldSchedule);
225
226 return mysqli_query($con, "INSERT INTO $stable ($sfieldSchedule, day, typeday, beginswork, endswork, beginsbreakfast, endsbreakfast, beginslunch, endslunch) VALUES ($sid, $sdow, $stypeday, $sbegins, $sends, $sbeginsb, $sendsb, $sbeginsl, $sendsl)");
227 }
228
229 public static function addDay2Template($id, $dow, $typeday, $begins, $ends, $beginsb, $endsb, $beginsl, $endsl) {
230 return self::_addDay($id, $dow, $typeday, $begins, $ends, $beginsb, $endsb, $beginsl, $endsl, "scheduletemplatesdays", "template");
231 }
232
233 public static function _getDay($id, $table) {
234 global $con;
235
236 $sid = (int)$id;
237 $stable = preg_replace("/[^A-Za-z0-9 ]/", '', $table);
238
239 $query = mysqli_query($con, "SELECT * FROM $stable WHERE id = $sid");
240
241 if (!mysqli_num_rows($query)) return false;
242
243 return mysqli_fetch_assoc($query);
244 }
245
246 public static function getTemplateDay($id) {
247 return self::_getDay($id, "scheduletemplatesdays");
248 global $con;
249 }
250
251 private static function _editDay($id, $begins, $ends, $beginsb, $endsb, $beginsl, $endsl, $table) {
252 global $con;
253
254 $sid = (int)$id;
255 $sbegins = (int)$begins;
256 $sends = (int)$ends;
257 $sbeginsb = (int)$beginsb;
258 $sendsb = (int)$endsb;
259 $sbeginsl = (int)$beginsl;
260 $sendsl = (int)$endsl;
261 $stable = preg_replace("/[^A-Za-z0-9 ]/", '', $table);
262
263 return mysqli_query($con, "UPDATE $stable SET beginswork = $sbegins, endswork = $sends, beginsbreakfast = $sbeginsb, endsbreakfast = $sendsb, beginslunch = $sbeginsl, endslunch = $sendsl WHERE id = $sid LIMIT 1");
264 }
265
266 public static function editTemplateDay($id, $begins, $ends, $beginsb, $endsb, $beginsl, $endsl) {
267 return self::_editDay($id, $begins, $ends, $beginsb, $endsb, $beginsl, $endsl, "scheduletemplatesdays");
268 }
269
270 private static function _removeDay($id, $table) {
271 global $con;
272
273 $sid = (int)$id;
274 $stable = preg_replace("/[^A-Za-z0-9 ]/", '', $table);
275
276 return mysqli_query($con, "DELETE FROM $stable WHERE id = $sid LIMIT 1");
277 }
278
279 public static function removeTemplateDay($id) {
280 return self::_removeDay($id, "scheduletemplatesdays");
281 }
282
283 private static function _dayExists($id, $table) {
284 global $con;
285
286 $sid = (int)$id;
287 $stable = preg_replace("/[^A-Za-z0-9 ]/", '', $table);
288
289 $query = mysqli_query($con, "SELECT id FROM $stable WHERE id = $sid");
290
291 return (mysqli_num_rows($query) > 0);
292 }
293
294 public static function templateDayExists($id) {
295 return self::_dayExists($id, "scheduletemplatesdays");
296 }
297
298 // SCHEDULES:
299
300 public static function checkOverlap($worker, $begins, $ends, $sans = 0) {
301 global $con;
302
303 $sworker = (int)$worker;
304 $sbegins = (int)$begins;
305 $sends = (int)$ends;
306 $ssans = (int)$sans;
307
308 $query = mysqli_query($con, "SELECT * FROM schedules WHERE begins <= $sends AND ends >= $sbegins AND worker = $sworker".($sans == 0 ? "" : " AND id <> $ssans")." LIMIT 1");
309
310 return (mysqli_num_rows($query) > 0);
311 }
312
313 public static function get($id) {
314 return self::_get($id, "schedules", "schedulesdays", "schedule");
315 }
316
317 public static function getAll($id, $showNotActive = true) {
318 global $con, $conf;
319
320 $sid = (int)$id;
321
322 $query = mysqli_query($con, "SELECT * FROM schedules WHERE worker = $sid".($showNotActive ? "" : " AND active = 1")." ORDER BY ".($conf["debug"] ? "id ASC" : "begins DESC"));
323
324 $schedules = [];
325
326 while ($row = mysqli_fetch_assoc($query)) {
327 $schedules[] = $row;
328 }
329
330 return $schedules;
331 }
332
333 public static function getCurrent($id = "ME", $isWorker = false) {
334 global $con;
335
336 if ($id == "ME") $id = people::userData("id");
337 $sid = (int)$id;
338
339 $date = new DateTime(date("Y-m-d")."T00:00:00");
340 $timestamp = (int)$date->getTimestamp();
341
342 $query = mysqli_query($con, "SELECT s.* FROM schedules s ".($isWorker ? "WHERE s.worker = $sid" : "LEFT JOIN workers w ON s.worker = w.id WHERE w.person = $sid")." AND s.active = 1 AND s.begins <= $timestamp AND s.ends >= $timestamp");
343
344 $return = [];
345
346 while ($row = mysqli_fetch_assoc($query)) {
347 self::_addDaysToReturn($row, "schedulesdays", "schedule", $row["id"]);
348 $return[] = $row;
349 }
350
351 return $return;
352 }
353
354 public static function getWorkerScheduleStatus($id) {
355 $currentSchedules = self::getCurrent($id, true);
356 if ($currentSchedules === false || !count($currentSchedules)) return ["status" => self::STATUS_NO_ACTIVE_SCHEDULE];
357
358 $schedule =& $currentSchedules[0];
359
360 foreach (calendars::$workingTypes as $type) {
361 if (!isset($schedule["days"][$type]) || !count($schedule["days"][$type])) return ["status" => self::STATUS_HALFWAY_CONFIGURED_SCHEDULE, "schedule" => $schedule["id"]];
362 }
363
364 return ["status" => self::STATUS_ACTIVE_SCHEDULE, "schedule" => $schedule["id"]];
365 }
366
367 public static function add($worker, $ibegins, $iends, $active = 0, $alreadyTimestamp = false) {
368 global $con;
369
370 $sworker = (int)$worker;
371 $sactive = (int)$active;
372 if ($alreadyTimestamp) {
373 $sbegins = (int)$ibegins;
374 $sends = (int)$iends;
375 } else {
376 $begins = new DateTime($ibegins);
377 $sbegins = (int)$begins->getTimestamp();
378 $ends = new DateTime($iends);
379 $sends = (int)$ends->getTimestamp();
380 }
381
382 if (!intervals::wellFormed([$sbegins, $sends])) return 3;
383
384 if (self::checkOverlap($worker, $sbegins, $sends)) {
385 return 1;
386 }
387
388 return (mysqli_query($con, "INSERT INTO schedules (worker, begins, ends, active) VALUES ('$sworker', $sbegins, $sends, $sactive)") ? 0 : 2);
389 }
390
391 public static function edit($id, $ibegins, $iends) {
392 global $con;
393
394 $sid = (int)$id;
395 $begins = new DateTime($ibegins);
396 $sbegins = (int)$begins->getTimestamp();
397 $ends = new DateTime($iends);
398 $sends = (int)$ends->getTimestamp();
399
400 if (!intervals::wellFormed([$sbegins, $sends])) return 3;
401
402 $actual = self::get($sid);
403 if ($actual === false) return 4;
404
405 if (self::checkOverlap($actual["worker"], $sbegins, $sends, $sid)) {
406 return 1;
407 }
408
409 return (mysqli_query($con, "UPDATE schedules SET begins = $sbegins, ends = $sends WHERE id = $sid LIMIT 1") ? 0 : 2);
410 }
411
412 public static function remove($id) {
413 return self::_remove($id, "schedules", "schedulesdays", "schedule");
414 }
415
416 public static function switchActive($id, $value) {
417 global $con;
418
419 $sid = (int)$id;
420 $svalue = (int)$value;
421 if ($svalue > 1 || $svalue < 0) return false;
422
423 return mysqli_query($con, "UPDATE schedules SET active = $svalue WHERE id = $sid LIMIT 1");
424 }
425
426 public static function exists($id) {
427 return self::_exists($id, "schedules");
428 }
429
430 public static function checkAddDay2ScheduleParticular($id, $dow, $typeday) {
431 return self::_checkAddDayParticular($id, $dow, $typeday, "schedulesdays", "schedule");
432 }
433
434 public static function addDay2Schedule($id, $dow, $typeday, $begins, $ends, $beginsb, $endsb, $beginsl, $endsl) {
435 return self::_addDay($id, $dow, $typeday, $begins, $ends, $beginsb, $endsb, $beginsl, $endsl, "schedulesdays", "schedule");
436 }
437
438 public static function getDay($id) {
439 return self::_getDay($id, "schedulesdays");
440 global $con;
441 }
442
443 public static function editDay($id, $begins, $ends, $beginsb, $endsb, $beginsl, $endsl) {
444 return self::_editDay($id, $begins, $ends, $beginsb, $endsb, $beginsl, $endsl, "schedulesdays");
445 }
446
447 public static function dayExists($id) {
448 return self::_dayExists($id, "schedulesdays");
449 }
450
451 public static function removeDay($id) {
452 return self::_removeDay($id, "schedulesdays");
453 }
454
455 public static function copyTemplate($template, $worker, $active) {
456 global $con;
457
458 $template = self::getTemplate($template);
459 if ($template === false) return 1;
460
461 $status = self::add($worker, $template["begins"], $template["ends"], $active, true);
462 if ($status != 0) return ($status + 1);
463
464 $id = mysqli_insert_id($con);
465
466 foreach ($template["days"] as $typeday) {
467 foreach ($typeday as $day) {
468 $status2 = self::addDay2Schedule($id, $day["day"], $day["typeday"], $day["beginswork"], $day["endswork"], $day["beginsbreakfast"], $day["endsbreakfast"], $day["beginslunch"], $day["endslunch"]);
469 if (!$status2) return -1;
470 }
471 }
472
473 return 0;
474 }
475}