Merge branch 'spidev' into 'master'

Zeiterfassung

See merge request fronk/thetool!1582
This commit is contained in:
Daniel Spitzer
2025-07-29 13:35:01 +00:00
5 changed files with 333 additions and 13 deletions

View File

@@ -290,8 +290,6 @@ $years[time() - 31536000] = date('Y', time() - 31536000);
</div>
</div>
</form>
</div>
<div class="card">
@@ -303,9 +301,12 @@ $years[time() - 31536000] = date('Y', time() - 31536000);
<div class="col-6 col-lg-2 col-xl-2 text-center">
<i class="fa-duotone fa-calendar-week display-calendar active-calendar" title="Kalenderwoche"
data-datatype="1"></i> <i class="fa-duotone fa-calendar-days display-calendar"
title="Kalendermonat" data-datatype="2"></i> <i title="Abwesenheiten"
class="fa-duotone fa-calendar-clock display-calendar"
data-datatype="3"></i>
title="Kalendermonat" data-datatype="2"></i> <i title="KalenderJahr"
class="fa-duotone fa-calendar display-calendar"
data-datatype="5"></i>
<i title="Abwesenheiten"
class="fa-duotone fa-calendar-clock display-calendar"
data-datatype="3"></i>
</div>
<div class="col-lg-2">
<div class="form-row">
@@ -322,7 +323,7 @@ $years[time() - 31536000] = date('Y', time() - 31536000);
<div class="input-group">
<select id="datamonth" class="form-control select2">
<?php foreach ($month as $key => $Month): ?>
<option <?= (date("Y-m",$timenow) == date("Y-m",$key)) ? 'selected="selected"' : '' ?>
<option <?= (date("Y-m", $timenow) == date("Y-m", $key)) ? 'selected="selected"' : '' ?>
value="<?= $key ?>"><?= $Month ?></option>
<?php endforeach; ?>
</select>

View File

@@ -0,0 +1,47 @@
<?php include(realpath(dirname(__FILE__) . "/../../$mfLayoutPackage") . "/header.php");
?>
<link href="<?= self::getResourcePath() ?>css/pages/Calendar/View.css?<?= $git_merge_ts ?>" rel="stylesheet"
type="text/css"/>
<style>
</style>
<!-- start page title -->
<div class="row">
<div class="col-12">
<div class="page-title-box">
<div class="page-title-right">
<ol class="breadcrumb m-0">
<li class="breadcrumb-item"><a href="<?= self::getUrl("Dashboard") ?>"><?= MFAPPNAME_SLUG ?></a>
</li>
<li class="breadcrumb-item active">Reports</li>
</ol>
</div>
<h4 class="page-title">Reports</h4>
</div>
</div>
</div>
<div class="card">
<div class="card-body">
<div class="feature col-lg-6 card h-100">
<div class="card-body">
<h3 class="fs-2 text-body-emphasis">Auswertungen Stichtag <i class="fa-duotone fa-solid fa-file-xls fa-xls-calendar"></i></h3>
<p>Hier wird eine XLS Auswertung pro Mitarbeiter generiert mit Urlaub, Mehrstunden und Gutstunden
zum Stichtag X</p>
<div class="row">
<div class="input-group col-12 col-lg-5">
<input type="date" class="form-control" aria-label="Stichtag">
<button class="btn btn-outline-secondary" type="button">Auswerten</button>
</div>
</div>
</div>
</div>
</div>
</div>
<?php include(realpath(dirname(__FILE__) . "/../../$mfLayoutPackage") . "/footer.php"); ?>

View File

@@ -98,9 +98,8 @@ class TimerecordingReportController extends mfBaseController
$workinghourshistory = TimerecordingEmployeeWorkingHourHistoryModel::getAll();
if ($workinghourshistory) {
$workingHoursHistory[2147483500] = $workingHours;
foreach ($workinghourshistory as $workinghourhistory) {
$workingHoursHistory[$workinghourshistory->user_id][2147483500] = $workingHours;
$workingHoursHistory[$workinghourhistory->user_id][2147483500] = $workingHours[$workinghourhistory->user_id];
$whenddate = $workinghourhistory->enddate;
$workinghourhistoryhours = json_decode($workinghourhistory->workinghours, true);
foreach ($workinghourhistoryhours as $workinghourhistoryhour) {
@@ -112,12 +111,8 @@ class TimerecordingReportController extends mfBaseController
$workingHoursHistory[$workinghourhistory->user_id][$whenddate][$workinghourhistoryhour['day']] = $workingHoursHistory[$whenddate][$workinghourhistoryhour['day']] + $whend - $whstart;
}
}
// echo $workinghourhistory->user_id."<br>";
}
}
// var_dump($workingHoursHistory);
// die();
foreach ($this->holidays as $holiday) {
$holiDay[date('Y-m-d', $holiday->timestamp)] = $holiday->timestamp;
@@ -182,6 +177,30 @@ class TimerecordingReportController extends mfBaseController
}
} else if ($datatype == 5) {
$firstdate = strtotime(date("Y-01-01", $datayear));
$lastdate = strtotime(date("Y-12-31 23:59:59", $datayear));
$daycount = date("t", $datamonth);
$lastdate = strtotime(date("Y-m-d", $lastdate) . ' 23:59:59');
if ($calendar == "1") {
$lastdate = strtotime(" +3 years", $lastdate);
}
$searchArray = ['start' => $firstdate, 'end' => $lastdate];
$timestamp = $firstdate;
for ($i = 1; $i <= $daycount; $i++) {
$dDate = date('Y-m-d', $timestamp);
$dDay = date('w', $timestamp);
if (!$holiDay[$dDate]) {
//$mustSeconds = $mustSeconds + $workingHours[$dDay];
}
$timestamp = $timestamp + 86400;
}
}

View File

@@ -0,0 +1,253 @@
<?php
use PhpOffice\PhpSpreadsheet\Spreadsheet;
use PhpOffice\PhpSpreadsheet\Writer\Xlsx;
use PhpOffice\PhpSpreadsheet\Style\NumberFormat;
use PhpOffice\PhpSpreadsheet\Style\Font;
class TimerecordingReportExportController extends mfBaseController
{
protected function init()
{
$this->needlogin = true;
$me = new User();
$me->loadMe();
$this->me = $me;
$this->layout()->set("me", $me);
if (!$me->is(["employee"])) {
$this->redirect("Dashboard");
}
}
protected function indexAction()
{
$this->layout()->setTemplate("TimerecordingReportExport/Index");
}
protected function exportdeadlineAction()
{
$timerecordingBillingsEmployees = TimerecordingBillingEmployeeModel::getAllOrderbyNameDate();
$timerecordingemployees = TimerecordingEmployeeModel::getAll();
$enddate = strtotime("01." . '12.2024');
$enddate = strtotime("last day of this month", $enddate);
$enddate = strtotime("23:59:59", $enddate);
$holidays = [];
$generateTimerecording=new TimerecordingController();
$newtimerecordings=$generateTimerecording->getTimerecordingsApi(6, '1672527600', '1672527600', '1672527600', '1672527600', '1735685940', 25);
var_dump($newtimerecordings);
die();
foreach ($timerecordingemployees as $timerecordingemployee) {
$holidays[$timerecordingemployee->user_id] = $this->getholidays($timerecordingemployee->user_id, $enddate);
}
$spreadsheet = new Spreadsheet();
$sheet = $spreadsheet->getActiveSheet();
$sheet->setTitle('Monatsübersicht');
$headers = [
'Monat', 'Mitarbeiter', 'LZ', 'NLZ', 'Urlaub', 'LZ + NLZ',
'Sollst.', 'Sollabweichung', 'Mehrstunden', 'Gutstunden'
];
if ($this->me->superexpertEnabled()) {
$headers[] = 'Black P. St.';
}
$sheet->fromArray($headers, NULL, 'A1');
$sheet->getStyle('A1:' . $sheet->getHighestColumn() . '1')->getFont()->setBold(true);
$row = 2;
$oldEmployee_id = "";
$bpadiff = 0;
$overtimediff = 0;
$plushoursdiff = 0;
foreach ($timerecordingBillingsEmployees as $timerecordingbillingsemployee) {
// Daten vorbereiten (Logik aus der View übernehmen)
$user = new User($timerecordingbillingsemployee->timerecordingEmployee->user->id);
$employee_number = (string)$user->getFlag('employee_number');
$nlz_text = "";
if ($timerecordingbillingsemployee->nlz) {
$nlz_details = json_decode($timerecordingbillingsemployee->nlz, true);
foreach ($nlz_details as $key => $nlz_detail) {
if ($nlz_detail > 0) {
if ($nlz_detail < 100) {
$nlz_text .= $key . ": " . $nlz_detail . " Tag(e)\n";
} else {
$nlz_text .= $key . ": " . round($nlz_detail / 3600, 2) . " Stunden\n";
}
}
}
}
$nlz_text = rtrim($nlz_text, "\n"); // Letzten Zeilenumbruch entfernen
if ($oldEmployee_id != $timerecordingbillingsemployee->timerecordingEmployee_id) {
$bpadiff = 0;
$overtimediff = 0;
$plushoursdiff = 0;
}
$bpadiff += $timerecordingbillingsemployee->transfer_bpahours;
$overtimediff += $timerecordingbillingsemployee->transfer_overtime;
$plushoursdiff += $timerecordingbillingsemployee->transfer_plushours;
// Werte berechnen
$ishours = round($timerecordingbillingsemployee->ishours / 3600, 2);
$ishourssum = round($timerecordingbillingsemployee->ishourssum / 3600, 2);
$musthours = round($timerecordingbillingsemployee->musthours / 3600, 2);
$deviation = round(($timerecordingbillingsemployee->ishourssum - $timerecordingbillingsemployee->musthours) / 3600, 2);
$plushours_all = round(($timerecordingbillingsemployee->plushours_all + $timerecordingbillingsemployee->transfer_plushours) / 3600, 2);
$overtime_now = round(($timerecordingbillingsemployee->timerecordingEmployee->overtime_now + $overtimediff) / 3600, 2);
$bpahours = round(($timerecordingbillingsemployee->timerecordingEmployee->bpahours + $bpadiff) / 3600, 2);
// Zeile mit Daten füllen
$sheet->setCellValue('A' . $row, $timerecordingbillingsemployee->timerecordingBilling->month);
$sheet->setCellValue('B' . $row, "(" . $employee_number . ") " . $timerecordingbillingsemployee->timerecordingEmployee->user->name);
$sheet->setCellValue('C' . $row, $ishours);
$sheet->setCellValue('D' . $row, $nlz_text);
$sheet->setCellValue('E' . $row, $holidays[$timerecordingbillingsemployee->timerecordingEmployee->user_id]);
$sheet->setCellValue('F' . $row, $ishourssum);
$sheet->setCellValue('G' . $row, $musthours);
$sheet->setCellValue('H' . $row, $deviation);
$sheet->setCellValue('I' . $row, $plushours_all);
$sheet->setCellValue('J' . $row, $overtime_now);
if ($this->me->superexpertEnabled()) {
$sheet->setCellValue('K' . $row, $bpahours);
}
$oldEmployee_id = $timerecordingbillingsemployee->timerecordingEmployee_id;
$row++;
}
// 5. Spalten formatieren und optimieren
// Zahlenformat für Stunden (2 Dezimalstellen)
$number_format_code = '#,##0.00';
$sheet->getStyle('C2:C' . $row)->getNumberFormat()->setFormatCode($number_format_code);
$sheet->getStyle('F2:F' . $row)->getNumberFormat()->setFormatCode($number_format_code);
$sheet->getStyle('G2:G' . $row)->getNumberFormat()->setFormatCode($number_format_code);
$sheet->getStyle('H2:H' . $row)->getNumberFormat()->setFormatCode($number_format_code);
$sheet->getStyle('I2:I' . $row)->getNumberFormat()->setFormatCode($number_format_code);
$sheet->getStyle('J2:J' . $row)->getNumberFormat()->setFormatCode($number_format_code);
if ($this->me->superexpertEnabled()) {
$sheet->getStyle('K2:K' . $row)->getNumberFormat()->setFormatCode($number_format_code);
}
$sheet->getStyle('D2:D' . $row)->getAlignment()->setWrapText(true);
foreach (range('A', $sheet->getHighestColumn()) as $columnID) {
$sheet->getColumnDimension($columnID)->setAutoSize(true);
}
$writer = new Xlsx($spreadsheet);
$filename = 'Monatsuebersicht_' . date('Y-m-d') . '.xlsx';
header('Content-Type: application/vnd.openxmlformats-officedocument.spreadsheetml.sheet');
header('Content-Disposition: attachment;filename="' . $filename . '"');
header('Cache-Control: max-age=0');
// Datei an den Browser senden
$writer->save('php://output');
exit();
}
protected function getholidays($userid, $enddate)
{
$employee = TimerecordingEmployeeModel::search(['user_id' => $userid]);
if ($employee) {
$employee = $employee[0];
$holidays = $employee->holidays;
$holidays_now = $employee->holidays_now;
$holidays_timestamp = $employee->holidays_timestamp;
$workinghours = TimerecordingEmployeeWorkingHourModel::search(['user_id' => $userid]);
$realHolidays = TimerecordingHolidayModel::getAll();
foreach ($realHolidays as $realHoliday) {
$realholiDay[date('Y-m-d', $realHoliday->timestamp)] = $realHoliday->timestamp;
}
if (!$holidays_timestamp) {
//$holidays_timestamp = $employee->startdate;
$holidays_timestamp = strtotime('2024-01-01 00:00:00');
$holidays_now = $holidays;
}
$timerecordings = TimerecordingModel::search(['user_id' => $userid, 'start' => $holidays_timestamp, 'timerecordingCategory_id' => 3]);
$timerecordingscorrections = TimerecordingModel::search(['user_id' => $userid, 'start' => $holidays_timestamp, 'endsdate' => $enddate, 'days' => 1]);
foreach ($timerecordings as $timerecording) {
if ($timerecording->end > $enddate) {
$timerecording->end = $enddate;
}
$daycounter = ($timerecording->end - $timerecording->start) / 86400;
$daycounter = intval(round($daycounter, 0, PHP_ROUND_HALF_DOWN));
$daycounter = $daycounter * 86400;
if (is_int($daycounter)) {
for ($i = 86400; $i <= $daycounter; $i = $i + 86400) {
$holidayDays[date("Y-m-d", $timerecording->start + $i - 86400)] = 1;
}
}
}
foreach ($workinghours as $workinghour) {
$whstart = strtotime(date('Y-m-d', time()) . " " . $workinghour->start . ":00");
$whend = strtotime(date('Y-m-d', time()) . " " . $workinghour->end . ":00");
if (!$workingHours[$workinghour->day]) {
$workingHours[$workinghour->day] = $whend - $whstart;
} else {
$workingHours[$workinghour->day] = $workingHours[$workinghour->day] + $whend - $whstart;
}
}
$workinghourshistory = TimerecordingEmployeeWorkingHourHistoryModel::search(['user_id' => $userid]);
if ($workinghourshistory) {
$workingHoursHistory[2147483500] = $workingHours;
foreach ($workinghourshistory as $workinghourhistory) {
$whenddate = $workinghourhistory->enddate;
$workinghourhistoryhours = json_decode($workinghourhistory->workinghours, true);
foreach ($workinghourhistoryhours as $workinghourhistoryhour) {
$whstart = strtotime(date('Y-m-d', time()) . " " . $workinghourhistoryhour['start'] . ":00");
$whend = strtotime(date('Y-m-d', time()) . " " . $workinghourhistoryhour['end'] . ":00");
if (!$workingHoursHistory[$whenddate][$workinghourhistoryhour['day']]) {
$workingHoursHistory[$whenddate][$workinghourhistoryhour['day']] = $whend - $whstart;
} else {
$workingHoursHistory[$whenddate][$workinghourhistoryhour['day']] = $workingHoursHistory[$whenddate][$workinghourhistoryhour['day']] + $whend - $whstart;
}
}
}
}
//check if holiday is already in the list
foreach ($holidayDays as $key => $holidayDay) {
if ($workingHoursHistory) {
foreach ($workingHoursHistory as $whkey => $whdata) {
$whtimestamp = strtotime(date('Y-m-d 23:59:59', $whkey));
$timestamp = strtotime($key);
if ($whtimestamp >= $timestamp) {
$workingHours = $whdata;
}
}
}
if (($realholiDay[$key])) {
} else if ($workingHours[date('w', strtotime($key))]) {
$holidays_now--;
}
}
foreach ($timerecordingscorrections as $timerecordingscorrection) {
$holidays_now = $holidays_now + $timerecordingscorrection->days;
}
}
return $holidays_now;
}
}

View File

@@ -567,7 +567,7 @@ $(document).ready(function () {
$('#dynamictime-div').show();
$('#dataweek-col').hide();
$('#datayear-col').hide();
} else if ($(this).data('datatype') == "3") {
} else if ($(this).data('datatype') == "3" || $(this).data('datatype') == "5") {
$('#datayear-col').show();
$('#dynamictime-div').hide();
$('#datamonth-col').hide();