Merge branch 'master' into fronkdev

This commit is contained in:
Frank Schubert
2025-03-05 17:35:35 +01:00
23 changed files with 1382 additions and 512 deletions

View File

@@ -37,6 +37,7 @@ endforeach;
<script type="text/javascript"
src="<?= self::getResourcePath() ?>assets/js/calendar/rrule/rrule.min.js?<?= $git_merge_ts ?>"></script>
<script type="text/javascript"
src="<?= self::getResourcePath() ?>assets/js/calendar/moment/moment.min.js?<?= $git_merge_ts ?>"></script>
<script type="text/javascript"
@@ -57,6 +58,7 @@ endforeach;
src="<?= self::getResourcePath() ?>assets/js/calendar/eventsource.min.js?<?= $git_merge_ts ?>"></script>
<link href="<?= self::getResourcePath() ?>assets/css/datatables-std.css?<?= $git_merge_ts ?>" rel="stylesheet"
type="text/css"/>
<!-- start page title -->
<div class="row">
<div class="col-12">
@@ -102,6 +104,7 @@ endforeach;
class="btn btn-light btn-light-search top-search-filter"><i
class="fa-duotone fa-solid fa-phone-volume fa-calendar-call fa-calendar-call-search"></i>
</button>
</div>
</div>
<?php if (!$rights) : ?>
@@ -109,7 +112,14 @@ endforeach;
<span class="font-18 font-weight-500">Du wurdest nicht für den Kalender frei geschalten.</span>
</div>
<?php die(); endif; ?>
<div class="d-inline-block mr-1">
<button title="Excel Export"
class="btn btn-light btn-xls-calendar">
<i class="fa-duotone fa-solid fa-file-xls fa-xls-calendar"></i>
</div>
</button>
<div class="d-inline-block w-50 search-div" style="margin-top: -7px;">
<select id="jumpevent" class="jumpevent"></select>
</div>
@@ -349,129 +359,219 @@ endforeach;
</div>
</div>
<div class="modal fade" id="EventModal" aria-labelledby="EventModalLabel" aria-hidden="true">
<div class="modal-dialog modal-lg modal-dialog-centered">
<div class="modal-content">
<div class="modal-header">
<h1 class="modal-title font-18 ml-2" id="EventModalLabel"><i
class="fa-duotone fa-solid fa-calendar-symbol"></i> <span>neuer Termin</span></h1>
<button type="button" class="close" data-dismiss="modal" aria-label="Close">
<span aria-hidden="true">&times;</span>
</button>
</div>
<div class="modal-body">
<div class="row justify-content-center">
<div class="col-1">
<label for="name" class="col-form-label fw-medium ">Betreff</label>
</div>
<div class="col-6">
<div class="mb-2">
<input type="text" class="form-control is-require eventmodal-input" id="name">
</div>
</div>
<div class="col-3">
<div class="input-group mb-2">
<span title="Erinnerung" class="input-group-text spanwidht">Typ</span>
<select class="form-control form-select select-2" aria-label="Default select" id="type">
<option value="1">Termin</option>
<option value="2">IBN Xinon</option>
<option value="3">IBN ESTMK</option>
<option value="4">IBN SNOPP</option>
<option value="5">Störung</option>
<option value="6">Support Gespräch</option>
</select>
</div>
</div>
<div class="col-1 text-center">
<i title="Normal" class="fa-duotone privacy-click fa-regular fa-unlock mt-1"></i>
</div>
<div class="modal fade" id="EventModal" aria-labelledby="EventModalLabel" aria-hidden="true">
<div class="modal-dialog modal-lg modal-dialog-centered">
<div class="modal-content">
<div class="modal-header">
<h1 class="modal-title font-18 ml-2" id="EventModalLabel"><i
class="fa-duotone fa-solid fa-calendar-symbol"></i> <span>neuer Termin</span></h1>
<button type="button" class="close" data-dismiss="modal" aria-label="Close">
<span aria-hidden="true">&times;</span>
</button>
</div>
<div class="row justify-content-center">
<div class="col-1">
<label for="location" class="col-form-label fw-medium ">Ort</label>
</div>
<div class="col-6">
<div class="mb-2">
<input type="text" class="form-control eventmodal-input" id="location">
<div class="modal-body">
<div class="row justify-content-center">
<div class="col-1">
<label for="name" class="col-form-label fw-medium ">Betreff</label>
</div>
<div class="col-6">
<div class="mb-2">
<input type="text" class="form-control is-require eventmodal-input" id="name">
</div>
</div>
<div class="col-3">
<div class="input-group mb-2">
<span title="Erinnerung" class="input-group-text spanwidht">Typ</span>
<select class="form-control form-select select-2" aria-label="Default select" id="type">
<option value="1">Termin</option>
<option value="2">IBN Xinon</option>
<option value="3">IBN ESTMK</option>
<option value="4">IBN SNOPP</option>
<option value="5">Störung</option>
<option value="6">Support Gespräch</option>
</select>
</div>
</div>
<div class="col-1 text-center">
<i title="Normal" class="fa-duotone privacy-click fa-regular fa-unlock mt-1"></i>
</div>
</div>
<div class="col-4">
<div class="input-group mb-2">
<div class="row justify-content-center">
<div class="col-1">
<label for="location" class="col-form-label fw-medium ">Ort</label>
</div>
<div class="col-6">
<div class="mb-2">
<input type="text" class="form-control eventmodal-input" id="location">
</div>
</div>
<div class="col-4">
<div class="input-group mb-2">
<span title="Erinnerung" class="input-group-text spanwidht"><i
class="fa-regular fa-eye"></i></span>
<select class="form-control form-select" aria-label="Default select" id="busy">
<option value="1">gebucht</option>
<option value="0">frei</option>
<option value="2">mit Vorbehalt</option>
</select>
</div>
</div>
</div>
<div class="row justify-content-center">
<div class="col-4">
<div class="input-group mb-2">
<span class="input-group-text spanwidht">Start</span>
<input id="start-date" type="date" class="form-control is-require eventmodal-input"
placeholder="Datum"
aria-label="Datum"
aria-describedby="Datum">
</div>
</div>
<div class="col-2">
<input id="start-time" type="time" class="form-control is-require eventmodal-input"
placeholder="Uhrzeit"
aria-label="Uhrzeit"
aria-describedby="Uhrzeit">
</div>
<div class="col-3 text-center">
<div class="form-check" style="margin-top: 7px;">
<input class="form-check-input eventmodal-checkbox" type="checkbox" value=""
id="allday"> <label class="form-check-label fw-medium checkbox-label" for="allday">
Ganztägig
</label>
<select class="form-control form-select" aria-label="Default select" id="busy">
<option value="1">gebucht</option>
<option value="0">frei</option>
<option value="2">mit Vorbehalt</option>
</select>
</div>
</div>
</div>
</div>
<div class="row justify-content-center">
<div class="row justify-content-center">
<div class="col-4">
<div class="input-group mb-2">
<span class="input-group-text spanwidht">Ende</span>
<input id="end-date" type="date" class="form-control is-require eventmodal-input"
placeholder="Datum"
aria-label="Datum"
aria-describedby="Datum">
<div class="col-4">
<div class="input-group mb-2">
<span class="input-group-text spanwidht">Start</span>
<input id="start-date" type="date" class="form-control is-require eventmodal-input"
placeholder="Datum"
aria-label="Datum"
aria-describedby="Datum">
</div>
</div>
<div class="col-2">
<input id="start-time" type="time" class="form-control is-require eventmodal-input"
placeholder="Uhrzeit"
aria-label="Uhrzeit"
aria-describedby="Uhrzeit">
</div>
<div class="col-2 text-center">
<div class="form-check" style="margin-top: 7px;">
<input class="form-check-input eventmodal-checkbox" type="checkbox" value=""
id="allday"> <label class="form-check-label fw-medium checkbox-label"
for="allday">
Ganztägig
</label>
</div>
</div>
<div class="col-2 text-right">
<div class="form-check" style="margin-top: 7px;">
<input class="form-check-input eventmodal-checkbox" type="checkbox" value=""
id="recurringCheck">
<label class="form-check-label fw-medium checkbox-label" for="recurringCheck">
Serientermin
</label>
</div>
</div>
</div>
<div class="col-2">
<input id="end-time" type="time" class="form-control is-require eventmodal-input"
placeholder="Uhrzeit"
aria-label="Uhrzeit"
aria-describedby="Uhrzeit">
</div>
<div class="col-3">
<div class="input-group mb-2">
<div class="row justify-content-center">
<div class="col-4">
<div class="input-group mb-2">
<span class="input-group-text spanwidht">Ende</span>
<input id="end-date" type="date" class="form-control is-require eventmodal-input"
placeholder="Datum"
aria-label="Datum"
aria-describedby="Datum">
</div>
</div>
<div class="col-2">
<input id="end-time" type="time" class="form-control is-require eventmodal-input"
placeholder="Uhrzeit"
aria-label="Uhrzeit"
aria-describedby="Uhrzeit">
</div>
<div class="col-4">
<div class="input-group mb-2">
<span title="Erinnerung" class="input-group-text spanwidht"><i
class="fa-regular fa-bell"></i></span>
<select class="form-control form-select select2" aria-label="Default select" id="reminder">
<option value="NULL">Keine</option>
<option value="0">Zum Termin</option>
<option value="300">5 Minuten</option>
<option value="600">10 Minuten</option>
<option value="900">15 Minuten</option>
<option value="1800">30 Minuten</option>
<option value="3600">1 Stunde</option>
<option value="86400">1 Tag</option>
<option value="604800">1 Woche</option>
<select class="form-control form-select select2" aria-label="Default select"
id="reminder">
<option value="NULL">Keine</option>
<option value="0">Zum Termin</option>
<option value="300">5 Minuten</option>
<option value="600">10 Minuten</option>
<option value="900">15 Minuten</option>
<option value="1800">30 Minuten</option>
<option value="3600">1 Stunde</option>
<option value="86400">1 Tag</option>
<option value="604800">1 Woche</option>
</select>
</div>
</div>
</div>
<!-- Serientermin-Checkbox und zugehörige RRule-Optionen -->
<div class="row" id="recurring-settings" style="display:none;">
<div class="col-1"></div>
<div class="col-3"
">
<!-- Frequenz: Täglich / Wöchentlich / Monatlich / Jährlich -->
<div class="mb-2">
<label for="rrule-frequency" class="col-form-label fw-medium">Wiederholungstyp</label>
<select class="form-control" id="rrule-frequency">
<option value="">Bitte wählen</option>
<option value="DAILY">Täglich</option>
<option value="WEEKLY">Wöchentlich</option>
<option value="MONTHLY">Monatlich</option>
<option value="YEARLY">Jährlich</option>
</select>
</div>
<div class="mb-2">
<label for="rrule-count" class="col-form-label fw-medium">Wiederhol. (optional)</label>
<input type="number" min="1" class="form-control" id="rrule-count" placeholder="z.B. 10">
</div>
<!-- Enddatum der Serie (optional) -->
<div class="mb-2">
<label for="rrule-until" class="col-form-label fw-medium">Ende (optional)</label>
<input type="date" class="form-control" id="rrule-until">
</div>
</div>
<!-- Wöchentliche Optionen (z.B. jeden Dienstag, jeden Freitag + Montag) -->
<div class="col-7">
<div class="mb-2" id="weekly-options" style="display:none;">
<label for="rrule-byweekday" class="col-form-label fw-medium">Wochentag(e)</label>
<select class="form-control" id="rrule-byweekday" multiple>
<option value="MO">Montag</option>
<option value="TU">Dienstag</option>
<option value="WE">Mittwoch</option>
<option value="TH">Donnerstag</option>
<option value="FR">Freitag</option>
<option value="SA">Samstag</option>
<option value="SU">Sonntag</option>
</select>
</div>
<div class="mb-2" id="monthly-options" style="display:none;">
<label class="col-form-label fw-medium">Monatliches Muster</label>
<select class="form-control" id="monthly-type">
<option value="BYMONTHDAY">Jeden X. Tag des Monats</option>
<option value="BYSETPOS">Jeden X. [Wochentag] im Monat</option>
</select>
<div class=" mt-2 row" id="monthly-day-select">
<label class="mr-2 col-form-label fw-medium col-2">Tag:</label>
<select class="form-control w-auto col-3" id="rrule-bymonthday">
<!-- Auswahl 131 -->
<?php for ($i = 1; $i <= 31; $i++): ?>
<option value="<?= $i ?>"><?= $i ?></option>
<?php endfor; ?>
</select>
</div>
<div class="mt-2 row" id="monthly-setpos-select" style="display:none;">
<label class="mr-2 col-2 col-form-label fw-medium">Position:</label>
<select class="form-control col-3 w-auto" id="rrule-setpos">
<option value="1">Erster</option>
<option value="2">Zweiter</option>
<option value="3">Dritter</option>
<option value="4">Vierter</option>
<option value="-1">Letzter</option>
</select>
<label class="mr-2 col-2 ml-3 col-form-label fw-medium text-center">Tag:</label>
<select class="form-control col-3 w-auto" id="rrule-bynweekday">
<option value="MO">Montag</option>
<option value="TU">Dienstag</option>
<option value="WE">Mittwoch</option>
<option value="TH">Donnerstag</option>
<option value="FR">Freitag</option>
<option value="SA">Samstag</option>
<option value="SU">Sonntag</option>
</select>
</div>
</div>
</div>
</div>
@@ -570,6 +670,7 @@ endforeach;
</div>
<div class="d-inline-block" id="customer-info-check-info"></div>
</div>
<div id="relContainer2" style="position:relative">
<select id="customer" class="jumpevent"></select>
</div>
@@ -651,13 +752,16 @@ endforeach;
</div>
<div id="output"></div>
<script type="text/javascript" src="<?= self::getResourcePath() ?>plugins/select2/js/i18n/de.js?<?= $git_merge_ts ?>"></script>
<script type="text/javascript" src="<?=self::getResourcePath()?>plugins/tinymce/tinymce.min.js?<?= $git_merge_ts ?>"></script>
<script type="text/javascript"
src="<?= self::getResourcePath() ?>plugins/select2/js/i18n/de.js?<?= $git_merge_ts ?>"></script>
<script type="text/javascript"
src="<?= self::getResourcePath() ?>plugins/tinymce/tinymce.min.js?<?= $git_merge_ts ?>"></script>
<script>
let requestUrl = "<?= self::getUrl("Calendar", "api", ['do' => 'getCalendarEvents']) ?>";
let requestEventUrl = "<?= self::getUrl("Calendar", "api", ['do' => 'getCalendarEvent']) ?>";
let requestEventSearchURL = "<?= self::getUrl("Calendar", "api", ['do' => 'searchCalendarEvents']) ?>";
let requestgenerateEventsxlsx = "<?= self::getUrl("Calendar", "api", ['do' => 'generateCalendarEventsXlsx']) ?>";
let requestEventAttachmentUrl = "<?= self::getUrl("Calendar", "api", ['do' => 'getCalendarEventAttachment']) ?>";
let requestEventAttachmentTmpUrl = "<?= self::getUrl("Calendar", "api", ['do' => 'getCalendarEventAttachmentTmp']) ?>";
let requestEventAttachmentTmpDeleteUrl = "<?= self::getUrl("Calendar", "api", ['do' => 'deleteCalendarEventAttachmentTmp']) ?>";
@@ -728,7 +832,6 @@ endforeach;
endforeach; ?>
</script>
<script type="text/javascript"
src="<?= self::getResourcePath() ?>js/pages/Calendar/View.js?<?= $git_merge_ts ?>"></script>

View File

@@ -184,7 +184,7 @@ $pagination_entity_name = "Zustimmungserklärungen";
'key' => 'inspection_planner',
'header' => 'Begehung Planer durchgeführt',
'icon' => 'fas fa-clipboard-check',
'denominator' => $stats['building'] + $stats['street'],
'denominator' => $stats['building'],
'color' => '#28a745'
],
[
@@ -198,7 +198,7 @@ $pagination_entity_name = "Zustimmungserklärungen";
'key' => 'conduit_installed_ftu',
'header' => 'Leerrohr bis HAK',
'icon' => 'fas fa-road',
'denominator' => $stats['street'],
'denominator' => $stats['building'],
'color' => '#f5b902'
],
[

View File

@@ -142,6 +142,12 @@
$('body').on('click', '#bmd-export-nlz', function () {
window.open('<?= self::getUrl("TimerecordingBilling", "api", ['do' => 'generatebmdexportnlz']) ?>&month=' + $('#month').data('month'), '_blank');
});
$('body').on('click', '#open-workdays', function () {
window.open('<?= self::getUrl("TimerecordingBilling", "api", ['do' => 'generateopenworkdays']) ?>&month=' + $('#month').data('month'), '_blank');
});
$('body').on('click', '#month-complete', function () {
if (confirm('Monat ' + $('#month').data('month') + ' wirklich abschließen?')) {
$('#datatable_wrapper').hide();

View File

@@ -92,7 +92,7 @@
</li>
<?php endif; ?>
<?php if($me->is(["Admin", "netowner", "salespartner"]) && ($me->is("employee") || $me->can(["Fibu", "Billing", "Preorderpricing", "Preorderbilling"]) || $me->address_id == 2187)): ?>
<?php if($me->is(["Admin", "netowner", "salespartner"]) && ($me->is("employee") || $me->can(["Fibu", "Billing", "Preorderpricing", "Preorderbilling"])) || in_array($me->address_id, [1,209,5908,2187])): ?>
<li class="has-submenu mobile-hide">
<a href="#">
<i class="fas fa-fw fa-money-from-bracket"></i> Backoffice <div class="arrow-down"></div>

View File

@@ -75,13 +75,13 @@ class ADBHausnummer extends mfBaseModel {
}
public function getBuildingType() {
$rimo_type = strtolower($this->rimo_type);
$rimo_type = $this->rimo_type === NULL ? '' : strtolower($this->rimo_type);
if(array_key_exists($this->rimo_type, TT_ADB_GDA_TYPES)) {
return TT_ADB_GDA_TYPES[$this->rimo_type];
}
$gdaeigenschaft = strtolower($this->gdaeigenschaft);
$gdaeigenschaft = $this->gdaeigenschaft === NULL ? '' : strtolower($this->gdaeigenschaft);
if(array_key_exists($gdaeigenschaft, TT_ADB_GDA_TYPES)) {
return TT_ADB_GDA_TYPES[$gdaeigenschaft];
}

View File

@@ -1,5 +1,12 @@
<?php
use PhpOffice\PhpSpreadsheet\Spreadsheet;
use PhpOffice\PhpSpreadsheet\Writer\Xlsx;
use PhpOffice\PhpSpreadsheet\Style\Alignment;
use PhpOffice\PhpSpreadsheet\Style\Border;
use PhpOffice\PhpSpreadsheet\Style\Color;
use PhpOffice\PhpSpreadsheet\Helper\Html;
class CalendarController extends mfBaseController
{
@@ -45,6 +52,11 @@ class CalendarController extends mfBaseController
$calendarEvents = CalendarModel::searchCalendarEvents($r);
return $calendarEvents;
die();
case "generateCalendarEventsXlsx" :
$r = $this->request;
$this->generateXlsx($r);
return $calendarEvents;
die();
case "getCalendarEventAttachment" :
$r = $this->request;
$id = ($r->id);
@@ -109,7 +121,10 @@ class CalendarController extends mfBaseController
die();
case "insertCalendarEvent":
$r = $this->request;
$calendarEvents = CalendarModel::insertCalendarEvent($r, $this->me);
if ($r->rruleData) {
$rrules = json_encode($this->generateGraphRecurrence($r->rruleData, $r->start));
}
$calendarEvents = CalendarModel::insertCalendarEvent($r, $this->me, $rrules);
if ($r->customer_info_check) {
if ($r->customer_info_type == 1) {
$body = $r->customer_info_text;
@@ -389,6 +404,186 @@ class CalendarController extends mfBaseController
die();
}
private function generateGraphRecurrence($data, $startDate)
{
$graph = [
'pattern' => [],
'range' => []
];
$freq = strtoupper($data['rrule_frequency']);
switch ($freq) {
case 'DAILY':
$graph['pattern']['type'] = 'daily';
break;
case 'WEEKLY':
$graph['pattern']['type'] = 'weekly';
break;
case 'MONTHLY':
if (!empty($data['monthly_type']) && $data['monthly_type'] === 'BYMONTHDAY') {
$graph['pattern']['type'] = 'absoluteMonthly';
$graph['pattern']['dayOfMonth'] = intval($data['rrule_bymonthday']);
} elseif (!empty($data['monthly_type']) && $data['monthly_type'] === 'BYSETPOS') {
$graph['pattern']['type'] = 'relativeMonthly';
// Mapping: 1 => first, 2 => second, 3 => third, 4 => fourth, -1 => last
$setpos = intval($data['rrule_setpos']);
$indexMapping = [
1 => 'first',
2 => 'second',
3 => 'third',
4 => 'fourth',
-1 => 'last'
];
$graph['pattern']['index'] = isset($indexMapping[$setpos]) ? $indexMapping[$setpos] : 'first';
if (!empty($data['rrule_bynweekday'])) {
$graph['pattern']['daysOfWeek'] = [$this->convertDayToGraph(strtoupper($data['rrule_bynweekday']))];
}
}
break;
case 'YEARLY':
$graph['pattern']['type'] = 'absoluteYearly';
break;
default:
$graph['pattern']['type'] = 'daily';
}
$graph['pattern']['interval'] = 1;
if ($freq === 'WEEKLY' && !empty($data['rrule-byweekday'])) {
$days = is_array($data['rrule-byweekday']) ? $data['rrule-byweekday'] : [$data['rrule-byweekday']];
$graphDays = [];
foreach ($days as $day) {
$graphDays[] = $this->convertDayToGraph(strtoupper($day));
}
$graph['pattern']['daysOfWeek'] = $graphDays;
}
$graph['range']['startDate'] = $startDate;
if (!empty($data['rrule_until'])) {
$graph['range']['endDate'] = $data['rrule_until'];
$graph['range']['type'] = 'endDate';
} elseif (!empty($data['rrule_count'])) {
$graph['range']['numberOfOccurrences'] = intval($data['rrule_count']);
$graph['range']['type'] = 'numbered';
} else {
$graph['range']['type'] = 'noEnd';
}
return $graph;
}
private function convertDayToGraph($dayAbbrev)
{
$mapping = [
'MO' => 'monday',
'TU' => 'tuesday',
'WE' => 'wednesday',
'TH' => 'thursday',
'FR' => 'friday',
'SA' => 'saturday',
'SU' => 'sunday'
];
return isset($mapping[$dayAbbrev]) ? $mapping[$dayAbbrev] : $dayAbbrev;
}
private function generateXlsx($r)
{
$calendarEvents = json_decode(CalendarModel::getCalendarEvents($this->me->id, 0, $r, true), true);
$eventTypes = CalendarModel::$eventTypes;
$xls = new Spreadsheet();
$sheet = $xls->getActiveSheet();
$writer = new Xlsx($xls);
$sheet->getStyle('A1:AN1')->getFont()->setBold(true);
$sheet->getStyle('A:E')->getAlignment()->setHorizontal(\PhpOffice\PhpSpreadsheet\Style\Alignment::HORIZONTAL_CENTER);
$sheet->getStyle('I:L')->getAlignment()->setHorizontal(\PhpOffice\PhpSpreadsheet\Style\Alignment::HORIZONTAL_CENTER);
$sheet->getColumnDimension('A')->setWidth(25);
$sheet->getColumnDimension('B')->setWidth(15);
$sheet->getColumnDimension('C')->setWidth(10);
$sheet->getColumnDimension('D')->setWidth(15);
$sheet->getColumnDimension('E')->setWidth(10);
$sheet->getColumnDimension('F')->setWidth(40);
$sheet->getColumnDimension('G')->setWidth(40);
$sheet->getColumnDimension('H')->setWidth(20);
$sheet->getColumnDimension('I')->setWidth(10);
$sheet->getColumnDimension('J')->setWidth(10);
$sheet->getColumnDimension('K')->setWidth(15);
$sheet->getColumnDimension('L')->setWidth(15);
$sheet->setCellValue([1, 1], 'Kalender')
->setCellValue([2, 1], 'Startdatum')
->setCellValue([3, 1], 'Startzeit')
->setCellValue([4, 1], 'Enddatum')
->setCellValue([5, 1], 'Endzeit')
->setCellValue([6, 1], 'Betreff')
->setCellValue([7, 1], 'Ort')
->setCellValue([8, 1], 'Beschreibung')
->setCellValue([9, 1], 'Termintyp')
->setCellValue([10, 1], 'Privat')
->setCellValue([11, 1], 'Sichtbarkeit')
->setCellValue([12, 1], 'Organisator')
;
$counter = 2;
foreach ($calendarEvents['data'] as $event) {
if ($event['privateflag']['privateflag']) {
$event['privateflag']['privateflag'] = "Ja";
} else {
$event['privateflag']['privateflag'] = "Nein";
}
if ($event['isorganizer']['isorganizer']) {
$event['isorganizer']['isorganizer'] = "Ja";
} else {
$event['isorganizer']['isorganizer'] = "Nein";
}
if ($event['busy']['busy'] == 1) {
$event['busy']['busy'] = "gebucht";
} else if ($event['busy']['busy'] == 2) {
$event['busy']['busy'] = "mit Vorbehalt";
} else {
$event['busy']['busy'] = "frei";
}
if ($event['allDay']['allDay'] == 0) {
$starttime = date("H:i", strtotime($event['cstart']['cstart']));
$endtime = date("H:i", strtotime($event['cend']['cend']));
$startdate = date("d.m.Y", strtotime($event['cstart']['cstart']));
$enddate = date("d.m.Y", strtotime($event['cend']['cend']));
} else {
$starttime = "";
$startdate = date("d.m.Y", strtotime($event['cstart']['cstart']));
$endtime = "";
$enddate = date("d.m.Y", strtotime($event['cend']['cend']) - 7500);
}
if ($event['event_type']['event_type'] != "") {
$helper = new Html();
$richText = $helper->toRichTextObject($event['description']['description']);
$calendarType = $eventTypes[$event['event_type']['event_type']];
$sheet->setCellValue([1, $counter], $event['calendar_name']['calendar_name'])
->setCellValue([2, $counter], $startdate)
->setCellValue([3, $counter], $starttime)
->setCellValue([4, $counter], $enddate)
->setCellValue([5, $counter], $endtime)
->setCellValue([6, $counter], $event['ccategory']['ccategory'])
->setCellValue([7, $counter], $event['location']['location'])
->setCellValue([8, $counter], $richText)
->setCellValue([9, $counter], $calendarType)
->setCellValue([10, $counter], $event['privateflag']['privateflag'])
->setCellValue([11, $counter], $event['busy']['busy'])
->setCellValue([12, $counter], $event['isorganizer']['isorganizer'])
;
$counter++;
}
}
header("Content-type: application/vnd.openxmlformats-officedocument.spreadsheetml.sheet");
header('Content-disposition: attachment; filename="Kalender-' . date('Y-m-d_H-i-s') . '-' . uniqid() . '.xlsx"');
$writer->save('php://output');
exit;
}
protected function indexAction()
{
$this->layout()->setTemplate("Calendar/Index");

View File

@@ -32,6 +32,15 @@ class CalendarModel
'690', // Spusu
'699' // 3 (Drei)
);
public static $eventTypes = array(
1 => 'Termin',
2 => 'IBN Xinon',
3 => 'IBN ESTMK',
4 => 'IBN Snopp',
5 => 'Störung',
6 => 'Support Gespräch'
);
public static $standardCalendarColors = [
'#b3d5f3', '#d0e2c0', '#bac2d4', '#c6d4c0', '#e3e3e3',
'#acdce5', '#cdb6c0', '#aae0e1', '#d9d9d9', '#d9e5d5',
@@ -75,9 +84,13 @@ class CalendarModel
{
$unicode_map = [
'\u00f6' => 'ö',
'u00f6' => 'ö',
'\u00fc' => 'ü',
'u00fc' => 'ü',
'\u00e4' => 'ä',
'u00e4' => 'ä',
'\u00df' => 'ß',
'u00df' => 'ß',
'\u00e9' => 'é',
'\u00e0' => 'à',
'\u00f3' => 'ó',
@@ -145,7 +158,7 @@ class CalendarModel
die();
}
public static function getCalendarEvents($me, $id = 0, $r = 0)
public static function getCalendarEvents($me, $id = 0, $r = 0, $returnvalues = false)
{
$rrulefreq = array('daily' => 'DAILY', 'weekly' => 'WEEKLY', 'relativeMonthly' => 'MONTHLY', 'yearly' => 'YEARLY', 'absoluteMonthly' => 'absoluteMonthly');
$calendar = self::search(array("user_id" => $me));
@@ -192,7 +205,7 @@ class CalendarModel
}
$sql = "SELECT `cal_events`.id,`cal_events`.categories, uuid, calendar_id, `cal_events`.user_id, start_time, end_time, timezone, all_day_event, `cal_events`.name,`cal_calendars`.name calendar_name, description, location, repeat_end_time, reminder, ctime,cname, mtime,mname, muser_id, busy, status, resource_event_id, private, rrule, `cal_events`.background, `cal_events`.files_folder_id, read_only, category_id, exception_for_event_id, recurrence_id, is_organizer,event_type,busy,recurrence,rrule_events FROM cal_events INNER JOIN `cal_calendars` ON (`cal_calendars`.`id`=`cal_events`.`calendar_id`) WHERE 1=1 $where ";
$sql = "SELECT `cal_events`.id,`cal_events`.categories, uuid, calendar_id, `cal_events`.user_id, start_time, end_time, timezone, all_day_event, `cal_events`.name,`cal_calendars`.name calendar_name, description, location, repeat_end_time, reminder, ctime,cname, mtime,mname, muser_id, busy, status, resource_event_id, private, rrule, `cal_events`.background, `cal_events`.files_folder_id, read_only, category_id, exception_for_event_id, recurrence_id, is_organizer,event_type,busy,recurrence,rrule_events FROM cal_events INNER JOIN `cal_calendars` ON (`cal_calendars`.`id`=`cal_events`.`calendar_id`) WHERE 1=1 $where ORDER BY start_time DESC";
$res = $dbcal->query($sql);
if ($dbcal->num_rows($res)) {
@@ -380,7 +393,7 @@ WHERE `TimerecordingCategory`.`hourday`!='1' AND `TimerecordingCategory`.`hourda
$json = json_encode($json);
if ($id == 0) {
if ($id == 0 && $returnvalues == false) {
echo trim($json);
die();
} else {
@@ -445,13 +458,15 @@ WHERE `TimerecordingCategory`.`hourday`!='1' AND `TimerecordingCategory`.`hourda
}
$Allcalendar['Daniel Whoknows'] = 2;
$Allcalendar['Stefan Plaschg'] = 26;
$res = $dbcal->select("cal_events", "id, uuid, calendar_id, user_id, start_time, end_time, timezone, all_day_event, name, description, location, repeat_end_time, reminder, ctime, mtime, muser_id, busy, status, resource_event_id, private, rrule, background, files_folder_id, read_only, category_id, exception_for_event_id, recurrence_id, is_organizer,event_type,customer,customer_info,customer_info_send,customer_info_reminder,busy,attendees,organizer,is_organizer,accepted", "1=1 AND id='" . $id . "' ORDER BY id");
$res = $dbcal->select("cal_events", "id, uuid, calendar_id, user_id, start_time, end_time, timezone, all_day_event, name, description, location, repeat_end_time, reminder, ctime, mtime, muser_id, busy, status, resource_event_id, private, rrule, background, files_folder_id, read_only, category_id, exception_for_event_id, recurrence_id, recurrence, is_organizer,event_type,customer,customer_info,customer_info_send,customer_info_reminder,busy,attendees,organizer,is_organizer,accepted", "1=1 AND id='" . $id . "' ORDER BY id");
if ($dbcal->num_rows($res)) {
$data = $dbcal->fetch_array($res);
if ($data['recurrence']) {
$recurrence = self::generateDataRecurrence(json_decode($data['recurrence'], true));
} else {
$recurrence = false;
}
$attachment = 0;
if ($data['all_day_event'] == 1) {
$starttime = date("Y-m-d", $data['start_time']);
$endtime = date("Y-m-d", $data['end_time']);
@@ -517,6 +532,7 @@ WHERE `TimerecordingCategory`.`hourday`!='1' AND `TimerecordingCategory`.`hourda
}
}
}
if (!$data['accepted'] && $data['busy'] == 1) {
$data['accepted']['ok'] = 1;
$data['accepted'] = json_encode($data['accepted']);
@@ -541,8 +557,9 @@ WHERE `TimerecordingCategory`.`hourday`!='1' AND `TimerecordingCategory`.`hourda
'privateflag' => array('privateflag' => $data['private'], 'order' => $data['private']),
'isorganizer' => array('isorganizer' => $data['is_organizer']),
'attendees' => array('attendees' => json_encode($AttendeeArray)),
'organizer' => array('organizer' => $data['organizer']),
'organizer' => array('organizer' => self::replace_unicode_sequences($data['organizer'])),
'accepted' => array('accepted' => $data['accepted']),
'recurrence' => array('recurrence' => $recurrence),
'attachment' => array('attachment' => $attachment, 'order' => $attachment),
'attachments' => array('attachments' => $attachments, 'order' => $attachments)
);
@@ -814,7 +831,7 @@ WHERE `TimerecordingCategory`.`hourday`!='1' AND `TimerecordingCategory`.`hourda
$db->insert("TheTool_CalendarQueue", $data);
}
public static function insertCalendarEvent($r, $me)
public static function insertCalendarEvent($r, $me, $rrules = "")
{
$description = ($r->description);
$attachments = ($r->attachments);
@@ -844,7 +861,10 @@ WHERE `TimerecordingCategory`.`hourday`!='1' AND `TimerecordingCategory`.`hourda
$microsoft_user_id = $result->ms_user_id;
}
if (!$allday)
{
$allday=0;
}
if ($reminder == 'NULL') {
$reminder = NULL;
}
@@ -877,7 +897,7 @@ WHERE `TimerecordingCategory`.`hourday`!='1' AND `TimerecordingCategory`.`hourda
$customer_info_send = NULL;
}
$dataarray = array("start_time" => $start, 'end_time' => $end, 'name' => $title, 'description' => $description, 'location' => $location, 'calendar_id' => $user_id, 'uuid' => "a5eb79b3-fca7-5378-a09e-" . rand(100000000000, 999999999999), 'user_id' => 1, 'timezone' => 'Europe/Amsterdam', 'all_day_event' => 0, 'repeat_end_time' => 0, 'reminder' => $reminder, 'ctime' => time(), 'cname' => $me->name, 'mtime' => time(), 'mname' => $me->name, 'user_id' => 1, 'busy' => $busy, 'status' => 'CONFIRMED', 'resource_event_id' => 0, 'private' => $privateflag, 'rrule' => '', 'background' => 'EBF1E2', 'files_folder_id' => 0, 'read_only' => 0,'categories'=>$categories, 'exception_for_event_id' => 0, 'recurrence_id' => 0, 'is_organizer' => 1, 'event_type' => $type, 'customer' => $customer, 'customer_info' => $customer_info, 'customer_info_send' => $customer_info_send, 'customer_info_reminder' => $customer_info_reminder_check);
$dataarray = array("start_time" => $start, 'end_time' => $end, 'name' => $title, 'description' => $description, 'location' => $location, 'calendar_id' => $user_id, 'uuid' => "a5eb79b3-fca7-5378-a09e-" . rand(100000000000, 999999999999), 'user_id' => 1, 'timezone' => 'Europe/Amsterdam', 'all_day_event' => $allday, 'repeat_end_time' => 0, 'reminder' => $reminder, 'ctime' => time(), 'cname' => $me->name, 'mtime' => time(), 'mname' => $me->name, 'user_id' => 1, 'busy' => $busy, 'status' => 'CONFIRMED', 'resource_event_id' => 0, 'private' => $privateflag, 'rrule' => '', 'background' => 'EBF1E2', 'files_folder_id' => 0, 'read_only' => 0, 'categories' => $categories, 'exception_for_event_id' => 0, 'recurrence_id' => 0, 'is_organizer' => 1, 'event_type' => $type, 'customer' => $customer, 'customer_info' => $customer_info, 'customer_info_send' => $customer_info_send, 'customer_info_reminder' => $customer_info_reminder_check);
$db->insert("cal_events", $dataarray);
$event_id = $dataarray['uuid'];
@@ -898,6 +918,9 @@ WHERE `TimerecordingCategory`.`hourday`!='1' AND `TimerecordingCategory`.`hourda
if ($attendees)
$dataarray['attendees'] = $attendees;
$dataarray['attachments'] = $attachments;
if ($rrules) {
$dataarray['rrule'] = $rrules;
}
$json_data = json_encode($dataarray);
$data = [];
@@ -972,6 +995,87 @@ WHERE `TimerecordingCategory`.`hourday`!='1' AND `TimerecordingCategory`.`hourda
die();
}
public static function generateDataRecurrence($graph)
{
$data = [];
switch ($graph['pattern']['type']) {
case 'daily':
$data['rrule_frequency'] = 'DAILY';
break;
case 'weekly':
$data['rrule_frequency'] = 'WEEKLY';
if (!empty($graph['pattern']['daysOfWeek'])) {
$data['rrule_byweekday'] = array_map(function ($day) {
return self::convertGraphToDay($day);
}, $graph['pattern']['daysOfWeek']);
}
break;
case 'absoluteMonthly':
$data['rrule_frequency'] = 'MONTHLY';
$data['monthly_type'] = 'BYMONTHDAY';
$data['rrule_bymonthday'] = intval($graph['pattern']['dayOfMonth']);
break;
case 'relativeMonthly':
$data['rrule_frequency'] = 'MONTHLY';
$data['monthly_type'] = 'BYSETPOS';
$indexMapping = [
'first' => 1,
'second' => 2,
'third' => 3,
'fourth' => 4,
'last' => -1
];
$index = isset($graph['pattern']['index']) ? $graph['pattern']['index'] : 'first';
$data['rrule_setpos'] = isset($indexMapping[$index]) ? $indexMapping[$index] : 1;
if (!empty($graph['pattern']['daysOfWeek'])) {
$data['rrule_bynweekday'] = self::convertGraphToDay($graph['pattern']['daysOfWeek'][0]);
}
break;
case 'absoluteYearly':
$data['rrule_frequency'] = 'YEARLY';
break;
default:
$data['rrule_frequency'] = 'DAILY';
}
// Falls benötigt, kann der Start-Datum-Wert übernommen werden.
if (!empty($graph['range']['startDate'])) {
$data['startDate'] = $graph['range']['startDate'];
}
// Wiederherstellen des Endbereichs
if (!empty($graph['range']['type'])) {
switch ($graph['range']['type']) {
case 'endDate':
$data['rrule_until'] = $graph['range']['endDate'];
break;
case 'numbered':
$data['rrule_count'] = intval($graph['range']['numberOfOccurrences']);
break;
case 'noEnd':
break;
}
}
return $data;
}
public static function convertGraphToDay($graphDay)
{
$mapping = [
'MONDAY' => 'MO',
'TUESDAY' => 'TU',
'WEDNESDAY' => 'WE',
'THURSDAY' => 'TH',
'FRIDAY' => 'FR',
'SATURDAY' => 'SA',
'SUNDAY' => 'SU'
];
$graphDay = strtoupper($graphDay);
return isset($mapping[$graphDay]) ? $mapping[$graphDay] : $graphDay;
}
public static function create(array $data)
{
$model = new Calendar();

View File

@@ -968,39 +968,24 @@ class ConstructionConsentController extends mfBaseController {
return ["message" => "ConstructionConsent saved successfully"];
}
private function generateStats($baseFilter = array()) {
private function generateStats($baseFilter = array()): array {
function getFilteredCount($wantedFilter, $filterValue, $baseFilter) {
if (!empty($baseFilter[$wantedFilter]) && $baseFilter[$wantedFilter] != $filterValue) {
return 0;
} else {
return ConstructionConsent::count(array_merge($baseFilter, [$wantedFilter => $filterValue]));
}
if (!empty($baseFilter[$wantedFilter]) && $baseFilter[$wantedFilter] != $filterValue) return 0;
return ConstructionConsent::count(array_merge($baseFilter, [$wantedFilter => $filterValue]));
}
$allCount = ConstructionConsent::count($baseFilter);
$streetCount = getFilteredCount("object_type", "street", $baseFilter);
$buildingCount = getFilteredCount("object_type", "building", $baseFilter);
$inspection_planner = getFilteredCount("inspection_planner", "!NULL", $baseFilter);
$conduit_installed_building = getFilteredCount("conduit_installed_building", "!NULL", $baseFilter);
$conduit_installed_ftu = getFilteredCount("conduit_installed_ftu", "!NULL", $baseFilter);
$inhouse_cabling = getFilteredCount("inhouse_cabling", "!NULL", $baseFilter);
$status_light_blue = getFilteredCount("status_light", "blue", $baseFilter);
$status_light_red = getFilteredCount("status_light", "red", $baseFilter);
$status_light_yellow = getFilteredCount("status_light", "yellow", $baseFilter);
$status_light_green = getFilteredCount("status_light", "green", $baseFilter);
return array(
"all" => $allCount,
"street" => $streetCount,
"building" => $buildingCount,
"inspection_planner" => $inspection_planner,
"conduit_installed_building" => $conduit_installed_building,
"conduit_installed_ftu" => $conduit_installed_ftu,
"inhouse_cabling" => $inhouse_cabling,
"status_light_blue" => $status_light_blue,
"status_light_red" => $status_light_red,
"status_light_yellow" => $status_light_yellow,
"status_light_green" => $status_light_green
);
return [
"all" => ConstructionConsent::count($baseFilter),
"street" => getFilteredCount("object_type", "street", $baseFilter),
"building" => getFilteredCount("object_type", "building", $baseFilter),
"inspection_planner" => getFilteredCount("inspection_planner", "!NULL", $baseFilter),
"conduit_installed_building" => getFilteredCount("conduit_installed_building", "!NULL", $baseFilter),
"conduit_installed_ftu" => getFilteredCount("conduit_installed_ftu", "!NULL", $baseFilter),
"inhouse_cabling" => getFilteredCount("inhouse_cabling", "!NULL", $baseFilter),
"status_light_blue" => getFilteredCount("status_light", "blue", $baseFilter),
"status_light_red" => getFilteredCount("status_light", "red", $baseFilter),
"status_light_yellow" => getFilteredCount("status_light", "yellow", $baseFilter),
"status_light_green" => getFilteredCount("status_light", "green", $baseFilter)
];
}
}

View File

@@ -927,11 +927,72 @@ class PreorderModel
return 0;
}
public static function countHistoryStatus($filter = [], $status_code = null)
{
if ($status_code === null) {
die("Please select a status code");
public static function countActivePreorder($preorderCampaignId = null) {
$db = FronkDB::singleton();
$where = "1=1 ";
if ($preorderCampaignId) {
$where .= " AND p.preordercampaign_id = $preorderCampaignId";
}
$sql = "SELECT
COALESCE(SUM(CASE
WHEN LOWER(vh.gdaeigenschaft) IN ('multi dwelling', 'mehrparteienhaus', 'gebaeude mit 2 oder mehr wohnungen', 'gebäude mit 2 oder mehr wohnungen', 'wohngebaeude fuer gemeinschaften', 'wohngebäude für gemeinschaften', 'hotels und aehnliche gebaeude', 'hotels und ähnliche gebäude', 'wohngebäude mit 2 und mehr wohnungen')
OR LOWER(vh.rimo_type) IN ('multi dwelling', 'mehrparteienhaus', 'gebaeude mit 2 oder mehr wohnungen', 'gebäude mit 2 oder mehr wohnungen', 'wohngebaeude fuer gemeinschaften', 'wohngebäude für gemeinschaften', 'hotels und aehnliche gebaeude', 'hotels und ähnliche gebäude', 'wohngebäude mit 2 und mehr wohnungen')
THEN COALESCE(p.connection_count, 1)
ELSE 0
END), 0) AS md_count,
COALESCE(SUM(COALESCE(p.connection_count, 1)), 0) AS total_count
FROM
`".FRONKDB_DBNAME."`.Preorder p
LEFT JOIN `".ADDRESSDB_DBNAME."`.view_hausnummer vh ON p.adb_hausnummer_id = vh.hausnummer_id
LEFT JOIN `".FRONKDB_DBNAME."`.Preorderstatus tt_preorderstatus ON p.status_id = tt_preorderstatus.id
WHERE p.deleted = 0 AND tt_preorderstatus.code < 899";
$queryStart = microtime(true);
$res = $db->query($sql . $where);
mfLoghandler::singleton()->debug("[Query took: ".(microtime(true) - $queryStart)." seconds] " . $sql);
if ($db->num_rows($res)) {
$data = $db->fetch_object($res);
return ['md_count' => $data->md_count, 'sd_count' => $data->total_count - $data->md_count, 'total_count' => $data->total_count];
}
return ['md_count' => 0, 'sd_count' => 0, 'total_count' => 0];
}
public static function countTotalUnits($preorderCampaignId = null) {
$db = FronkDB::singleton();
$where = "1=1 ";
if ($preorderCampaignId) {
$where .= " AND pc.id = $preorderCampaignId";
}
$sql = "SELECT
pc.id AS campaign_id,
COALESCE(SUM(n.unit_count), 0) AS total_unit_count,
COALESCE(SUM(n.unit_count_sd), 0) AS total_unit_count_sd,
COALESCE(SUM(n.unit_count_md), 0) AS total_unit_count_md
FROM Preordercampaign pc
LEFT JOIN `".FRONKDB_DBNAME."`.PreordercampaignSalescluster pcs ON pc.id = pcs.preordercampaign_id
LEFT JOIN `".ADDRESSDB_DBNAME."`.Netzgebiet n ON pcs.salescluster_id = n.id
WHERE $where
GROUP BY pc.id";
$queryStart = microtime(true);
$res = $db->query($sql);
mfLoghandler::singleton()->debug("[Query took: ".(microtime(true) - $queryStart)." seconds] " . $sql);
if ($db->num_rows($res)) {
$data = $db->fetch_object($res);
return ['total_unit_count' => $data->total_unit_count, 'total_unit_count_sd' => $data->total_unit_count_sd, 'total_unit_count_md' => $data->total_unit_count_md];
}
return ['total_unit_count' => 0, 'total_unit_count_sd' => 0, 'total_unit_count_md' => 0];
}
public static function countHistoryStatus($filter = [], $status_code = null) {
if ($status_code === null) {
die("Please select a status code");
}
if (!is_array($filter)) return false;

File diff suppressed because it is too large Load Diff

View File

@@ -263,7 +263,7 @@ class TimerecordingController extends mfBaseController
{
$r = $this->request;
$id = $r->id;
$oldbpseconds=0;
$oldbpseconds = 0;
$enddate = $r->enddate;
$hourday = $r->hourday;
@@ -530,7 +530,7 @@ class TimerecordingController extends mfBaseController
}
$workinghourshistory = TimerecordingEmployeeWorkingHourHistoryModel::search(['user_id' => $userid]);
if ($workinghourshistory) {
$workingHoursHistory[2147483500]=$workingHours;
$workingHoursHistory[2147483500] = $workingHours;
foreach ($workinghourshistory as $workinghourhistory) {
$whenddate = $workinghourhistory->enddate;
$workinghourhistoryhours = json_decode($workinghourhistory->workinghours, true);
@@ -547,13 +547,11 @@ class TimerecordingController extends mfBaseController
}
//check if holiday is already in the list
foreach ($holidayDays as $key => $holidayDay) {
if ($workingHoursHistory)
{
if ($workingHoursHistory) {
foreach ($workingHoursHistory as $whkey => $whdata) {
$whtimestamp = strtotime(date('Y-m-d 23:59:59', $whkey));
$timestamp = strtotime($key);
if ($whtimestamp >= $timestamp)
{
if ($whtimestamp >= $timestamp) {
$workingHours = $whdata;
}
}
@@ -672,7 +670,7 @@ class TimerecordingController extends mfBaseController
}
}
protected function getTimerecordingsApi($datatype, $dataweek, $datamonth, $datayear, $startime = null, $endtime = null, $userid = null)
public function getTimerecordingsApi($datatype, $dataweek, $datamonth, $datayear, $startime = null, $endtime = null, $userid = null, $ajax = 1)
{
$holidayrows = [];
$mustSeconds = 0;
@@ -680,7 +678,7 @@ class TimerecordingController extends mfBaseController
$holiDays = 0;
$plusHours = 0;
$startdate = time();
$allhours=0;
$allhours = 0;
if (!$userid) {
$userid = $this->me->id;
}
@@ -699,8 +697,8 @@ class TimerecordingController extends mfBaseController
$overtime = $employee[0]->overtime_now;
if ($employee[0]->only_admin) {
$getAllHours=TimerecordingModel::getAllHours($userid);
$getAllHours=$getAllHours[0]->gesamt_summe;
$getAllHours = TimerecordingModel::getAllHours($userid);
$getAllHours = $getAllHours[0]->gesamt_summe;
}
}
$workinghours = TimerecordingEmployeeWorkingHourModel::search(['user_id' => $userid]);
@@ -718,7 +716,7 @@ class TimerecordingController extends mfBaseController
$workinghourshistory = TimerecordingEmployeeWorkingHourHistoryModel::search(['user_id' => $userid]);
if ($workinghourshistory) {
$workingHoursHistory[2147483500]=$workingHours;
$workingHoursHistory[2147483500] = $workingHours;
foreach ($workinghourshistory as $workinghourhistory) {
$whenddate = $workinghourhistory->enddate;
$workinghourhistoryhours = json_decode($workinghourhistory->workinghours, true);
@@ -757,12 +755,10 @@ class TimerecordingController extends mfBaseController
}
$dDate = date('Y-m-d', $timestamp + $WintertimeCompensation);
$dDay = date('w', $timestamp + $WintertimeCompensation);
if ($workingHoursHistory)
{
if ($workingHoursHistory) {
foreach ($workingHoursHistory as $whkey => $whdata) {
$whtimestamp = strtotime(date('Y-m-d 23:59:59', $whkey));
if ($whtimestamp >= $timestamp)
{
if ($whtimestamp >= $timestamp) {
unset($workingHours);
$workingHours = $whdata;
}
@@ -792,12 +788,10 @@ class TimerecordingController extends mfBaseController
}
$dDate = date('Y-m-d', $timestamp + $WintertimeCompensation);
$dDay = date('w', $timestamp + $WintertimeCompensation);
if ($workingHoursHistory)
{
if ($workingHoursHistory) {
foreach ($workingHoursHistory as $whkey => $whdata) {
$whtimestamp = strtotime(date('Y-m-d 23:59:59', $whkey));
if ($whtimestamp >= $timestamp)
{
if ($whtimestamp >= $timestamp) {
unset($workingHours);
$workingHours = $whdata;
}
@@ -826,12 +820,10 @@ class TimerecordingController extends mfBaseController
}
$dDate = date('Y-m-d', $timestamp + $WintertimeCompensation);
$dDay = date('w', $timestamp + $WintertimeCompensation);
if ($workingHoursHistory)
{
if ($workingHoursHistory) {
foreach ($workingHoursHistory as $whkey => $whdata) {
$whtimestamp = strtotime(date('Y-m-d 23:59:59', $whkey));
if ($whtimestamp >= $timestamp)
{
if ($whtimestamp >= $timestamp) {
unset($workingHours);
$workingHours = $whdata;
}
@@ -859,12 +851,10 @@ class TimerecordingController extends mfBaseController
if (date('I', $timestamp) == 0) {
$WintertimeCompensation = 3600;
}
if ($workingHoursHistory)
{
if ($workingHoursHistory) {
foreach ($workingHoursHistory as $whkey => $whdata) {
$whtimestamp = strtotime(date('Y-m-d 23:59:59', $whkey));
if ($whtimestamp >= $timestamp)
{
if ($whtimestamp >= $timestamp) {
unset($workingHours);
$workingHours = $whdata;
// echo date('Y-m-d 23:59:59', $whkey).PHP_EOL;
@@ -908,6 +898,9 @@ class TimerecordingController extends mfBaseController
$sum = sprintf("%02d", $hours) . ":" . sprintf("%02d", $minutes);
$day = $daysgerm[date("w", $timerecording->start)];
$isSeconds = $isSeconds + $seconds;
if ($ajax == 0) {
$workdaycheck[] = $datadate;
}
} else if ($timerecording->timerecordingCategory->hourday == 2 || ($timerecording->timerecordingCategory->hourday == 3 && $timerecording->end)) {
$date = date("d.m.", $timerecording->start) . " - " . $daysgerm[date("w", $timerecording->end)] . " " . date("d.m.Y", $timerecording->end);
@@ -931,12 +924,10 @@ class TimerecordingController extends mfBaseController
$savecounter = 0;
$sumdays = 0;
for ($i = $starttimecalc; $i <= $endtimecalc; $i = $i + 86400) {
if ($workingHoursHistory)
{
if ($workingHoursHistory) {
foreach ($workingHoursHistory as $whkey => $whdata) {
$whtimestamp = strtotime(date('Y-m-d 23:59:59', $whkey));
if ($whtimestamp >= $i)
{
if ($whtimestamp >= $i) {
unset($workingHours);
$workingHours = $whdata;
}
@@ -944,6 +935,10 @@ class TimerecordingController extends mfBaseController
}
$holidaycounter = $workingHours[date("w", $i)];
$daycheck = date("Y-m-d", $i);
if ($ajax == 0) {
$workdaycheck[] = $daycheck;
}
if (!$holiDay[$daycheck]) {
if ($holidaycounter) {
$isSeconds = $isSeconds + $holidaycounter;
@@ -991,17 +986,19 @@ class TimerecordingController extends mfBaseController
$savecounter = 0;
// echo $starttimecalc."<br>";
for ($i = $starttimecalc; $i <= $endtimecalc; $i = $i + 86400) {
if ($workingHoursHistory)
{
if ($workingHoursHistory) {
foreach ($workingHoursHistory as $whkey => $whdata) {
$whtimestamp = strtotime(date('Y-m-d 23:59:59', $whkey));
if ($whtimestamp >= $i)
{
if ($whtimestamp >= $i) {
unset($workingHours);
$workingHours = $whdata;
}
}
}
if ($ajax == 0) {
$daycheck = date("Y-m-d", $i);
$workdaycheck[] = $daycheck;
}
$holidaycounter = $workingHours[$timerecording->user_id][date("w", $i)];
$daycheck = date("Y-m-d", $i);
if (!$holiDay[$daycheck]) {
@@ -1049,6 +1046,10 @@ class TimerecordingController extends mfBaseController
$sum = "-" . sprintf("%02d", $hours) . ":" . sprintf("%02d", $minutes);
$day = $daysgerm[date("w", $timerecording->start)];
$isSeconds = $isSeconds + $seconds;
if ($ajax == 0) {
$workdaycheck[] = $datadate;
}
} else if ($timerecording->timerecordingCategory->hourday == 7) {
$date = date("d.m.Y", $timerecording->start);
$datadate = date("Y-m-d", $timerecording->start);
@@ -1164,9 +1165,15 @@ class TimerecordingController extends mfBaseController
$json['data'] = $rows;
$json['recordsFiltered'] = $responsecount;
$json['recordsTotal'] = $responsecount;
$json = json_encode($json);
echo trim($json);
die();
if ($ajax == 1) {
$json = json_encode($json);
echo trim($json);
die();
} else {
$json['workcheck'] = $workdaycheck;
return $json;
}
}
}

View File

@@ -107,6 +107,9 @@ class TimerecordingBillingController extends mfBaseController
case "generatebmdexportnlz":
$return = $this->generateBmdExport($month, 1);
break;
case "generateopenworkdays":
$return = $this->generateopenworkdays($month);
break;
case "generatebmdexportclosed":
$return = $this->generateBmdExportClosed($month);
break;
@@ -383,6 +386,37 @@ class TimerecordingBillingController extends mfBaseController
exit;
}
protected function generateopenworkdays($month)
{
$filename = "open_workdays_" . $month . ".csv";
$file = fopen("php://output", 'w');
header('Content-Type: text/csv; charset=utf-8');
header('Content-Disposition: attachment; filename=' . $filename);
$headerarray = ["Monat", "Mitarbeiter", "Tag"];
fputcsv($file, $headerarray, ";");
$month = strtotime("01." . $month);
//last of month
$monthend = date("Y-m-d", strtotime("last day of this month", $month));
$monthend = strtotime($monthend . " 23:59:59");
$monthbmd = date("n", $month);
$timerecordingsEmolyees = TimerecordingEmployeeModel::getAll();
foreach ($timerecordingsEmolyees as $timerecordingsEmolyee) {
unset($bodyarray);
if ($timerecordingsEmolyee->bmd_active == 0 || $timerecordingsEmolyee->startdate > $monthend || ($timerecordingsEmolyee->enddate && $timerecordingsEmolyee->enddate < $month)) continue;
$user = new User($timerecordingsEmolyee->user_id);
$employee_number = (string)$user->getFlag('employee_number');
$WorkingDays = $this->checkWorkingDays($user->id, $month, $timerecordingsEmolyee);
foreach ($WorkingDays as $WorkingDay) {
$bodyarray = [date("m-Y", $month), mb_convert_encoding($user->name, 'ISO-8859-1', 'UTF-8'), date("d.m.Y", strtotime($WorkingDay))];
fputcsv($file, $bodyarray, ";");
}
}
fclose($file);
exit;
}
protected function generateBmdExport($month, $nlz = 0, $export = 1)
{
//create and download csv file
@@ -606,8 +640,8 @@ class TimerecordingBillingController extends mfBaseController
}
if ($timerecording['daysum']) {
$data['nlz'] = $timerecording['daysum'];
if (strpos($data['nlz'],"Urlaub aufbuchen") !== false) {
$holidays=$this->getholidays($timerecording['user_id'], $enddate);
if (strpos($data['nlz'], "Urlaub aufbuchen") !== false) {
$holidays = $this->getholidays($timerecording['user_id'], $enddate);
$data['holidays'] = $holidays;
}
}
@@ -879,4 +913,94 @@ class TimerecordingBillingController extends mfBaseController
return $holidays_now;
}
protected function checkWorkingDays($user_id, $month, $employee)
{
$holidays = TimerecordingHolidayModel::getAll();
$datamonth = $month;
$firstdate = strtotime(date("Y-m-01", $datamonth));
$lastdate = strtotime(date("Y-m-t", $datamonth));
$daycount = date("t", $datamonth);
$lastdate = strtotime(date("Y-m-d", $lastdate) . ' 23:59:59');
$startdate = $employee->startdate;
if ($employee->enddate) {
$enddate = strtotime(date('Y-m-d', $employee->enddate) . " 23:59:59");
} else {
$enddate = 2208985200;
}
$bpahours = $employee->bpahours;
$workinghours = TimerecordingEmployeeWorkingHourModel::search(['user_id' => $user_id]);
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' => $user_id]);
if ($workinghourshistory) {
$workingHoursHistory[9732489200] = $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;
}
}
}
}
foreach ($holidays as $holiday) {
$holiDay[date('Y-m-d', $holiday->timestamp)] = $holiday->timestamp;
}
//Lastdate staticmust deleted
// $lastdate = strtotime("2024-03-22 23:59:59");
// $daycount=22;
$searchArray = ['user_id' => $user_id, 'start' => $firstdate, 'end' => $lastdate];
$timestamp = $firstdate;
for ($i = 1; $i <= $daycount; $i++) {
$WintertimeCompensation = 0;
if (date('I', $timestamp) == 0) {
$WintertimeCompensation = 3600;
}
$dDate = date('Y-m-d', $timestamp + $WintertimeCompensation);
$dDay = date('w', $timestamp + $WintertimeCompensation);
if ($workingHoursHistory) {
foreach ($workingHoursHistory as $whkey => $whdata) {
$whtimestamp = strtotime(date('Y-m-d 23:59:59', $whkey));
if ($whtimestamp >= $timestamp) {
$workingHours = $whdata;
}
}
}
if (!$holiDay[$dDate] && $dDate >= date('Y-m-d', $startdate) && $dDate <= date('Y-m-d', $enddate)) {
$mustSeconds = $mustSeconds + $workingHours[$dDay];
if ($workingHours[$dDay]) {
$workDays[$dDate] = $dDate;
}
}
$timestamp = $timestamp + 86400;
}
$timerecordingC = new TimerecordingController();
$timerecordingC = $timerecordingC->getTimerecordingsApi('2', $month, $month, $month, 0, 0, $user_id, 0);
foreach ($timerecordingC['workcheck'] as $key => $value) {
unset($workDays[$value]);
}
return $workDays;
}
}

View File

@@ -0,0 +1,20 @@
<?php /** @noinspection ALL */
declare(strict_types=1);
use Phinx\Migration\AbstractMigration;
final class AddPreorderDbImprovements extends AbstractMigration {
public function up(): void {
if ($this->getEnvironment() == "thetool") {
$this->table("PreordercampaignSalescluster")->addIndex(["preordercampaign_id","salescluster_id"])->save();
$this->table("RimoWorkorder")->addIndex(["adb_wohneinheit_id"])->save();
}
}
public function down(): void {
if ($this->getEnvironment() == "thetool") {
$this->table("PreordercampaignSalescluster")->removeIndex(["preordercampaign_id","salescluster_id"])->save();
$this->table("RimoWorkorder")->removeIndex(["adb_wohneinheit_id"])->save();
}
}
}

View File

@@ -1,17 +1,16 @@
<?php
class FronkDB {
public $link;
private $result;
private $lastError;
private $log;
public $insert_id;
private static $instances = [];
//private static $instance;
public $host;
public $user;
public $pass;
public $db;
public function __construct($host = false, $user = false, $pass = false, $db = false) {
$this->host = $host;

View File

@@ -89,6 +89,7 @@ table = $('#datatable').DataTable({
});
if (initc === 2) {
$('.buttons-excel').closest('div').append('<div ><button id="month-complete" class="btn btn-danger margina">Monats Abschluss</button></div>');
$('.buttons-excel').closest('div').append('<div ><button id="open-workdays" class="btn btn-info margina">Offene Arbeitstage</button></div>');
$('.buttons-excel').closest('div').append('<div ><button id="bmd-export" class="btn btn-info margina">BMD Import</button></div>');
$('.buttons-excel').closest('div').append('<div ><button id="bmd-export-nlz" class="btn btn-info margina">BMD NLZ Import</button></div>');
}

View File

@@ -673,11 +673,12 @@ thead .fc-day-today .fc-scrollgrid-sync-inner .fc-col-header-cell-cushion {
margin-left: -28px;
margin-top: 4px;
}
.cursor-alias{
.cursor-alias {
cursor: alias;
}
.fa-calendar-alert
{
.fa-calendar-alert {
font-size: 15px;
vertical-align: top;
margin-top: -1px;
@@ -688,57 +689,65 @@ thead .fc-day-today .fc-scrollgrid-sync-inner .fc-col-header-cell-cushion {
color: #ff0000;
opacity: 0.7;
}
.fa-calendar-alert:before {
color: #fff;
}
.fa-calendar-alert-search
{
.fa-calendar-alert-search {
font-size: 22px;
}
.logo-top-search
{
.logo-top-search {
height: 20px;
vertical-align: top;
cursor: pointer;
}
.btn-light-search
{
.btn-light-search {
border-color: #e5e5e5;
padding: 3px 6px;
}
.top-search-filter
{
filter:grayscale(100%);
.top-search-filter {
filter: grayscale(100%);
}
.fc-event-time {
z-index: 100;
}
.note-editor .dropdown-item {
padding: .15rem 1.2rem;
}
.note-editable p {
line-height: 1.2;
margin-bottom: 0;
}
.tooltip-description p {
margin-bottom: 0;
line-height: 1.2;
}
.note-dropdown-menu {
max-height: 250px !important;
overflow-y: auto !important;
z-index: 99999 !important;
}
.tox-dialog {
z-index: 1060 !important; /* oder ein höherer Wert, je nach Bedarf */
}
.tox .tox-tbtn--select {
max-width: 95px;
}
.fa-calendar-call
{
.fa-calendar-call {
font-size: 13px;
vertical-align: top;
margin-top: 0px;
@@ -748,12 +757,30 @@ thead .fc-day-today .fc-scrollgrid-sync-inner .fc-col-header-cell-cushion {
color: #ff0000;
opacity: 1;
}
.fa-calendar-call:before {
content: "\f2a0";
color: #000000;
opacity: 1;
}
.fa-calendar-call-search {
font-size: 21px;
margin-top: 0px;
}
}
.fa-xls-calendar {
font-size: 24px;
padding-top: 2px;
padding-bottom: 2px;
}
.btn-xls-calendar {
padding: 3px 6px;
background-color: #ffffff;
}
.btn-xls-calendar:hover, .btn-xls-calendar:focus, .btn-xls-calendar:active {
background-color: #ffffff !important;
border-color: #ffffff !important;
}

View File

@@ -554,6 +554,18 @@ document.addEventListener('DOMContentLoaded', function () {
$('#delete-event').hide();
$('#update-event').hide();
$('.show-attendee').hide();
$('#recurringCheck').prop('checked', true);
$('#recurringCheck').change();
if (data.data.recurrence) {
$('#rrule-frequency').val(data.data.recurrence.recurrence.rrule_frequency);
$('#rrule-frequency').trigger('change');
$('#rrule-interval').val(data.data.recurrence.recurrence.rrule_interval);
$('#rrule-byweekday').val(data.data.recurrence.recurrence.rrule_byweekday);
$('#rrule-until').val(data.data.recurrence.recurrence.rrule_until);
$('#monthly-type').val(data.data.recurrence.recurrence.monthly_type);
$('#monthly-type').trigger('change');
$('#rrule-bymonthday').val(data.data.recurrence.recurrence.rrule_bymonthday);
}
}
@@ -914,9 +926,11 @@ if (typeof (EventSource) !== 'undefined') {
let rruleflag = false;
let cursorclass = '';
if (event.rrule) {
$('.calendar-check').eq(0).trigger('change');
rrule = event.rrule;
duration = event.duration;
rruleflag = true;
return;
}
if (event.calendar_id in calendarRights) {
if (calendarRights[event.calendar_id] == 'all') {
@@ -1190,7 +1204,15 @@ $(document).ready(function () {
$('#EventModal .is-require').each(function (index, value) {
$(this).removeClass('required');
});
$('#recurring-settings').hide();
$('#weekly-options').hide();
$('#monthly-type').val('BYMONTHDAY');
$('#rrule-frequency').val('');
$('#rrule-count').val('');
$('#rrule-until').val('');
$('#rrule-byweekday').val('');
$('#rrule-bymonthday').val('1');
$('#monthly-options').hide();
// $('.select2-multiple').select2();
$('#calendar-users').prop("disabled", false);
$("#calendar-users option").each(function () {
@@ -1298,6 +1320,27 @@ $(document).ready(function () {
users.push($(this).val());
});
if ($('#recurringCheck').is(':checked') && $('#rrule-frequency').val() !== '') {
var rruleData = {};
rruleData.rrule_frequency = $('#rrule-frequency').val();
if (rruleData.rrule_frequency === 'WEEKLY') {
// Mehrere Wochentage als Array
rruleData['rrule-byweekday'] = $('#rrule-byweekday').val();
} else if (rruleData.rrule_frequency === 'MONTHLY') {
rruleData.monthly_type = $('#monthly-type').val();
if (rruleData.monthly_type === 'BYMONTHDAY') {
rruleData.rrule_bymonthday = $('#rrule-bymonthday').val();
} else if (rruleData.monthly_type === 'BYSETPOS') {
rruleData.rrule_setpos = $('#rrule-setpos').val();
rruleData.rrule_bynweekday = $('#rrule-bynweekday').val();
}
}
// Optionale Felder
rruleData.rrule_count = $('#rrule-count').val();
rruleData.rrule_until = $('#rrule-until').val();
}
$.post(requestInsertUrl, {
start: start,
@@ -1312,6 +1355,7 @@ $(document).ready(function () {
attachments: attachments,
users: users,
privateflag: privateflag,
rruleData: rruleData,
attendees: $('#calendar-attendees').val(),
customer: customer,
customer_info_check: customer_info_check,
@@ -2049,6 +2093,63 @@ $(document).ready(function () {
}
});
$("body").on("click", ".fa-xls-calendar", function (e) {
let checkbox = $(".calendar-check");
let calendarFilter = $('.btn-light-search');
let visibleCalendars = [];
let visibleCalendarTypes = [];
let mycalendar_id = $('#calendar-id').data('calendarid');
visibleCalendars.push(0);
checkbox.each(function () {
if ($(this).prop('checked')) {
visibleCalendars.push($(this).data('calendar_id'));
}
});
calendarFilter.each(function () {
if (!$(this).hasClass('top-search-filter')) {
visibleCalendarTypes.push($(this).data('ctype'));
}
});
$.ajax({
url: requestgenerateEventsxlsx,
method: 'POST',
data: {
visibleCalendars: visibleCalendars,
visibleCalendarTypes: visibleCalendarTypes
},
xhrFields: {
responseType: 'blob' // Erwartet binäre Daten als Blob
},
success: function (data, status, xhr) {
// Header auslesen, um den Dateinamen zu erhalten
var disposition = xhr.getResponseHeader('Content-Disposition');
var filename = "download.xlsx";
if (disposition && disposition.indexOf('attachment') !== -1) {
var filenameRegex = /filename[^;=\n]*=((['"]).*?\2|[^;\n]*)/;
var matches = filenameRegex.exec(disposition);
if (matches != null && matches[1]) {
filename = matches[1].replace(/['"]/g, '');
}
}
// Blob in eine URL umwandeln und Download auslösen
var url = window.URL.createObjectURL(data);
var a = document.createElement('a');
a.href = url;
a.download = filename;
document.body.appendChild(a);
a.click();
a.remove();
window.URL.revokeObjectURL(url);
},
error: function (jqXHR, status, error) {
console.error("Download error:", error);
}
});
});
$("body").on("blur", ".cal-group-name-input", function (e) {
let groupname = $.trim($(this).val());
if (groupname.length > 3) {
@@ -2417,14 +2518,54 @@ $(document).ready(function () {
});
}
// $('.select-2').select2({
// containerCssClass : 'meine-custom-dropdown', // Klasse für das Dropdown-Menü
// containerCss : "wrap",
// selectionCssClass: 'meine-custom-selection'
// });
$(document).on('focusin', function (e) {
if ($(e.target).closest(".tox-tinymce, .tox-tinymce-aux, .moxman-window, .tam-assetmanager-root").length) {
e.stopImmediatePropagation();
}
});
$(document).ready(function () {
// Checkbox toggelt das RRule-Panel
$('#recurringCheck').on('change', function () {
if ($(this).is(':checked')) {
$('#recurring-settings').show();
$('#rrule-until').attr('min', $('#start-date').val());
} else {
$('#recurring-settings').hide();
$('#weekly-options').hide();
$('#monthly-options').hide();
}
});
// Falls eine Frequenz gewählt wird, passende Felder (weekly/monthly) einblenden
$('#rrule-frequency').on('change', function () {
var freq = $(this).val();
// Alles erstmal verstecken
$('#weekly-options').hide();
$('#monthly-options').hide();
if (freq === 'WEEKLY') {
$('#weekly-options').show();
} else if (freq === 'MONTHLY') {
$('#monthly-options').show();
}
});
// Zeige/Verstecke im monatlichen Bereich "Tag des Monats" oder "X. Wochentag"
$('#monthly-type').on('change', function () {
var monthlyType = $(this).val();
if (monthlyType === 'BYMONTHDAY') {
$('#monthly-day-select').show();
$('#monthly-setpos-select').hide();
} else {
$('#monthly-day-select').hide();
$('#monthly-setpos-select').show();
}
});
// Du kannst bei Klick auf "Hinzufügen" oder "Speichern" dann aus den ausgewählten Werten
// dein RRule-String generieren (z.B. mit rrule.js oder manuell).
});
});

View File

@@ -50,9 +50,12 @@ Vue.component('Pop', {
defaultPageSize: 25,
headers: [
{text: 'Name', key: 'name', priority: 10},
{text: 'Netzgebiet', key: 'networkArea', class: 'text-center', filter: 'autocomplete',
filterOptions: window['TT_CONFIG']['NETWORKS'],
priority: 8},
{text: 'Netzgebiet', key: 'networkArea', class: 'text-center',
// TODO: fix autocomplete Filter
// filter: 'autocomplete',
// filterOptions: window['TT_CONFIG']['NETWORKS'],
// priority: 8
},
{text: 'Zutritt', key: 'location', class: 'text-center', priority: 1},
{text: 'Standort', key: 'gps', class: 'text-center', priority: 2},
{text: 'Vlan Public/Nat/ipv6', key: 'vlan', class: 'text-center', priority: 7},

View File

@@ -0,0 +1,36 @@
.campaign-grid {
display: grid;
grid-template-columns: auto 1fr;
gap: 0.75rem;
align-items: center;
}
.campaign-icon,
.campaign-icon > i{
font-size: 24px !important;
padding: 0.25rem;
}
.campaign-icon:hover {
opacity: 0.8;
}
.progress-grid {
display: grid;
grid-template-columns: auto repeat(3, max-content);
gap: 0.4rem;
}
.progress-row > *:nth-child(n+2) {
text-align: right;
font-variant-numeric: tabular-nums;
}
.preorder-campaign-table-actions > a > i {
font-size: 17px !important;
}
.preorder-campaign-table-actions {
display: flex;
justify-content: space-between;
}

View File

@@ -0,0 +1,103 @@
Vue.component('Preordercampaign', {
template: `
<tt-card>
<tt-table :data="window['TT_CONFIG']['CAMPAIGNS']" :config="PreordercampaignTableConfig">
<template v-slot:top-buttons>
<button v-if="window['TT_CONFIG']['IS_ADMIN'] === '1'" type="button" class="btn btn-primary" @click="window.location = window['TT_CONFIG']['ADD_URL']">
<i class="fas fa-plus"></i> Vorbestellung hinzufügen
</button>
<a v-if="window['TT_CONFIG']['SHOW_MISC_BUTTONS'] === '1'" class="btn btn-purple" :href="window['TT_CONFIG']['PREORDER_DISCOUNT_IMPORT_URL']">
<i class="fas fa-tags fa-fw"></i> Gutscheincodes importieren</a>
<a v-if="window['TT_CONFIG']['SHOW_MISC_BUTTONS'] === '1'" class="btn btn-purple ml-1" :href="window['TT_CONFIG']['PREORDER_STATUS_UPDATE_IMPORT_URL']">
<i class="fas fa-retweet fa-fw"></i> Statusupdates importieren</a>
</template>
<template v-slot:status="{ row: campaign }">
<span class="fa-stack" title="Vorbestellkampagne aktiv" v-if="campaign.from <= Date.now()/1000 && campaign.to >= Date.now()/1000">
<i class="fas fa-alarm-clock fa-stack-1x text-success" style="z-index:20"></i>
<i class="fas fa-circle-check" style="color:Tomato;font-size:.7em"></i>
</span>
</template>
<template v-slot:add="{ row: campaign }">
<a class="btn btn-sm btn-outline-primary" :href="window['TT_CONFIG']['ADD_PREORDER_URL'] + '?preordercampaign_id=' + campaign.id">
<i class="fas fa-plus" title="Vorbestellung hinzufügen"></i></a>
</template>
<template v-slot:progress="{ row: campaign }">
<div class="campaign-grid">
<a :href="window['TT_CONFIG']['VIEW_URL'] + '?filter[preordercampaign_id]=' + campaign.id" class="campaign-icon">
<i class="fas fa-list-alt" title="Bestellungen anzeigen"></i></a>
<div class="progress-grid">
<template v-if="campaign.count_total_units.total_unit_count_sd > 0">
<span>EFH:</span><span>{{ campaign.active_preorder_count.sd_count }} /</span>
<span>{{ campaign.count_total_units.total_unit_count_sd }}</span>
<span>({{ ((campaign.active_preorder_count.sd_count / campaign.count_total_units.total_unit_count_sd) * 100 || 0).toFixed(2) }} %)</span>
</template>
<template v-if="campaign.count_total_units.total_unit_count_md > 0">
<span>MPH:</span><span>{{ campaign.active_preorder_count.md_count }} /</span>
<span>{{ campaign.count_total_units.total_unit_count_md }}</span>
<span>({{ ((campaign.active_preorder_count.md_count / campaign.count_total_units.total_unit_count_md) * 100 || 0).toFixed(2) }} %)</span>
</template>
<template v-if="campaign.count_total_units.total_unit_count > 0">
<span>Gesamt:</span><span>{{ campaign.active_preorder_count.total_count }} /</span>
<span>{{ campaign.count_total_units.total_unit_count * 2 }}</span>
<span>({{ ((campaign.active_preorder_count.total_count / (campaign.count_total_units.total_unit_count * 2)) * 100 || 0).toFixed(1) }} %)</span>
</template>
</div>
</div>
</template>
<template v-slot:from="{ row: campaign }">{{ formatDate(campaign.from) }}</template>
<template v-slot:to="{ row: campaign }">{{ formatDate(campaign.to) }}</template>
<template v-slot:rimo_workoders="{ row: campaign }">{{ campaign.rimo_workoders }} / {{ campaign.active_preorder_count.total_count }}</template>
<template v-slot:actions="{ row: campaign }">
<div class="preorder-campaign-table-actions">
<a :href="window['TT_CONFIG']['PREORDER_NOTIFICATION_URL'] + '?filter[preordercampaign_id]=' + campaign.id" title="Email Aussendungen"><i class="far fa-envelope"></i></a>
<template v-if="window['TT_CONFIG']['IS_ADMIN'] === '1'">
<a class="ml-2" :href="window['TT_CONFIG']['EDIT_URL'] + '?id=' + campaign.id"><i class="far fa-edit" title="Bearbeiten"></i></a>
<a class="ml-2" :href="window['TT_CONFIG']['ADMIN_URL'] + '?id=' + campaign.id"><i class="far fa-a" title="Adminfunktionen"></i></a>
</template>
</div>
</template>
</tt-table>
</tt-card>
`,
data() {
return {
window: window,
PreordercampaignTableConfig: {
key: 'PreordercampaignTable',
tableHeader: 'Vorbestellkampagnen',
defaultPageSize: 25,
headers: [
{text: 'Status', key: 'status', class: 'text-center',sortable: false,filter:false, priority: 20},
{text: '', key: 'add', class: 'text-center',filter:false, sortable: false, priority: 18},
{text: 'Netzgebiet', key: 'network_name', priority: 19},
{text: 'Name', key: 'name', priority: 14},
{text: 'Fortschritt', key: 'progress', priority: 17,filter:false,sortable: false},
{text: 'Workorders', key: 'rimo_workoders', class: 'text-center', priority: 16,filter:false},
{text: 'Von', key: 'from', class: 'text-center', priority: 13,filter:false,sortable: false},
{text: 'Bis', key: 'to', class: 'text-center', priority: 12,filter:false,sortable: false},
{text: 'Aktionen', key: 'actions', class: 'text-center', sortable: false, priority: 21,filter:false}
],
customRowClass: (row) => {
if (row.from <= Date.now()/1000 && row.to >= Date.now()/1000) {
return 'active';
}
}
}
}
},
mounted() {
if (window.TT_CONFIG.IS_ADMIN === "1") {
this.PreordercampaignTableConfig.headers.splice(3, 0, {text: 'Netzbesitzer', key: 'network_owner_name', priority: 4, filter: 'select', filterOptions: window['TT_CONFIG']['NETWORK_OWNER_OPTIONS']});
}
},
methods: {
formatDate: date => window.moment(date * 1000).format('DD.MM.YYYY'),
}
});

View File

@@ -83,7 +83,7 @@
}
.tt-table.table-sm > tbody > tr > td * {
font-size: 13px;
font-size: 14px;
}
.tt-table.table-sm > tbody > tr {

View File

@@ -66,8 +66,9 @@ Vue.component('tt-table-crud', {
<tt-select v-show="crudModalColumnVisibility[column.key]" v-else-if="column.type === 'select'" v-model="crudModalData[column.key]" :label="column.text" :options="column.items" sm row/>
<tt-autocomplete v-show="crudModalColumnVisibility[column.key]" v-else-if="column.type === 'autocomplete'" v-model="crudModalData[column.key]" :label="column.text" :api-url="column.apiUrl" :items="column.items" sm row :return-text="column.returnText" />
<tt-date-picker v-show="crudModalColumnVisibility[column.key]" v-else-if="column.type === 'datepicker'" v-model="crudModalData[column.key]" :label="column.text" sm row :date-range="false" :ref="column.key.toLowerCase() + '-modal-input'"/>
<tt-icon-select v-show="crudModalColumnVisibility[column.key]" v-else-if="column.type === 'icon-select'" v-model="crudModalData[column.key]" :label="column.text" sm row/>
<tt-icon-select v-show="crudModalColumnVisibility[column.key]" v-else-if="column.type === 'icon-select'" v-model="crudModalData[column.key]" :options="column.items" :label="column.text" sm row/>
<tt-checkbox v-show="crudModalColumnVisibility[column.key]" v-else-if="column.type === 'checkbox'" v-model="crudModalData[column.key]" :label="column.text" sm row/>
<tt-positions-manager v-show="crudModalColumnVisibility[column.key]" v-else-if="column.type === 'positions-manager'" v-model="crudModalData[column.key]" :config="column.config" sm row/>
</slot>
<!-- @formatter:on -->
</template>

View File

@@ -825,16 +825,16 @@ Vue.component('tt-table', {
}
})
Vue.config.errorHandler = function (err, vm, info) {
// still log errors to the console
console.error(info, err, vm);
if (typeof vm.config.key === 'string') {
// check if document.querySelector table.tt-table exists aswell if it has atleast 3 <tr> elements
const table = document.querySelector('table.tt-table');
if (!table || !table.querySelectorAll('tr').length > 2) {
// localStorage.removeItem(`tt-table-${vm.config.key}`);
// window.location.reload();
}
}
}
// Vue.config.errorHandler = function (err, vm, info) {
// // still log errors to the console
// console.error(info, err, vm);
//
// if (typeof vm.config.key === 'string') {
// // check if document.querySelector table.tt-table exists aswell if it has atleast 3 <tr> elements
// const table = document.querySelector('table.tt-table');
// if (!table || !table.querySelectorAll('tr').length > 2) {
// // localStorage.removeItem(`tt-table-${vm.config.key}`);
// // window.location.reload();
// }
// }
// }