Merge branch 'master' into fronkdev

This commit is contained in:
Frank Schubert
2024-12-22 14:31:17 +01:00
29 changed files with 1265 additions and 295 deletions

View File

@@ -102,18 +102,20 @@ endforeach;
<div id="calendar-side-div" class="col-12 col-lg-2">
<div class="calendar-side-borders-main">
<h3 class="no-movable" data-id="<?= $Calendar[0]->id ?>" id="calendar-id">Kalender
<h3 class="no-movable" data-calendarid="<?= $Calendar[0]->go_calendar_id ?>" data-id="<?= $Calendar[0]->id ?>" id="calendar-id">Kalender
<div class="add-cal-group-div"><i title="Kalendergruppe hinzufügen"
class="fa-duotone fa-solid fa-rectangle-history-circle-plus"></i>
</div>
</h3>
<?php
if ($Calendar[0]->groups) : ?>
if ($Calendar[0]->groups) :
$Rights = $rights;
?>
<div class="add-cal-group-div-sub">
<?php
$groups = json_decode($Calendar[0]->groups, true);
foreach ($groups as $group) :
?>
<div class="calendar-side-borders-sub mb-2" data-origin="<?= $group['origin'] ?>">
@@ -131,7 +133,10 @@ endforeach;
</h5>
<div class="calendar-side-borders-sub-inner <?= ($group['origin']) ? 'data-origin-' . $group['origin'] : '' ?>"
style="<?= ($group['show']) ? '' : 'display:none;' ?>">
<?php foreach ($group['calendars'] as $calendar) : ?>
<?php foreach ($group['calendars'] as $calendar) :
unset($Rights[$calendar['calendar_id']]);
?>
<div class="form-check text-left ml-2"
data-origin="<?= $calendar['origin'] ?>">
<input data-calendar_id="<?= $calendar['calendar_id'] ?>"
@@ -154,10 +159,52 @@ endforeach;
<?= ($CalendarAll[$calendar['calendar_id']]->user->name) ?: $specialCalendars[$calendar['calendar_id']] ?>
</label>
</div>
<?php endforeach; ?>
<?php endforeach;
// foreach ($Rights as $key => $calendar):
?>
<!---->
<!-- <div class="form-check text-left ml-2"-->
<!-- data-origin="-->
<?php //= $calendar['origin']
?><!--">-->
<!-- <input data-calendar_id="-->
<?php //= $calendar['calendar_id']
?><!--"-->
<!-- class="form-check-input calendar-check"-->
<!-- name="form-check-input" type="checkbox"-->
<!-- --><?php //= ($calendar['checked']) ? 'checked="checked"' : ''
?>
<!-- value="2">-->
<!-- <input-->
<!-- data-calendar_id="-->
<?php //= $calendar['calendar_id']
?><!--"-->
<!-- type="color"-->
<!-- class="form-control-color color-input"-->
<!-- value="-->
<?php //= $Calendar_colors[$key]['bgcolor']
?><!--"-->
<!-- title="Hintergrundfarbe">-->
<!-- <input data-calendar_id="-->
<?php //= $calendar['calendar_id']
?><!--"-->
<!-- type="color"-->
<!-- class="form-control-color color-text-input"-->
<!-- value="-->
<?php //= ($Calendar_colors[$key]['txtcolor']) ? $Calendar_colors[$key]['txtcolor'] : '#000000'
?><!--"-->
<!-- title="Textfarbe">-->
<!-- <label class="calendar-side-label" style="margin-top:2px;">-->
<!-- --><?php //= ($CalendarAll[$key]->user->name) ?: $specialCalendars[$calendar['calendar_id']]
?>
<!-- </label>-->
<!-- </div>-->
<!-- --><?php //endforeach;
?>
</div>
</div>
<?php endforeach;
// var_dump($Rights);
?>
</div>
<?php else : ?>
@@ -248,24 +295,26 @@ endforeach;
</div>
</h5>
<div class="calendar-side-borders-sub-inner data-origin-2">
<!-- <div style="display:none">-->
<!-- <div class="form-check text-left ml-2" data-origin="2">-->
<!-- <input data-calendar_id="999" class="form-check-input calendar-check"-->
<!-- name="form-check-input"-->
<!-- type="checkbox" value="">-->
<!-- <input data-calendar_id="999" type="color"-->
<!-- class="form-control-color color-input"-->
<!-- value="--><?php //= ($Calendar_colors[999]['bgcolor']) ?: $specialCalendarColors[999] ?><!--"-->
<!-- title="Hintergrundfarbe">-->
<!-- <input data-calendar_id="999" type="color"-->
<!-- class="form-control-color color-text-input"-->
<!-- value="--><?php //= ($Calendar_colors[999]['txtcolor']) ? $Calendar_colors[999]['txtcolor'] : '#ffffff' ?><!--"-->
<!-- title="Textfarbe">-->
<!-- <label class="calendar-side-label" for="" style="margin-top:2px;">-->
<!-- Abwesenheiten-->
<!-- </label>-->
<!-- </div>-->
<!-- </div>-->
<!-- <div style="display:none">-->
<!-- <div class="form-check text-left ml-2" data-origin="2">-->
<!-- <input data-calendar_id="999" class="form-check-input calendar-check"-->
<!-- name="form-check-input"-->
<!-- type="checkbox" value="">-->
<!-- <input data-calendar_id="999" type="color"-->
<!-- class="form-control-color color-input"-->
<!-- value="-->
<?php //= ($Calendar_colors[999]['bgcolor']) ?: $specialCalendarColors[999] ?><!--"-->
<!-- title="Hintergrundfarbe">-->
<!-- <input data-calendar_id="999" type="color"-->
<!-- class="form-control-color color-text-input"-->
<!-- value="-->
<?php //= ($Calendar_colors[999]['txtcolor']) ? $Calendar_colors[999]['txtcolor'] : '#ffffff' ?><!--"-->
<!-- title="Textfarbe">-->
<!-- <label class="calendar-side-label" for="" style="margin-top:2px;">-->
<!-- Abwesenheiten-->
<!-- </label>-->
<!-- </div>-->
<!-- </div>-->
<div class="form-check text-left ml-2" data-origin="2">
<input data-calendar_id="998" class="form-check-input calendar-check"
name="form-check-input"
@@ -339,17 +388,18 @@ endforeach;
<input type="text" class="form-control is-require eventmodal-input" id="name">
</div>
</div>
<div class="col-1 text-right">
<label for="location" class="col-form-label fw-medium ">Typ</label>
</div>
<div class="col-3">
<div class="mb-2">
<div class="input-group mb-2">
<span title="Erinnerung" class="input-group-text spanwidht">Typ</span>
<select class="form-control form-select" aria-label="Default select" id="type">
<option value="1">Termin</option>
<option value="2">IBN</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="row justify-content-center">
@@ -361,11 +411,10 @@ endforeach;
<input type="text" class="form-control eventmodal-input" id="location">
</div>
</div>
<div class="col-1 text-right">
<label for="location" class="col-form-label fw-medium ">Anzeige</label>
</div>
<div class="col-3">
<div class="mb-2">
<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>
@@ -373,7 +422,6 @@ endforeach;
</select>
</div>
</div>
</div>
<div class="row justify-content-center">
@@ -439,7 +487,6 @@ endforeach;
</select>
</div>
</div>
</div>
<div class="row justify-content-center mt-2">
@@ -541,7 +588,9 @@ endforeach;
</div>
<div class="d-inline-block" id="customer-info-check-info"></div>
</div>
<select id="customer" class="jumpevent"></select>
<div id="relContainer2" style="position:relative">
<select id="customer" class="jumpevent"></select>
</div>
</div>
</div>
</div>

View File

@@ -146,13 +146,20 @@
</select>
</div>
<div class="col-sm-12 col-md-2">
<label class="form-label" for="partner_id">Partner</label>
<select name="filter[partner_id]" id="filter_partner_id" class="form-control">
<option value="">Alle</option>
<?php foreach($partners as $partner): ?>
<option value="<?=$partner['partner_id']?>" <?=(isset($filter) && array_key_exists("partner_id", $filter) && $filter["partner_id"] == $partner['partner_id']) ? "selected='selected'" : ""?>><?=$partner['name']?></option>
<?php endforeach; ?>
<select name="filter[partner_id][]" id="filter_partner_id" class="form-control" multiple="multiple">
<?php
// if partner_id is string unset it from filter
if(isset($filter) && array_key_exists("partner_id", $filter) && is_string($filter["partner_id"])) {
unset($filter["partner_id"]);
}
?>
<?php foreach($partners as $partner): ?>
<option value="<?=$partner['partner_id']?>" <?=(isset($filter) && array_key_exists("partner_id", $filter) && in_array($partner['partner_id'], $filter["partner_id"])) ? "selected='selected'" : ""?>><?=$partner['name']?></option>
<?php endforeach; ?>
</select>
</div>
@@ -165,6 +172,17 @@
</select>
</div>
<div class="col-sm-12 col-md-2">
<label class="form-label" for="connection_type">Anschlusstyp</label>
<select name="filter[connection_type][]" id="connection_type_id" class="form-control" multiple="multiple">
<?php
$connection_types = ["single-dwelling","multi-dwelling","apartment-building","apartment","business"];
foreach($connection_types as $connection_type): ?>
<option value="<?=$connection_type?>" <?=(isset($filter) && array_key_exists("connection_type", $filter) && in_array($connection_type, $filter["connection_type"])) ? "selected='selected'" : ""?>><?=__($connection_type, "preorder")?></option>
<?php endforeach; ?>
</select>
</div>
<div class="col-sm-12 col-md-2">
<label class="form-label" for="filter_type">Bestelltyp</label>
<select name="filter[type][]" id="filter_type" class="form-control" multiple="multiple">
@@ -174,13 +192,14 @@
</select>
</div>
<div class="col-sm-12 col-md-2">
<label class="form-label" for="filter_addon_services">Zusatzdienste</label>
<select name="filter[addon_services]" id="filter_addon_services" class="form-control">
<option></option>
<option value="1" <?=(isset($filter['addon_services']) && $filter['addon_services'] == 1) ? "selected='selected'" : ""?>>Nur mit Zusatzdiensten</option>
</select>
</div>
<!-- INFO THIS FILTER IS CURRENTLY HIDDEN -->
<!-- <div class="col-sm-12 col-md-2">-->
<!-- <label class="form-label" for="filter_addon_services">Zusatzdienste</label>-->
<!-- <select name="filter[addon_services]" id="filter_addon_services" class="form-control">-->
<!-- <option></option>-->
<!-- <option value="1" --><?php //=(isset($filter['addon_services']) && $filter['addon_services'] == 1) ? "selected='selected'" : ""?><!-- >Nur mit Zusatzdiensten</option>-->
<!-- </select>-->
<!-- </div>-->
</div>
<div class="row mt-2">
<div class="col-sm-12 col-md-1">
@@ -193,6 +212,11 @@
<input type="text" class="form-control" name="filter[oaid]" id="filter_oaid" value="<?=$filter['oaid'] ?? ""?>" />
</div>
<div class="col-sm-12 col-md-2">
<label class="form-label" for="filter_workorder_name">Rimo Workorder Name</label>
<input type="text" class="form-control" name="filter[rimo_workorder_name]" id="filter_workorder_name" value="<?=$filter['workorder_name'] ?? ""?>" />
</div>
<div class="col-sm-12 col-md-2">
<label class="form-label" for="filter_address">Anschlussadresse</label>
<input type="text" class="form-control" name="filter[address]" id="filter_address" value="<?=$filter['address'] ?? ""?>" />
@@ -220,8 +244,17 @@
</select>
</div>
<div class="col-sm-12 col-md-2">
<label class="form-label" for="filter_connection_count">Anzahl Anschlüsse</label>
<div class="col-sm-12 col-md-2">
<label class="form-label" for="rimo_home_extref">Wohneinheit Rimo-Extref</label>
<select name="filter[rimo_home_extref]" id="filter_rimo_home_extref" class="form-control">
<option value=""></option>
<option value="0" <?=(isset($filter) && array_key_exists("rimo_home_extref", $filter) && strlen($filter['rimo_home_extref']) && intval($filter['rimo_home_extref']) === 0) ? "selected='selected'" : ""?>>Fehlt</option>
<option value="1" <?=(isset($filter) && array_key_exists("rimo_home_extref", $filter) && intval($filter['rimo_home_extref']) === 1) ? "selected='selected'" : ""?>>Vorhanden</option>
</select>
</div>
<div class="col-sm-12 col-md-1">
<label class="form-label" for="filter_connection_count">Anz. Anschlüsse</label>
<select name="filter[connection_count]" id="filter_connection_count" class="form-control">
<option value=""></option>
<option value="1" <?=(isset($filter) && array_key_exists("connection_count", $filter) && intval($filter['connection_count']) === 1) ? "selected='selected'" : ""?>>1</option>
@@ -229,6 +262,15 @@
</select>
</div>
<div class="col-sm-12 col-md-1">
<label class="form-label" for="filter_borderpoint">Übergabepunkt</label>
<select name="filter[borderpoint]" id="filter_borderpoint" class="form-control">
<option value="all" <?=(isset($filter) && array_key_exists("borderpoint", $filter) && $filter["borderpoint"] == "all") ? "selected='selected'" : ""?>>Alle</option>
<option value="with" <?=(isset($filter) && array_key_exists("borderpoint", $filter) && $filter["borderpoint"] == "with") ? "selected='selected'" : ""?>>Mit</option>
<option value="without" <?=(isset($filter) && array_key_exists("borderpoint", $filter) && $filter["borderpoint"] == "without") ? "selected='selected'" : ""?>>Ohne</option>
</select>
</div>
<div class="col-sm-12 col-md-1">
<label class="form-label" for="filter_rimo_workorder">Rimo Workorder</label>
<select name="filter[rimo_workorder]" id="filter_rimo_workorder" class="form-control">
@@ -251,11 +293,6 @@
</select>
</div>
<div class="col-sm-12 col-md-2">
<label class="form-label" for="filter_workorder_name">Rimo Workorder Name</label>
<input type="text" class="form-control" name="filter[rimo_workorder_name]" id="filter_workorder_name" value="<?=$filter['workorder_name'] ?? ""?>" />
</div>
<div class="col-sm-12 col-md-2">
<label class="form-label" for="filter_rimo_workorder_team_id">Rimo Workorder Assigned Team</label>
<select name="filter[rimo_workorder_team_id]" id="filter_rimo_workorder_team_id" class="form-control">
@@ -266,15 +303,6 @@
</select>
</div>
<div class="col-sm-12 col-md-2">
<label class="form-label" for="rimo_home_extref">Wohneinheit Rimo-Extref</label>
<select name="filter[rimo_home_extref]" id="filter_rimo_home_extref" class="form-control">
<option value=""></option>
<option value="0" <?=(isset($filter) && array_key_exists("rimo_home_extref", $filter) && strlen($filter['rimo_home_extref']) && intval($filter['rimo_home_extref']) === 0) ? "selected='selected'" : ""?>>Fehlt</option>
<option value="1" <?=(isset($filter) && array_key_exists("rimo_home_extref", $filter) && intval($filter['rimo_home_extref']) === 1) ? "selected='selected'" : ""?>>Vorhanden</option>
</select>
</div>
</div>
<div class="row mt-2">
@@ -442,6 +470,8 @@
$(document).ready(function() {
$("#filter_type").select2({closeOnSelect: false});
$("#filter_status").select2({closeOnSelect: false});
$("#filter_partner_id").select2({closeOnSelect: false});
$("#connection_type_id").select2({closeOnSelect: false});
var attributes = ["bep_specified", "inhouse_cabling_supplied"];

View File

@@ -98,10 +98,10 @@ TODO: enable option for showing prices
<table style="border-collapse: collapse; width: 100%;" id="invoiceTable">
<tr style="font-weight: bold; border-bottom: 1px solid black;" class="uneven">
<th style="text-align: center">Position</th>
<th style="text-align: right;padding-right: 4pt">Menge</th>
<th style="text-align: left;padding-right: 4pt">Einheit</th>
<th style="text-align: center">Artikel</th>
<th style="text-align: center;padding-right: 6pt">Position</th>
<th style="text-align: right;padding-right: 6pt">Menge</th>
<th style="text-align: left;padding-right: 6pt">Einheit</th>
<th style="text-align: left">Artikel</th>
<?php if($showPrices): ?>
<th style="text-align: right;padding-right: 4pt">Preis</th>
<?php endif; ?>
@@ -112,7 +112,7 @@ TODO: enable option for showing prices
<td style="text-align: center;"><?= $i + 1 ?></td>
<td style="text-align: right;padding-right: 8pt"><?=$p["amount"]?> </td>
<td style="text-align: left;padding-right: 8pt"><?=$p["articleUnit"]?> </td>
<td style="text-align: center;"><b><?=$p["articleTitle"]?></b></td>
<td style="text-align: left;"><b><?=$p["articleTitle"]?></b></td>
<?php if($showPrices): ?>
<td style="text-align: right;padding-right: 8pt"><?=number_format(
$p["price"] * $p["amount"], 2, ",", ".")?> €</td>
@@ -123,7 +123,7 @@ TODO: enable option for showing prices
<td></td>
<td></td>
<td style="text-align: center;"><?= $p["articleDescription"] ?></td>
<td style="text-align: left;"><?= $p["articleDescription"] ?></td>
<?php if($showPrices): ?>
<td></td>
<?php endif; ?>

View File

@@ -79,7 +79,15 @@ class AddressController extends mfBaseController {
if(array_key_exists("kunde", $filter) && $filter["kunde"]) {
$kunde = $this->db()->escape($filter['kunde']);
$new_filter['add-where'] .= " AND (company like '%$kunde%' OR firstname like '%$kunde%' OR lastname like '%$kunde%' OR concat(firstname, ' ', lastname) like '%$kunde%' OR concat(lastname, ' ', firstname) like '%$kunde%')";
// if kunde contains ß or ss we want to search both cases but not with % because it can lead to wrong results
if (strpos($kunde, "ß") !== false || strpos($kunde, "ss") !== false) {
$kundeWithSS = str_replace("ß", "ss", $kunde);
$kundeWithoutSS = str_replace("ss", "ß", $kunde);
$new_filter['add-where'] .= " AND (company like '%$kundeWithoutSS%' OR firstname like '%$kundeWithoutSS%' OR lastname like '%$kundeWithoutSS%' OR concat(firstname, ' ', lastname) like '%$kundeWithoutSS%' OR concat(lastname, ' ', firstname) like '%$kundeWithoutSS%')";
$new_filter['add-where'] .= " OR (company like '%$kundeWithSS%' OR firstname like '%$kundeWithSS%' OR lastname like '%$kundeWithSS%' OR concat(firstname, ' ', lastname) like '%$kundeWithSS%' OR concat(lastname, ' ', firstname) like '%$kundeWithSS%')";
} else {
$new_filter['add-where'] .= " AND (company like '%$kunde%' OR firstname like '%$kunde%' OR lastname like '%$kunde%' OR concat(firstname, ' ', lastname) like '%$kunde%' OR concat(lastname, ' ', firstname) like '%$kunde%')";
}
}
if(!array_key_exists("parents_only", $filter)) {

View File

@@ -152,6 +152,7 @@ class CalendarApicontroller extends mfBaseApicontroller
$now = floor(microtime(true) * 1000);
$timenow = time();
while (true) {
$colors = $redis->get('thetool_calendar_usercolors_' . $decstring);
if ($colors) {
$calendarColors = json_decode($colors, true);
@@ -172,11 +173,15 @@ class CalendarApicontroller extends mfBaseApicontroller
$message[0]['start_time'] = date('Y-m-d H:i', $json['start_time']);
$message[0]['end_time'] = date('Y-m-d H:i', $json['end_time']);
}
$message[0]['name']= $Calendarevent['data'][0]['category']['category'];
$message[0]['description'] = $Calendarevent['data'][0]['description']['description'];
$message[0]['location'] = $Calendarevent['data'][0]['location']['location'];
$message[0]['event_type'] = $Calendarevent['data'][0]['event_type']['event_type'];
$message[0]['attachment'] = $Calendarevent['data'][0]['attachment']['attachment'];
$message[0]['attachments'] = $Calendarevent['data'][0]['attachments']['attachments'];
$message[0]['calendar_name'] = $Calendarevent['data'][0]['calendar_name']['calendar_name'];
$message[0]['isorganizer'] = $Calendarevent['data'][0]['isorganizer']['isorganizer'];
$message[0]['privateflag'] = $Calendarevent['data'][0]['privateflag']['privateflag'];
$message[0]['rrule'] = $Calendarevent['data'][0]['rrule']['rrule'];
$message[0]['duration'] = $Calendarevent['data'][0]['duration']['duration'];
$message[0]['mtime'] = $Calendarevent['data'][0]['mtime']['mtime'];
@@ -189,6 +194,7 @@ class CalendarApicontroller extends mfBaseApicontroller
$message[0]['bgColor'] = $calendarColors[$json['calendar_id']]['bgcolor'];
$message[0]['txtColor'] = $calendarColors[$json['calendar_id']]['txtcolor'];
$message[0]['rights'] = $rights[$json['calendar_id']];
$message[0]['me'] = $decstring;
$result = json_encode($message);
if (in_array($message[0]['calendar_id'], $allowedCalendar)) {

View File

@@ -38,11 +38,30 @@ class CalendarModel
'#c8a7d5', '#aad8d0', '#dcadc3', '#d9e8e5', '#e4d8d5',
'#b4dcc5', '#b9d1e2', '#a3c1c2', '#cdd2b9', '#e3c8ce',
'#aae2c5', '#c7d0cc', '#d0c0da', '#dbdfc8', '#b4e3d7',
'#c3e4e3', '#c5c4e3', '#d0b7e5', '#b6e98c', '#e3d1de'
'#c3e4e3', '#c5c4e3', '#d0b7e5', '#b6e98c', '#e3d1de',
'#dbbeb3', '#e9aebc', '#b5cae7', '#dec2d4', '#aebbd5',
'#e2baaf', '#b6ebd5', '#a9c4bb', '#d7d2c5', '#d7e3d4'
];
public static $specialCalendarColors = array(997 => '#bd0000', 998 => '#8000A3', 999 => '#08769b');
public static function convertToSummertime($timestamp)
{
// Create a DateTime object from the Unix timestamp
$date = new DateTime();
$date->setTimestamp($timestamp);
// Set the timezone to Europe/Berlin
$date->setTimezone(new DateTimeZone('Europe/Berlin'));
if ($date->format('I') == 1) {
$cest = 7200;
} else {
$cest = 3600;
}
$unixtimestamp = $date->getTimestamp() + $cest;
// Format the date and time
return date('Y-m-d H:i:s', $unixtimestamp);
}
public static function replace_unicode_sequences($string)
{
@@ -96,16 +115,18 @@ class CalendarModel
$rows = array();
while ($data = $dbcal->fetch_array($res)) {
if ($data['location']) {
$searchName = $data['name'] . " @" . $data['location'];
} else {
$searchName = $data['name'];
if ($data['private'] != 1 || $data['calendar_id'] == $r->cal_id) {
if ($data['location']) {
$searchName = $data['name'] . " @" . $data['location'];
} else {
$searchName = $data['name'];
}
$id = $data['id'] . ";" . date("Y-m-d", $data['start_time']);
$rows[] = array(
'id' => $id,
'text' => $searchName . " (" . date("Y-m-d", $data['start_time']) . ")"
);
}
$id = $data['id'] . ";" . date("Y-m-d", $data['start_time']);
$rows[] = array(
'id' => $id,
'text' => $searchName . " (" . date("Y-m-d", $data['start_time']) . ")"
);
}
$json['incomplete_results'] = false;
$json['total_count'] = count($rows);
@@ -118,7 +139,7 @@ class CalendarModel
public static function getCalendarEvents($me, $id = 0, $r = 0)
{
$rrulefreq = array('daily' => 'DAILY', 'weekly' => 'WEEKLY', 'relativeMonthly' => 'MONTHLY', 'yearly' => 'YEARLY');
$rrulefreq = array('daily' => 'DAILY', 'weekly' => 'WEEKLY', 'relativeMonthly' => 'MONTHLY', 'yearly' => 'YEARLY', 'absoluteMonthly' => 'absoluteMonthly');
$calendar = self::search(array("user_id" => $me));
$standardCalendarColors = CalendarModel::$standardCalendarColors;
$calendarColors = json_decode($calendar[0]->colors, true);
@@ -195,7 +216,11 @@ class CalendarModel
if ($data['recurrence']) {
$recurrence = json_decode($data['recurrence'], true);
$rrule_events= json_decode($data['rrule_events'], true);
$rrule_events = json_decode($data['rrule_events'], true);
foreach ($rrule_events as $key => $value) {
$rrule_events[$key]['start'] = self::convertToSummertime(strtotime($value['start']));
$rrule_events[$key]['end'] = self::convertToSummertime(strtotime($value['end']));
}
if ($rrulefreq[$recurrence['pattern']['type']]) {
unset ($byweekday);
$freq = $rrulefreq[$recurrence['pattern']['type']];
@@ -237,9 +262,20 @@ class CalendarModel
$txtcolor = "#000";
$colorCounter++;
}
$location = $data['location'];
$eventtype = $data['event_type'];
$description = $data['description'];
if ($calenderRights[$data['calendar_id']]) {
$rights = $calenderRights[$data['calendar_id']];
$CalendarUsers[$data['calendar_id']] = $data['calendar_name'];
if ($data['private'] == 1 && $calendar[0]->go_calendar_id != $data['calendar_id']) {
$name = "Privat";
$attachment = 0;
$attachmentLinks = "";
$location = "";
$eventtype = "1";
$description = "";
}
$rows[] = array(
'id' => array('id' => $data['id']),
'cstart' => array('cstart' => $starttime),
@@ -250,14 +286,15 @@ class CalendarModel
'bgColor' => array('bgColor' => $bgcolor),
'txtColor' => array('txtColor' => $txtcolor),
'rights' => array('rights' => $rights, 'order' => $rights),
'location' => array('location' => $data['location']),
'location' => array('location' => $location),
'busy' => array('busy' => $data['busy']),
'privateflag' => array('privateflag' => $data['private']),
'allDay' => array('allDay' => $data['all_day_event']),
'rrule' => array('rrule' => $rrule),
'rrule_events' => array('rrule_events' => $rrule_events),
'duration' => array('duration' => $duration),
'event_type' => array('event_type' => $data['event_type']),
'description' => array('description' => ($data['description'])),
'event_type' => array('event_type' => $eventtype),
'description' => array('description' => ($description)),
'attachment' => array('attachment' => $attachment),
'attachments' => array('attachments' => $attachmentLinks),
'calendar_name' => array('calendar_name' => $data['calendar_name']),
@@ -466,6 +503,7 @@ WHERE `TimerecordingCategory`.`hourday`!='1' AND `TimerecordingCategory`.`hourda
'customer_info' => array('customer_info' => $data['customer_info']),
'customer_info_send' => array('customer_info_send' => $data['customer_info_send']),
'customer_info_reminder' => array('customer_info_reminder' => $data['customer_info_reminder']),
'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']),
@@ -539,6 +577,7 @@ WHERE `TimerecordingCategory`.`hourday`!='1' AND `TimerecordingCategory`.`hourda
$busy = ($r->busy);
$users = ($r->users);
$attendees = ($r->attendees);
$privateflag = ($r->privateflag);
foreach ($users as $key => $value) {
$user_id = $value;
}
@@ -573,6 +612,9 @@ WHERE `TimerecordingCategory`.`hourday`!='1' AND `TimerecordingCategory`.`hourda
if (isset($allday))
$updateArray['all_day_event'] = $allday;
if (isset($privateflag))
$updateArray['private'] = $privateflag;
if (isset($reminder)) {
if ($reminder == 'NULL')
$updateArray['reminder'] = NULL;
@@ -733,6 +775,7 @@ WHERE `TimerecordingCategory`.`hourday`!='1' AND `TimerecordingCategory`.`hourda
$users = ($r->users);
$customer_info_reminder_check = 0;
$attendees = ($r->attendees);
$privateflag = ($r->privateflag);
date_default_timezone_set('Europe/Berlin');
header('Content-Type: application/json');
foreach ($users as $key => $value) {
@@ -771,7 +814,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' => 0, 'rrule' => '', 'background' => 'EBF1E2', 'files_folder_id' => 0, 'read_only' => 0, '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' => 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, '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'];

View File

@@ -231,6 +231,12 @@ class PreorderController extends mfBaseController {
$new_filter['addon_services'] = true;
}
if(array_key_exists("borderpoint", $filter) && $filter['borderpoint'] === 'with') {
$new_filter['add-where'] .= " AND (adb_hausnummer.borderpoint_lat IS NOT NULL AND adb_hausnummer.borderpoint_long IS NOT NULL)";
} elseif(array_key_exists("borderpoint", $filter) && $filter['borderpoint'] === 'without') {
$new_filter['add-where'] .= " AND (adb_hausnummer.borderpoint_lat IS NULL OR adb_hausnummer.borderpoint_long IS NULL)";
}
if(array_key_exists("address_source", $filter)) {
if($filter['address_source'] == "manual") {
$new_filter['address_created'] = true;
@@ -293,10 +299,6 @@ class PreorderController extends mfBaseController {
}
}
/*if(array_key_exists("attributes", $filter) && count($filter['attributes'])) {
}*/
if(is_array($filter) && count($filter)) {
foreach($filter as $name => $value) {
$new_filter[$name] = $value;

View File

@@ -7,6 +7,7 @@ class WarehouseArticleController extends TTCrud {
// @formatter:off
protected array $columns = [
['key' => 'title', 'text' => 'Titel', 'required' => true, 'table' => ['priority' => 9]],
['key' => 'articleNumber', 'text' => 'Nr.', 'required' => true],
['key' => 'description', 'text' => 'Beschreibung', 'required' => true],
['key' => 'category', 'text' => 'Kategorie', 'required' => true],
['key' => 'unit', 'text' => 'Einheit', 'required' => true,'table' => false], // Boolean value
@@ -253,6 +254,48 @@ class WarehouseArticleController extends TTCrud {
}
}
protected function provArticleNumberImportAction() {
// if method is post and file is set read the csv and var dump json and die
// if method is get return basic html with a form to upload a file and a submit button
if ($_SERVER['REQUEST_METHOD'] == 'POST' && isset($_FILES['file'])) {
$file = fopen($_FILES['file']['tmp_name'], 'r');
$data = [];
// parse csv as object with first row as keys as header and use key => value
$firstRow = true;
while (($row = fgetcsv($file)) !== false) {
if ($firstRow) {
$header = $row;
$firstRow = false;
continue;
}
$data[] = array_combine($header, $row);
}
// loop through all the data and if PRODUKT 1.ZEILE is set and articleNumber is not set push the last 4 numbers of "EAN 13 Code" to articleNumber of the found article
foreach ($data as $item) {
if (isset($item['PRODUKT 1.ZEILE'])) {
$articles = WarehouseArticleModel::getAll(['title' => $item['PRODUKT 1.ZEILE']]);
if (!empty($articles)) {
$article = (array) WarehouseArticleModel::get($articles[0]->id);
$article['articleNumber'] = substr($item['EAN 13 Code'], -4);
WarehouseArticleModel::update($article);
}
}
}
fclose($file);
die(json_encode(['success' => true]));
}
$html = '<html><head></head><body><form method="post" enctype="multipart/form-data"><input type="file" name="file"><input type="submit"></form></body></html>';
echo $html;
die();
}
protected function prepareOrderAction() {
// inside post json it will look like
// [

View File

@@ -3,6 +3,7 @@
class WarehouseArticleModel extends TTCrudBaseModel {
public int $id;
public string $title;
public string $articleNumber;
public string $description;
public string $category;
public ?float $cheapestPurchasePrice;

View File

@@ -28,6 +28,13 @@ class WarehouseEShopController extends TTCrud {
return $this->user->can(["WarehouseEShop"]);
}
protected function prepareCrudConfig() {
if (!$this->user->can('WarehouseAdmin')) {
$this->columns[2]['table'] = false;
}
}
public function getAction() {
$filter = $this->postData['filters'] ?? [];
$order = $this->postData['order'] ?? ['key' => null, 'order' => 'ASC'];

View File

@@ -4,62 +4,70 @@ class WarehouseEShopOrderController extends TTCrud {
protected string $headerTitle = 'Energie Steiermark Bestellungen';
protected bool $createText = false;
protected array $columns = [['key' => 'id', 'text' => 'ID', 'modal' => false],
['key' => 'extRef', 'text' => 'Externe Referenz', 'required' => true],
['key' => 'status',
'text' => 'Status',
'required' => true,
'modal' => ['type' => 'select',
'items' => [['value' => 'new', 'text' => 'Neu'],
['value' => 'accepted', 'text' => 'An Lieferant übergeben'],
['value' => 'acceptedInternally', 'text' => 'Interne verarbeitung'],
['value' => 'sent', 'text' => 'Gesendet'],
['value' => 'done', 'text' => 'Erledigt'],]],
'table' => ['filter' => 'select']],
['key' => 'deliveryMode',
'text' => 'Liefermodus',
'required' => true,
'modal' => ['type' => 'select',
'items' => [['value' => 'singleAddress', 'text' => 'Einzelne Adresse'],
// ['value' => 'multipleAddresses', 'text' => 'Mehrere Adressen'],
]]],
['key' => 'deliveryAddressName', 'text' => 'Name', 'required' => true],
['key' => 'deliveryAddressLine', 'text' => 'Adresse', 'required' => true, 'required_length' => 4],
['key' => 'deliveryAddressPLZ', 'text' => 'PLZ', 'required' => true, 'regex' => '/^\d{4}$/'],
['key' => 'deliveryAddressCity', 'text' => 'Stadt', 'required' => true, 'required_length' => 3],
['key' => 'trackingNumber', 'text' => 'Trackingnummer', 'required' => false, 'modal' => false],
['key' => 'create', 'text' => 'Erstellt', 'required' => true, 'modal' => false, 'filter' => 'datetime'],
['key' => 'createBy',
'text' => 'Erstellt von',
'required' => true,
'table' => ['filter' => 'select'],
'modal' => ['type' => 'select', 'items' => []]],
['key' => 'actions',
'text' => 'Aktionen',
'required' => false,
'modal' => false,
'table' => ['filter' => false, 'sortable' => false, 'class' => 'text-center']],];
//@formatter:off
protected array $columns = [
['key' => 'id', 'text' => 'ID', 'modal' => false],
['key' => 'extRef', 'text' => 'Externe Referenz', 'required' => true],
['key' => 'status', 'text' => 'Status', 'required' => true, 'modal' => ['type' => 'select', 'items' => [['value' => 'new', 'text' => 'Neu'], ['value' => 'accepted', 'text' => 'An Lieferant übergeben'], ['value' => 'acceptedInternally', 'text' => 'Interne verarbeitung'], ['value' => 'sent', 'text' => 'Gesendet'], ['value' => 'done', 'text' => 'Erledigt'],]], 'table' => ['filter' => 'select']],
['key' => 'shippingNoteStatus', 'text' => 'LS-Status', 'required' => false, 'modal' => false, 'table' => ['filter' => false, 'order' => false]],
['key' => 'deliveryMode', 'text' => 'Liefermodus', 'required' => true, 'modal' => ['type' => 'select', 'items' => [['value' => 'singleAddress', 'text' => 'Einzelne Adresse']]]],
['key' => 'deliveryAddressName', 'text' => 'Name', 'required' => true],
['key' => 'deliveryAddressLine', 'text' => 'Adresse', 'required' => true, 'required_length' => 4],
['key' => 'deliveryAddressPLZ', 'text' => 'PLZ', 'required' => true, 'regex' => '/^\d{4}$/'],
['key' => 'deliveryAddressCity', 'text' => 'Stadt', 'required' => true, 'required_length' => 3],
['key' => 'trackingNumber', 'text' => 'Trackingnummer', 'required' => false, 'modal' => false],
['key' => 'positions', 'text' => 'Positionen', 'required' => false, 'table' => false],
['key' => 'create', 'text' => 'Erstellt', 'required' => true, 'modal' => false, 'filter' => 'datetime'],
['key' => 'createBy', 'text' => 'Erstellt von', 'required' => true, 'table' => ['filter' => 'select'], 'modal' => ['type' => 'select', 'items' => []]],
['key' => 'actions', 'text' => 'Aktionen', 'required' => false, 'modal' => false, 'table' => ['filter' => false, 'sortable' => false, 'class' => 'text-center']]
];
protected array $additionalActions = [['key' => 'openHistory', 'title' => 'Historie', 'class' => 'fas fa-history text-primary'],
['key' => 'showTrackingHistory', 'title' => 'Tracking Historie', 'class' => 'fas fa-truck text-primary'],
['key' => 'createShippingNote', 'title' => 'Lieferschein erstellen', 'class' => 'fas fa-file-invoice text-primary'],
['key' => 'openSingleOrderEmail', 'title' => 'Bestellbestätigung', 'class' => 'fas fa-envelope text-primary'],];
protected array $additionalActions = [
['key' => 'openHistory', 'title' => 'Historie', 'class' => 'fas fa-history text-primary'],
['key' => 'showTrackingHistory', 'title' => 'Tracking Historie', 'class' => 'fas fa-truck text-primary'],
['key' => 'createShippingNote', 'title' => 'Lieferschein erstellen', 'class' => 'fas fa-file-invoice text-primary'],
['key' => 'openSingleOrderEmail', 'title' => 'Bestellbestätigung', 'class' => 'fas fa-envelope text-primary']#
];
protected array $infoMessages = ['create' => 'Bestellung wurde erfolgreich erstellt, sie erhalten in Kürze eine Bestätigungsmail',
'update' => 'Bestellung wurde aktualisiert',
'delete' => 'Bestellung wurde gelöscht',
'noChanges' => 'Keine Änderungen',];
protected array $infoMessages = [
'create' => 'Bestellung wurde erfolgreich erstellt, sie erhalten in Kürze eine Bestätigungsmail',
'update' => 'Bestellung wurde aktualisiert',
'delete' => 'Bestellung wurde gelöscht',
'noChanges' => 'Keine Änderungen'
];
//@formatter:on
public function permissionCheck(): bool {
return $this->user->can(["WarehouseEShop"]);
}
protected function customRowsHandler($rows): array {
$statusToText = [
'new' => 'Neu',
'accepted' => 'Akzeptiert',
'invoiced' => 'Verrechnet',
'in_progress' => 'In Bearbeitung',
];
foreach ($rows as $row) {
$shippingNote = WarehouseShippingNoteModel::getAll(['eShopOrderId' => $row->id]);
if (empty($shippingNote)) {
$row->shippingNoteStatus = 'Kein LS';
} else {
$row->shippingNoteStatus = $statusToText[$shippingNote[0]->status];
}
}
return $rows;
}
protected function prepareCrudConfig() {
$users = array_map(function ($user) {
return ['value' => intval($user->id), 'text' => $user->name];
}, UserModel::search());
$this->columns[10]['modal']['items'] = $users;
$createByIndex = array_search('createBy', array_column($this->columns, 'key'));
$this->columns[$createByIndex]['modal']['items'] = $users;
}
protected function createShippingNote() {
@@ -71,7 +79,9 @@ class WarehouseEShopOrderController extends TTCrud {
$existingShippingNote = WarehouseShippingNoteModel::getAll(['eShopOrderId' => $id]);
if (!empty($existingShippingNote)) {
self::returnJson(['success' => false, 'message' => 'Für diese Bestellung existiert bereits ein Lieferschein', 'shippingNoteId' => $existingShippingNote[0]->id]);
self::returnJson(['success' => false,
'message' => 'Für diese Bestellung existiert bereits ein Lieferschein',
'shippingNoteId' => $existingShippingNote[0]->id]);
die();
}
@@ -120,9 +130,12 @@ class WarehouseEShopOrderController extends TTCrud {
'deliveryAddressLine' => $order->deliveryAddressLine,
'deliveryAddressPLZ' => $order->deliveryAddressPLZ,
'deliveryAddressCity' => $order->deliveryAddressCity,
'deliveryAddressEMail' => '',
'note' => 'Erstellung aus Energie Steiermark Shop Bestellung #' . $id,
'status' => 'new',
'positions' => $positions,
'textElements' => '[]',
'hoursEntries' => '[]',
'eShopOrderId' => $id,
'create' => time(),
'createBy' => $this->user->id]);
@@ -277,7 +290,8 @@ class WarehouseEShopOrderController extends TTCrud {
}
protected function getAllOrderItemsPerOrder(): array {
$items = WarehouseEShopOrderItemModel::getAll();
if (isset($_GET['orderId'])) return WarehouseEShopOrderItemModel::getAll(['orderId' => $_GET['orderId']]);
else $items = WarehouseEShopOrderItemModel::getAll();
$articles = WarehouseArticleModel::getAll();
$articlePackets = WarehouseArticlePacketModel::getAll();
@@ -384,7 +398,13 @@ class WarehouseEShopOrderController extends TTCrud {
die(json_encode($json));
}
protected function beforeCreate(): bool {
unset($this->postData['shippingNoteStatus']);
return true;
}
protected function beforeUpdate($postData): bool {
unset($this->postData['shippingNoteStatus']);
(new WarehouseHistoryController)->create($postData, $this->mod);
return true;
}
@@ -430,17 +450,17 @@ class WarehouseEShopOrderController extends TTCrud {
}
protected function readGLSEmailAction() {
function decode_utf8($str){
function decode_utf8($str) {
# paterns
$err="(=\?.{10,13}q\?_?|\?\=)";
$err = "(=\?.{10,13}q\?_?|\?\=)";
$pat = "/=([0-9A-F]{2})/";
$cha="'.chr(hexdec(";
$cha = "'.chr(hexdec(";
# erase null signs in string
$str = str_replace("\x00", "", $str);
# to decode with eval and replace
eval("\$str='".
preg_replace($pat,$cha."'$1')).'",$str)
."';");
eval("\$str='" .
preg_replace($pat, $cha . "'$1')).'", $str)
. "';");
# return
return $str;
}
@@ -571,4 +591,71 @@ class WarehouseEShopOrderController extends TTCrud {
$result = [];
}
protected function updatePositionAction() {
$json = json_decode(file_get_contents('php://input'), true);
$id = intval($json['id']);
$articleId = $json['articleId'] ? intval($json['articleId']) : null;
$articlePacketId = $json['articlePacketId'] ? intval($json['articlePacketId']) : null;
$quantity = intval($json['quantity']);
if (!$id || !$quantity) {
self::returnJson(['success' => false, 'message' => 'Ungültige Daten']);
die();
}
$position = (array) WarehouseEShopOrderItemModel::get($id);
if (!$position) {
self::returnJson(['success' => false, 'message' => 'Position nicht gefunden']);
die();
}
$position['articleId'] = $articleId;
$position['articlePacketId'] = $articlePacketId;
$position['quantity'] = $quantity;
WarehouseEShopOrderItemModel::update($position);
self::returnJson(['success' => true, 'position' => $position]);
}
protected function deletePositionAction() {
$json = json_decode(file_get_contents('php://input'), true);
$id = intval($json['id']);
if (!$id) {
self::returnJson(['success' => false, 'message' => 'Keine ID angegeben']);
die();
}
$position = WarehouseEShopOrderItemModel::get($id);
if (!$position) {
self::returnJson(['success' => false, 'message' => 'Position nicht gefunden']);
die();
}
WarehouseEShopOrderItemModel::delete($id);
self::returnJson(['success' => true, 'message' => 'Position gelöscht']);
}
protected function addPositionAction() {
$json = json_decode(file_get_contents('php://input'), true);
$orderId = intval($json['orderId']);
$articleId = $json['articleId'] ? intval($json['articleId']) : null;
$articlePacketId = $json['articlePacketId'] ? intval($json['articlePacketId']) : null;
$quantity = intval($json['quantity']);
if (!$orderId || !$quantity) {
self::returnJson(['success' => false, 'message' => 'Ungültige Daten']);
die();
}
$position = WarehouseEShopOrderItemModel::create(['orderId' => $orderId,
'articleId' => $articleId,
'articlePacketId' => $articlePacketId,
'quantity' => $quantity]);
$position = (array) WarehouseEShopOrderItemModel::get($position);
self::returnJson(['success' => true, 'position' => $position]);
}
}

View File

@@ -13,10 +13,10 @@ class WarehouseShippingNoteController extends TTCrud {
['key' => 'deliveryAddressPLZ', 'text' => 'L.-Adr. PLZ', 'required' => true],
['key' => 'deliveryAddressEMail', 'text' => 'L.-Adr. EMail', 'required' => false, 'table' => false],
['key' => 'note', 'text' => 'Art der Arbeit', 'required' => true, 'table' => false],
['key' => 'status', 'text' => 'Status', 'required' => true, 'table' => ['filter' => 'select'], 'modal' => ['type' => 'select', 'items' => [['value' => 'new', 'text' => 'Neu'], ['value' => 'inProgress', 'text' => 'In Bearbeitung'], ['value' => 'accepted', 'text' => 'Akzeptiert'], ['value' => 'invoiced', 'text' => 'In Rechnung gestellt'],]]],
['key' => 'status', 'text' => 'Status', 'required' => true, 'table' => ['filter' => 'select'], 'modal' => ['type' => 'select', 'items' => [['value' => 'new', 'text' => 'Neu'], ['value' => 'in_progress', 'text' => 'In Bearbeitung'], ['value' => 'accepted', 'text' => 'Akzeptiert'], ['value' => 'invoiced', 'text' => 'In Rechnung gestellt'],]]],
['key' => 'positions', 'text' => 'Positionen', 'required' => true, 'table' => false, 'modal' => false],
['key' => 'create', 'text' => 'Erstellt', 'required' => false, 'modal' => false, 'table' => ['filter' => 'date']],
['key' => 'createBy', 'text' => 'Erstellt von', 'required' => true, 'type' => 'autocomplete', 'table' => ['class' => 'text-nowrap', 'filter' => 'select'], 'modal' => ['items' => [], 'type' => 'select',]],
['key' => 'create', 'text' => 'Erstellt', 'required' => false, 'modal' => ['visible' => false], 'table' => ['filter' => 'date']],
['key' => 'createBy', 'text' => 'Erstellt von', 'required' => false, 'type' => 'autocomplete', 'table' => ['class' => 'text-nowrap', 'filter' => 'select'], 'modal' => ['items' => [], 'type' => 'select',]],
['key' => 'actions', 'text' => 'Aktionen', 'required' => false, 'modal' => false, 'table' => ['filter' => false, 'sortable' => false, 'class' => 'text-center']],];
protected array $defaultOrder = ['key' => 'create', 'order' => 'DESC'];
@@ -54,6 +54,14 @@ class WarehouseShippingNoteController extends TTCrud {
die();
}
foreach ($postData['hoursEntries'] as $hoursEntry) {
if (!preg_match('/^[0-9,.]*$/', $hoursEntry['hourCount'])) {
http_response_code(500);
self::returnJson(['success' => false, 'message' => 'Stundenanzahl darf nur Zahlen, Komma oder Punkt enthalten']);
die();
}
}
$postData['positions'] = json_encode($postData['positions']);
return true;
}
@@ -77,6 +85,13 @@ class WarehouseShippingNoteController extends TTCrud {
die();
}
foreach ($postData['hoursEntries'] as $hoursEntry) {
if (!preg_match('/^[0-9,.]*$/', $hoursEntry['hourCount'])) {
http_response_code(500);
self::returnJson(['success' => false, 'message' => 'Stundenanzahl darf nur Zahlen, Komma oder Punkt enthalten']);
die();
}
}
$postData['positions'] = json_encode($postData['positions']);
(new WarehouseHistoryController)->create($postData, $this->mod);
@@ -218,7 +233,16 @@ class WarehouseShippingNoteController extends TTCrud {
foreach (json_decode($shippingNote->positions, true) as $position) {
if (isset($position['article'])) {
$article = WarehouseArticleModel::get($position['article']);
$position['articleTitle'] = $article->title;
if (isset($article->articleNumber)) {
$position['articleTitle'] = $article->articleNumber . " | " . $article->title;
} else {
$position['articleTitle'] = $article->title;
}
if (isset($position['isEnergieMaterial']) && $position['isEnergieMaterial'] == 1) {
$position['articleTitle'] .= " (ESTMK)";
}
$position['articleDescription'] = $article->description === $article->title ? "" : $article->description;
$position['articleUnit'] = $article->unit;
$positions[] = $position;
@@ -240,18 +264,27 @@ class WarehouseShippingNoteController extends TTCrud {
$hoursEntries = json_decode($shippingNote->hoursEntries, true);
foreach ($hoursEntries as $hoursEntry) {
// die(json_encode($hoursEntry));
$positions[] = [
'articleTitle' => "Arbeitsstunden",
'articleDescription' => "Datum: ". date("d.m.Y", strtotime($hoursEntry['date'])) . " | Mitarbeiter: " . UserModel::getOne($hoursEntry['userId'])->name,
'articleUnit' => 'Std.',
'amount' => $hoursEntry['hourCount'],
'price' => $hoursEntry['hourlyPrice'] * $hoursEntry['hourCount'] ?? 0,
];
$articleTitle = "Arbeitsstunden";
if (isset($hoursEntry['priceType']) && $hoursEntry['priceType'] == 50) {
$articleTitle = "Arbeitsstunden (50% Zuschlag)";
} elseif (isset($hoursEntry['priceType']) && $hoursEntry['priceType'] == 100) {
$articleTitle = "Arbeitsstunden (100% Zuschlag)";
}
if (floatval(str_replace(",", ".", $hoursEntry['hourCount'])) > 0) {
$positions[] = [
'articleTitle' => $articleTitle,
'articleDescription' => "Datum: ". date("d.m.Y", strtotime($hoursEntry['date'])) . " | Mitarbeiter: " . UserModel::getOne($hoursEntry['userId'])->name,
'articleUnit' => 'Std.',
'amount' => $hoursEntry['hourCount'],
'price' => $hoursEntry['hourlyPrice'] * $hoursEntry['hourCount'] ?? 0,
];
}
if ($hoursEntry['carId']) {
$positions[] = [
'articleTitle' => "Fahrkostenpauschale",
'articleDescription' => "Fahrzeug: " . TimerecordingCarModel::getOne($hoursEntry['carId'])->number_plate,
'articleTitle' => "Fahrkostenpauschale (hin und retour)",
'articleDescription' => "Datum: ". date("d.m.Y", strtotime($hoursEntry['date'])) . " | Fahrzeug: " . TimerecordingCarModel::getOne($hoursEntry['carId'])->number_plate,
'articleUnit' => 'Km',
'amount' => $hoursEntry['kilometerCount'],
'price' => 1 * $hoursEntry['kilometerCount'] ?? 0,
@@ -261,6 +294,21 @@ class WarehouseShippingNoteController extends TTCrud {
}
usort($positions, function ($a, $b) {
$aHasMitarbeiter = str_contains($a['articleDescription'], 'Mitarbeiter');
$bHasMitarbeiter = str_contains($b['articleDescription'], 'Mitarbeiter');
$aHasFahrzeug = str_contains($a['articleDescription'], 'Fahrzeug');
$bHasFahrzeug = str_contains($b['articleDescription'], 'Fahrzeug');
if ($aHasMitarbeiter && !$bHasMitarbeiter) return -1;
if (!$aHasMitarbeiter && $bHasMitarbeiter) return 1;
if ($aHasFahrzeug && !$bHasFahrzeug) return -1;
if (!$aHasFahrzeug && $bHasFahrzeug) return 1;
return 0;
});
$textElements = [];
// parse shippingNote.textElements ({"1":true,"2":true}) to array, fetch each text element and put content into array
$shippingNoteTextElements = json_decode($shippingNote->textElements, true);
@@ -349,6 +397,28 @@ class WarehouseShippingNoteController extends TTCrud {
self::returnJson(array_values($out));
}
protected function changeStatusAction() {
ini_set('display_errors', 1);
ini_set('display_startup_errors', 1);
$json = json_decode(file_get_contents('php://input'), true);
$id = $json['id'];
$status = $json['status'];
if (strlen($id) < 1) {
http_response_code(500);
self::returnJson(['success' => false, 'message' => 'Lieferschein wurde nicht gefunden']);
}
$shippingNote = (array) WarehouseShippingNoteModel::get($id);
if ($shippingNote['status'] === 'invoiced') {
http_response_code(500);
self::returnJson(['success' => false, 'message' => 'Status kann nicht geändert werden']);
}
$shippingNote['status'] = $status;
WarehouseShippingNoteModel::update($shippingNote);
self::returnJson(['success' => true, 'message' => 'Status wurde geändert']);
}
//TODO: either move this to TimerecordingCarController or make it better
protected function timerecordingCarAutoCompleteAction() {
$timerecordingCars = array_map(function ($timerecordingCar) {

View File

@@ -17,8 +17,8 @@ class WarehouseShippingNoteModel extends TTCrudBaseModel {
public ?string $signatureName;
public ?string $signatureDate;
public ?int $eShopOrderId;
public int $create;
public int $createBy;
public ?int $create;
public ?int $createBy;
}

View File

@@ -0,0 +1,33 @@
<?php /** @noinspection ALL */
declare(strict_types=1);
use Phinx\Migration\AbstractMigration;
final class WarehouseModify5 extends AbstractMigration {
public function up(): void {
if ($this->getEnvironment() == "thetool") {
$WarehouseHistory = $this->table("WarehouseHistory", ["signed" => true]);
$WarehouseHistory->changeColumn("old_value", "text", ["null" => true]);
$WarehouseHistory->changeColumn("new_value", "text", ["null" => true]);
$WarehouseHistory->save();
}
if ($this->getEnvironment() == "addressdb") {
}
}
public function down(): void {
if ($this->getEnvironment() == "thetool") {
$WarehouseHistory = $this->table("WarehouseHistory", ["signed" => true]);
$WarehouseHistory->changeColumn("old_value", "string", ["null" => true, "limit" => 255]);
$WarehouseHistory->changeColumn("new_value", "string", ["null" => true, "limit" => 255]);
$WarehouseHistory->save();
}
if ($this->getEnvironment() == "addressdb") {
}
}
}

View File

@@ -0,0 +1,31 @@
<?php /** @noinspection ALL */
declare(strict_types=1);
use Phinx\Migration\AbstractMigration;
final class WarehouseModify6 extends AbstractMigration {
public function up(): void {
if ($this->getEnvironment() == "thetool") {
// add articleNumber varchar 255 to the table WarehouseArticle
$WarehouseArticle = $this->table("WarehouseArticle", ["signed" => true]);
$WarehouseArticle->addColumn("articleNumber", "string", ["null" => false, "limit" => 255]);
$WarehouseArticle->save();
}
if ($this->getEnvironment() == "addressdb") {
}
}
public function down(): void {
if ($this->getEnvironment() == "thetool") {
$WarehouseArticle = $this->table("WarehouseArticle", ["signed" => true]);
$WarehouseArticle->removeColumn("articleNumber");
$WarehouseArticle->save();
}
if ($this->getEnvironment() == "addressdb") {
}
}
}

View File

@@ -165,6 +165,10 @@ class TTCrud extends mfBaseController {
}
}
if (method_exists($this, 'customRowsHandler')) {
$rows = $this->customRowsHandler($rows);
}
self::returnJson(["rows" => $rows,
"autoCompleteData" => $autocompleteData,
"pagination" => ["page" => $page,
@@ -201,12 +205,12 @@ class TTCrud extends mfBaseController {
}
protected function updateAction() {
if (property_exists($this->model, 'createBy') && !isset($this->postData['createBy'])) {
$this->postData['createBy'] = $this->user->id;
}
if (property_exists($this->model, 'create') && !isset($this->postData['create'])) {
$this->postData['create'] = time();
}
// if (property_exists($this->model, 'createBy') && !isset($this->postData['createBy'])) {
// $this->postData['createBy'] = $this->user->id;
// }
// if (property_exists($this->model, 'create') && !isset($this->postData['create'])) {
// $this->postData['create'] = time();
// }
Helper::validateArray($this->postData, array_merge($this->checkArray, ['id' => ['required' => true]]));

View File

@@ -50,5 +50,42 @@ class XinonProject {
return $response;
}
/**
* Search for support tickets using a global search in Xinon OpenProject.
* @param string $search - The search query.
* @param int $pageSize - The number of results to return.
* @return array - The search results.
*/
public function searchSupportTickets(string $search, int $pageSize = 25): array {
$curl = curl_init();
$baseUrl = 'https://project.xinon.at/api/v3/projects/10/work_packages';
$queryParams = [
'pageSize' => 25,
'filters' => json_encode([['search' => ['operator' => '**', 'values' => [$search]]]])
];
curl_setopt_array($curl, array(
CURLOPT_URL => $baseUrl . '?' . http_build_query($queryParams),
CURLOPT_RETURNTRANSFER => true,
CURLOPT_ENCODING => '',
CURLOPT_MAXREDIRS => 10,
CURLOPT_TIMEOUT => 0,
CURLOPT_FOLLOWLOCATION => true,
CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1,
CURLOPT_CUSTOMREQUEST => 'GET',
CURLOPT_HTTPHEADER => array('Content-Type: application/json'),
CURLOPT_USERPWD => 'apikey:' . PROJECT_API_KEY
));
$response = curl_exec($curl);
curl_close($curl);
$json = json_decode($response, true);
return $json['_embedded']['elements'];
}
}
?>

View File

@@ -1,17 +1,19 @@
.fc-event {
border-radius: 2px;
border: none;
cursor: move;
cursor: no-drop;
font-size: 13px;
margin: 3px 7px;
padding: 1px 5px;
text-align: center;
}
.fc-timegrid-event-short .fc-event-main-frame {
flex-direction: row;
overflow: hidden;
margin-left: 10px;
}
.fc-toolbar {
@media (max-width: 767px) {
flex-direction: column;
@@ -184,6 +186,18 @@ thead .fc-day-today .fc-scrollgrid-sync-inner .fc-col-header-cell-cushion {
color: #fff;
}
.fc-scroller-harness-liquid {
overflow: visible;
}
.fc .fc-scroller-harness-liquid {
height: 700px;
}
.card {
overflow: auto;
}
.card-fullscreen {
display: block;
z-index: 1040;
@@ -311,6 +325,12 @@ thead .fc-day-today .fc-scrollgrid-sync-inner .fc-col-header-cell-cushion {
top: 0;
}
.fc-event-attachment-mr {
position: absolute;
right: 11px;
top: 0;
}
.fc-event-type {
position: absolute;
top: 0;
@@ -322,6 +342,14 @@ thead .fc-day-today .fc-scrollgrid-sync-inner .fc-col-header-cell-cushion {
bottom: -4px;
left: -4px;
}
.fc-event-private {
position: absolute;
top: 0px;
right: -2px;
}
.fc-timegrid-event-short .fc-event-time::after {
content: "";
}
@@ -498,7 +526,7 @@ thead .fc-day-today .fc-scrollgrid-sync-inner .fc-col-header-cell-cushion {
#calendar-side-div {
max-height: 850px;
overflow: scroll;
overflow: auto;
}
/* width */
@@ -612,3 +640,37 @@ thead .fc-day-today .fc-scrollgrid-sync-inner .fc-col-header-cell-cushion {
color: #0600ff;
cursor: pointer;
}
.fa-unlock:after {
color: #000;
opacity: 0.7;
}
.fa-unlock:before {
color: #cd0909;
}
.fa-lock:after {
color: #000;
opacity: 0.7;
}
.modal-body .fa-lock:before {
color: #0ab900;
}
.cal-group-name {
display: inline-block;
max-width: 170px;
margin-left: 10px;
}
.group-checkbox {
margin-left: -28px;
margin-top: 4px;
}
.cursor-alias{
cursor: alias;
}

View File

@@ -63,6 +63,7 @@ function getOffset(unixTimestamp) {
document.addEventListener('DOMContentLoaded', function () {
let checkbox = $(".form-check-input");
let visibleCalendars = [];
let mycalendar_id = $('#calendar-id').data('calendarid');
visibleCalendars.push(0);
checkbox.each(function () {
if ($(this).prop('checked')) {
@@ -86,6 +87,7 @@ document.addEventListener('DOMContentLoaded', function () {
var duration = null;
var rruleflag = false;
let allDAy;
let cursorclass = "";
$.each($('.calendar-check'), function (index, value) {
if ($(this).prop('checked')) {
rights = true;
@@ -108,7 +110,7 @@ document.addEventListener('DOMContentLoaded', function () {
duration = null;
rruleflag = false;
category = value.ccategory.ccategory;
cursorclass = "";
if (value.calendar_id.calendar_id in calendarRights) {
if (calendarRights[value.calendar_id.calendar_id] == 'all') {
@@ -116,8 +118,18 @@ document.addEventListener('DOMContentLoaded', function () {
} else {
rights = false;
}
if (value.privateflag.privateflag == '1' && mycalendar_id != value.calendar_id.calendar_id) {
rights = false;
}
if (value.isorganizer.isorganizer == '1' && rights) {
movable = true;
} else if (rights) {
movable = false;
cursorclass = "cursor-alias";
} else {
movable = false;
}
@@ -147,9 +159,10 @@ document.addEventListener('DOMContentLoaded', function () {
attachments: value.attachments.attachments,
calendar_id: value.calendar_id,
event_type: value.event_type.event_type,
classNames: ['cal-class-group-' + value.calendar_id.calendar_id, 'cal-class-id-' + value.id.id],
classNames: ['cal-class-group-' + value.calendar_id.calendar_id, 'cal-class-id-' + value.id.id, cursorclass],
textColor: value.txtColor.txtColor,
backgroundColor: value.bgColor.bgColor,
privateflag: value.privateflag.privateflag,
editable: rights,
rruleflag: rruleflag,
dates: rrule,
@@ -183,9 +196,10 @@ document.addEventListener('DOMContentLoaded', function () {
attachments: value.attachments.attachments,
calendar_id: value.calendar_id,
event_type: value.event_type.event_type,
classNames: ['cal-class-group-' + value.calendar_id.calendar_id, 'cal-class-id-' + value.id.id],
classNames: ['cal-class-group-' + value.calendar_id.calendar_id, 'cal-class-id-' + value.id.id, cursorclass],
textColor: value.txtColor.txtColor,
backgroundColor: value.bgColor.bgColor,
privateflag: value.privateflag.privateflag,
editable: rights,
rruleflag: rruleflag,
dates: rrule,
@@ -276,10 +290,12 @@ document.addEventListener('DOMContentLoaded', function () {
schedulerLicenseKey: 'CC-Attribution-NonCommercial-NoDerivatives',
timeZone: 'UTC',
locale: 'de',
height: 'auto',
resourceAreaWidth: '220px',
themeSystem: 'bootstrap4',
snapDuration: '00:15:00',
selectable: true,
weekNumbers: true,
headerToolbar: {
left: "prev,today,next",
center: 'title',
@@ -370,6 +386,13 @@ document.addEventListener('DOMContentLoaded', function () {
} else {
$('#reminder').val('NULL');
}
if (data.data.privateflag.privateflag == 1) {
$('.privacy-click').removeClass('fa-unlock');
$('.privacy-click').removeClass('fa-lock');
$('.privacy-click').addClass('fa-lock');
}
$('#type').val(data.data.type.type);
$('#busy').val(data.data.busy.busy);
isOrganizer = data.data.isorganizer.isorganizer;
@@ -388,6 +411,7 @@ document.addEventListener('DOMContentLoaded', function () {
$('#customer').select2({
placeholder: "Kunden Suche",
minimumInputLength: 3,
dropdownParent: jQuery('#relContainer2'),
// dropdownParent: $('#EventModal'),
language: "de",
ajax: {
@@ -410,7 +434,7 @@ document.addEventListener('DOMContentLoaded', function () {
}
if (data.data.customer_info_send.customer_info_send) {
const obj = JSON.parse(data.data.customer_info_send.customer_info_send);
$('#customer-info-check-info').html('<i class="fa-sharp-duotone fa-solid fa-circle-info"></i>');
$('#customer-info-check-info').html('<i class="fa-sharp-duotone fa-solid fa-circle-info customer-info-point"></i>');
const unixTime = obj.sendtime;
const date = new Date(unixTime * 1000);
const germanDateTime = date.toLocaleString('de-DE', {
@@ -434,7 +458,8 @@ document.addEventListener('DOMContentLoaded', function () {
<div class="text-left"><span class="font-weight-500">gesendet am:</span> ` + germanDateTime + `</div>
`;
var tooltip = new Tooltip($('.fa-circle-info'), {
console.log(title);
var tooltip = new Tooltip($('.customer-info-point'), {
title: title,
placement: 'right',
trigger: 'hover',
@@ -460,7 +485,10 @@ document.addEventListener('DOMContentLoaded', function () {
const datetime = new Date(accept.time * 1000);
const germanDateTime = datetime.toLocaleString('de-DE');
$('.accepted-status').prop('title', accept.user + ' (' + germanDateTime + ')');
} else {
$('.accepted-status').prop('title', '');
}
$('.accepted-status').addClass('fa-circle-check');
$('.event-accepted').text('Akzeptiert');
} else {
@@ -472,6 +500,7 @@ document.addEventListener('DOMContentLoaded', function () {
$('.show-attendee').show();
$('.show-attendee').data('id', info.event.id);
}
if (data.data.attendees.attendees) {
let attendees = JSON.parse(data.data.attendees.attendees);
$.each(attendees, function (index, value) {
@@ -487,6 +516,12 @@ document.addEventListener('DOMContentLoaded', function () {
$('#calendar-attendees').val(attendees).trigger('change');
}
if (info.event.extendedProps.rruleflag == true) {
$('#delete-event').hide();
$('#update-event').hide();
$('.show-attendee').hide();
}
});
if (info.event.extendedProps.clickable) {
@@ -611,16 +646,24 @@ document.addEventListener('DOMContentLoaded', function () {
title += '<div class="text-left font-13"><span class="font-weight-500">geändert von </span> ' + info.event.extendedProps['mname'] + '</div>';
}
if ($('.fc-button-active').hasClass('fc-timeGridWeek-button') || $('.fc-button-active').hasClass('fc-timeGridDay-button')) {
if (info.event.extendedProps['attachment']) {
info.el.querySelector(".fc-event-title").insertAdjacentHTML("afterend", "<div class=\"fc-event-attachment\"><i class=\"fa-light fa-paperclip-vertical\"></i></div>");
}
if (info.event.extendedProps['event_type'] == '2') {
info.el.querySelector(".fc-event-title").insertAdjacentHTML("afterend", "<div class=\"fc-event-type\"><i class=\"fa-light fa-user-helmet-safety\"></i></div>");
}
if (info.event.extendedProps['privateflag'] == 1) {
info.el.querySelector(".fc-event-title").insertAdjacentHTML("afterend", "<div class=\"fc-event-private\"><i class=\"fa-regular fa-event-lock fa-lock\"></i></div>");
}
if (info.event.extendedProps['attachment'] && info.event.extendedProps['privateflag'] == 1) {
info.el.querySelector(".fc-event-title").insertAdjacentHTML("afterend", "<div class=\"fc-event-attachment-mr\"><i class=\"fa-light fa-paperclip-vertical\"></i></div>");
} else if (info.event.extendedProps['attachment']) {
info.el.querySelector(".fc-event-title").insertAdjacentHTML("afterend", "<div class=\"fc-event-attachment\"><i class=\"fa-light fa-paperclip-vertical\"></i></div>");
}
if (info.event.extendedProps['rruleflag']) {
info.el.querySelector(".fc-event-title").insertAdjacentHTML("afterend", "<div class=\"fc-event-recurrence\"><i class=\"fa-regular fa-arrows-rotate\"></i></div>");
}
}
var tooltip = new Tooltip(info.el, {
@@ -784,6 +827,7 @@ if (typeof (EventSource) !== 'undefined') {
// document.getElementById('result').innerHTML += e.data + '<br>';
let checkbox = $(".form-check-input");
let visibleCalendars = [];
let mycalendar_id = $('#calendar-id').data('calendarid');
visibleCalendars.push(0);
checkbox.each(function () {
if ($(this).prop('checked')) {
@@ -801,6 +845,7 @@ if (typeof (EventSource) !== 'undefined') {
var rrule = null;
var duration = null;
var rruleflag = false;
let cursorclass = '';
if (event.rrule) {
rrule = event.rrule;
duration = event.duration;
@@ -814,9 +859,17 @@ if (typeof (EventSource) !== 'undefined') {
}
if (event.isorganizer == '1' && rights) {
movable = true;
} else if (rights) {
cursorclass = "cursor-alias"
movable = false;
} else {
movable = false;
}
if (event.privateflag == '1' && mycalendar_id != event.calendar_id_check.calendar_id) {
rights = false;
movable = false;
}
}
if (event.change_type == '2' && cevent) {
@@ -834,10 +887,11 @@ if (typeof (EventSource) !== 'undefined') {
backgroundColor: event.bgColor,
location: event.location,
event_type: event.event_type,
classNames: ['cal-class-group-' + event.calendar_id, 'cal-class-id-' + event.cal_events_id],
classNames: ['cal-class-group-' + event.calendar_id, 'cal-class-id-' + event.cal_events_id, cursorclass],
attachment: event.attachment,
attachments: event.attachments,
editable: movable,
privateflag: event.privateflag,
rruleflag: rruleflag,
rrule: rrule,
duration: duration,
@@ -863,11 +917,12 @@ if (typeof (EventSource) !== 'undefined') {
editable: false,
location: event.location,
event_type: event.event_type,
classNames: ['cal-class-group-' + event.calendar_id, 'cal-class-id-' + event.cal_events_id],
classNames: ['cal-class-group-' + event.calendar_id, 'cal-class-id-' + event.cal_events_id, cursorclass],
attachment: event.attachment,
attachments: event.attachments,
calendar_id: event.calendar_id_check,
resourceId: event.calendar_id,
privateflag: event.privateflag,
calendar_name: event.calendar_name,
clickable: rights,
mtime: event.mtime,
@@ -892,8 +947,9 @@ if (typeof (EventSource) !== 'undefined') {
textColor: event.txtColor,
backgroundColor: event.bgColor,
location: event.location,
privateflag: event.privateflag,
event_type: event.event_type,
classNames: ['cal-class-group-' + event.calendar_id, 'cal-class-id-' + event.cal_events_id],
classNames: ['cal-class-group-' + event.calendar_id, 'cal-class-id-' + event.cal_events_id, cursorclass],
attachment: event.attachment,
attachments: event.attachments,
editable: movable,
@@ -918,8 +974,9 @@ if (typeof (EventSource) !== 'undefined') {
color: '#ab0000',
editable: false,
location: event.location,
privateflag: event.privateflag,
event_type: event.event_type,
classNames: ['cal-class-group-' + event.calendar_id, 'cal-class-id-' + event.cal_events_id],
classNames: ['cal-class-group-' + event.calendar_id, 'cal-class-id-' + event.cal_events_id, cursorclass],
attachment: event.attachment,
attachments: event.attachments,
calendar_id: event.calendar_id_check,
@@ -986,6 +1043,9 @@ $(document).ready(function () {
$('.attachment-div').empty();
$('#reminder').val('NULL');
$('#type').val('1');
$('.privacy-click').removeClass('fa-lock');
$('.privacy-click').removeClass('fa-unlock');
$('.privacy-click').addClass('fa-unlock');
$('#customer-info-check-info').empty();
$('#customer-info-type').val('1');
$('#customer').val('');
@@ -1061,6 +1121,11 @@ $(document).ready(function () {
var customer_info_type;
var customer_info_type_text;
var customer_info_reminder_check = 0;
var private = $('.privacy-click');
var privateflag = 0;
if (private.hasClass('fa-lock')) {
privateflag = 1;
}
var users = [];
if ($('#allday').is(':checked')) {
@@ -1095,6 +1160,7 @@ $(document).ready(function () {
description: description,
attachments: attachments,
users: users,
privateflag: privateflag,
attendees: $('#calendar-attendees').val(),
customer: customer,
customer_info_check: customer_info_check,
@@ -1149,6 +1215,11 @@ $(document).ready(function () {
var customer_info_type;
var customer_info_type_text;
var customer_info_reminder_check = 0;
var private = $('.privacy-click');
var privateflag = 0;
if (private.hasClass('fa-lock')) {
privateflag = 1;
}
var users = [];
if ($('#allday').is(':checked')) {
@@ -1175,7 +1246,6 @@ $(document).ready(function () {
users.push($(this).val());
});
$.post(requestUpdateUrl, {
id: id,
start: start,
@@ -1189,6 +1259,7 @@ $(document).ready(function () {
description: description,
attachments: attachments,
users: users,
privateflag: privateflag,
attendees: $('#calendar-attendees').val(),
customer: customer,
customer_info_check: customer_info_check,
@@ -1276,17 +1347,6 @@ $(document).ready(function () {
var txtcolors = [];
var calendar_id = [];
var thisis = $(this);
if (init === 0) {
var resourceA = calendar.getResourceById(thisis.data('calendar_id'));
if (resourceA) {
var events = resourceA.getEvents();
events.forEach(function (event) {
event.setProp('backgroundColor', thisis.closest('div').find('.color-input').val());
event.setProp('textColor', thisis.closest('div').find('.color-text-input').val());
});
}
calendar.render();
}
$.each($('.color-input'), function (index, value) {
if ($(this).data('calendar_id')) {
bgcolors.push($(this).closest('div').find('.color-input').val());
@@ -1308,6 +1368,10 @@ $(document).ready(function () {
groups: groups
}, function (data) {
}).done(function (data) {
if (init === 0) {
refreshCalendarEvents();
}
})
});
$('body').on('change', '.calendar-check', function (event, init = 0) {
@@ -1440,6 +1504,7 @@ $(document).ready(function () {
placeholder: "Kunden Suche",
minimumInputLength: 3,
// dropdownParent: $('#EventModal'),
dropdownParent: jQuery('#relContainer2'),
language: "de",
ajax: {
url: requestAddressUrl,
@@ -1487,7 +1552,8 @@ $(document).ready(function () {
return {
q: params.term, // Der Suchbegriff
term: params.term, // Der Suchbegriff
calendars: visibleCalendars // Die dynamische Variable, die du hinzufügen möchtest
calendars: visibleCalendars, // Die dynamische Variable, die du hinzufügen möchtest
cal_id: $('#calendar-id').data('calendarid')
};
},
dataType: 'json',
@@ -1591,6 +1657,11 @@ Xinon GMbH`;
let groupname = $.trim($(this).text());
$(this).html('<input type="text" data-oldname="' + groupname + '" class="form-control cal-group-name-input" value="' + groupname + '">');
var strLength = $(this).find('.cal-group-name-input').val().length * 2;
$(this).find('.cal-group-name-input')[0].setSelectionRange(strLength, strLength);
$(this).find('.cal-group-name-input').focus();
});
$("body").on("click", ".group-checkbox", function () {
@@ -1642,14 +1713,28 @@ Xinon GMbH`;
$(this).removeClass('fa-square-arrow-up');
$(this).addClass('fa-square-arrow-down');
$(this).closest('.calendar-side-borders-sub').find('.calendar-side-borders-sub-inner').hide();
$(".color-input").eq(0).trigger("change", 1);
} else {
$(this).removeClass('fa-square-arrow-down');
$(this).addClass('fa-square-arrow-up');
$(this).closest('.calendar-side-borders-sub').find('.calendar-side-borders-sub-inner').show();
$(".color-input").eq(0).trigger("change", 1);
}
});
$("body").on("click", ".privacy-click", function (e) {
if ($(this).hasClass('fa-unlock')) {
$(this).removeClass('fa-unlock');
$(this).addClass('fa-lock');
$(this).prop('title', 'Privat');
} else {
$(this).removeClass('fa-lock');
$(this).addClass('fa-unlock');
$(this).prop('title', 'Normal');
}
});
$("body").on("keyup", ".cal-group-name-input", function (e) {
if (e.keyCode === 13) {
let groupname = $.trim($(this).val());
@@ -1672,11 +1757,26 @@ Xinon GMbH`;
$(this).closest('div').find('.move-group-div').show();
$(this).closest('div').find('.group-checkbox-div').show();
$(this).closest('.cal-group-name').text($(this).data('oldname'));
}
}
});
$("body").on("blur", ".cal-group-name-input", function (e) {
let groupname = $.trim($(this).val());
if (groupname.length > 3) {
$(this).closest('div').find('.dropdown-group-div').show();
$(this).closest('div').find('.move-group-div').show();
$(this).closest('div').find('.group-checkbox-div').show();
$(this).closest('.cal-group-name').text(groupname);
$(".color-input").eq(0).trigger("change", 1);
} else {
$(this).closest('div').find('.dropdown-group-div').show();
$(this).closest('div').find('.move-group-div').show();
$(this).closest('div').find('.group-checkbox-div').show();
$(this).closest('.cal-group-name').text($(this).data('oldname'));
}
});
function getGroups() {
const groups = [];
@@ -1794,6 +1894,7 @@ Xinon GMbH`;
calendar.removeAllEvents();
let checkbox = $(".calendar-check");
let visibleCalendars = [];
let mycalendar_id = $('#calendar-id').data('calendarid');
visibleCalendars.push(0);
checkbox.each(function () {
if ($(this).prop('checked')) {
@@ -1817,6 +1918,7 @@ Xinon GMbH`;
var duration = null;
var rruleflag = false;
let allDAy = false;
let cursorclass = "";
$.each($('.calendar-check'), function (index, value) {
if ($(this).prop('checked')) {
rights = true;
@@ -1838,6 +1940,7 @@ Xinon GMbH`;
duration = null;
rruleflag = false;
allDAy = false;
cursorclass = "";
category = value.ccategory.ccategory;
if (value.rrule.rrule) {
rrule = value.rrule.rrule;
@@ -1853,9 +1956,19 @@ Xinon GMbH`;
}
if (value.isorganizer.isorganizer == '1' && rights) {
movable = true;
} else if (rights) {
movable = false;
cursorclass = "cursor-alias";
} else {
movable = false;
}
if (value.privateflag.privateflag == '1' && mycalendar_id != value.calendar_id.calendar_id) {
rights = false;
movable = false;
}
if (value.allDay.allDay == "1") {
allDAy = true;
}
@@ -1882,10 +1995,11 @@ Xinon GMbH`;
attachments: value.attachments.attachments,
calendar_id: value.calendar_id,
event_type: value.event_type.event_type,
classNames: ['cal-class-group-' + value.calendar_id.calendar_id, 'cal-class-id-' + value.id.id],
classNames: ['cal-class-group-' + value.calendar_id.calendar_id, 'cal-class-id-' + value.id.id, cursorclass],
textColor: value.txtColor.txtColor,
backgroundColor: value.bgColor.bgColor,
editable: rights,
privateflag: value.privateflag.privateflag,
rruleflag: rruleflag,
dates: rrule,
duration: duration,
@@ -1918,10 +2032,11 @@ Xinon GMbH`;
attachments: value.attachments.attachments,
calendar_id: value.calendar_id,
event_type: value.event_type.event_type,
classNames: ['cal-class-group-' + value.calendar_id.calendar_id, 'cal-class-id-' + value.id.id],
classNames: ['cal-class-group-' + value.calendar_id.calendar_id, 'cal-class-id-' + value.id.id, cursorclass],
textColor: value.txtColor.txtColor,
backgroundColor: value.bgColor.bgColor,
editable: rights,
privateflag: value.privateflag.privateflag,
rruleflag: rruleflag,
dates: rrule,
duration: duration,

View File

@@ -1,74 +1,235 @@
// noinspection JSUnusedLocalSymbols
const articleAPIUrl = `${window['TT_CONFIG']['BASE_PATH']}/WarehouseArticle/autocomplete`;
const articlePacketAPIUrl = `${window['TT_CONFIG']['BASE_PATH']}/WarehouseArticlePacket/autocomplete`;
Vue.component('warehouse-e-shop-order-modal-positions-mgmt', {
props: {
id: {type: [String, Number], required: true},
}, data() {
return {
window: window,
positions: [],
articleAPIUrl: articleAPIUrl,
articlePacketAPIUrl: articlePacketAPIUrl,
articleNames: {},
articlePacketNames: {},
articleId: null,
articlePacketId: null,
amount: null,
editIndex: null,
}
}, async mounted() {
// get all positions by /WarehouseEShopOrder/getAllItemsPerOrder?orderId=ID
const response = await axios.get(`${window['TT_CONFIG']['BASE_PATH']}/WarehouseEShopOrder/getAllItemsPerOrder?orderId=${this.id}`);
this.positions = response.data;
}, methods: {
async fetchNames() {
console.log("hallohallo");
// TODO: there must be a better way to do this
for (const position of this.positions) {
if (position.articleId) this.$set(this.articleNames, position.articleId, 'Loading...');
if (position.articlePacketId) this.$set(this.articlePacketNames, position.articlePacketId, 'Loading...');
}
const articlePromises = this.positions.filter(position => position.articleId)
.map(position => axios.get(window.TT_CONFIG["BASE_PATH"] + '/WarehouseArticle/autoComplete?searchedID=' + position.articleId));
const articlePacketPromises = this.positions.filter(position => position.articlePacketId)
.map(position => axios.get(window.TT_CONFIG["BASE_PATH"] + '/WarehouseArticlePacket/autoComplete?searchedID=' + position.articlePacketId));
const articleResponses = await Promise.all(articlePromises);
const articlePacketResponses = await Promise.all(articlePacketPromises);
for (const response of articleResponses) {
this.$set(this.articleNames, response.data[0].value, response.data[0].text);
}
for (const response of articlePacketResponses) {
this.$set(this.articlePacketNames, response.data[0].value, response.data[0].text);
}
},
async addPosition() {
if (!this.articleId && !this.articlePacketId) return window.notify('error', 'Bitte wählen Sie einen Artikel oder ein Artikel Packet aus.');
if (this.editIndex !== null) return this.updatePosition();
const response = await axios.post(`${window['TT_CONFIG']['BASE_PATH']}/WarehouseEShopOrder/addPosition`, {
orderId: this.id,
articleId: this.articleId,
articlePacketId: this.articlePacketId,
quantity: this.amount,
});
if (response.data.success) {
this.positions.push(response.data.position);
this.articleId = null;
this.articlePacketId = null;
this.amount = null;
window.notify('success', 'Position hinzugefügt');
} else {
window.notify('error', response.data.message);
}
},
async updatePosition() {
const position = this.positions[this.editIndex];
const response = await axios.post(`${window['TT_CONFIG']['BASE_PATH']}/WarehouseEShopOrder/updatePosition`, {
id: position.id,
articleId: this.articleId,
articlePacketId: this.articlePacketId,
quantity: this.amount,
});
if (response.data.success) {
this.positions[this.editIndex] = response.data.position;
this.articleId = null;
this.articlePacketId = null;
this.amount = null;
this.editIndex = null;
window.notify('success', response.data.message);
} else {
window.notify('error', response.data.message);
}
},
async deletePosition(id) {
const response = await axios.post(`${window['TT_CONFIG']['BASE_PATH']}/WarehouseEShopOrder/deletePosition`, {id});
if (response.data.success) {
this.positions = this.positions.filter(position => position.id !== id);
window.notify('success', response.data.message);
} else {
window.notify('error', response.data.message);
}
},
async editPosition(id) {
const position = this.positions.find(position => position.id === id);
this.articleId = position.articleId;
this.articlePacketId = position.articlePacketId;
this.amount = position.quantity;
this.editIndex = this.positions.indexOf(position);
},
}, watch: {positions: {handler: 'fetchNames', immediate: true}}, //language=Vue
template: `
<div>
<div style="display: grid;grid-template-columns: 2fr 1fr 0.5fr 1fr;grid-gap: 10px;">
<tt-autocomplete v-model="articleId" :api-url="articleAPIUrl" label="Artikel" sm ref="article"/>
<tt-autocomplete v-model="articlePacketId" :api-url="articlePacketAPIUrl" label="Artikel Packet" sm/>
<tt-input v-model="amount" label="Menge" sm/>
<div style="display: flex;flex-direction: column;justify-content: center;padding-top: 13px;">
<button @click="addPosition" class="btn btn-primary">Hinzufügen</button>
</div>
</div>
<div style="display: flex; align-items: center; justify-content: center;">
<table class="table table-striped table-sm" style="width: max-content">
<thead>
<tr>
<th>Artikel</th>
<th>Menge</th>
<th>Aktionen</th>
</tr>
</thead>
<tbody>
<tr v-if="positions.length === 0">
<td colspan="4" class="text-center">Keine Einträge</td>
</tr>
<tr v-for="(position, index) in positions" :key="index">
<td>{{ position.articleId ? articleNames[position.articleId] : position.articlePacketId ? articlePacketNames[position.articlePacketId] :
'Loading...' }}
</td>
<td>{{ position.quantity }}</td>
<td>
<button class="btn btn-sm btn-danger" @click="deletePosition(position.id)">Löschen</button>
<button class="btn btn-sm btn-primary" @click="editPosition(position.id)">Bearbeiten</button>
</td>
</tr>
</tbody>
</table>
</div>
</div>
`,
})
Vue.component('warehouse-e-shop-order', {
//language=Vue
template: `
<tt-card>
<tt-table-crud @openHistory="historyModal = true; historyModalId = $event.id"
@openSingleOrderEmail="openSingleOrderEmailModal = true; singleOrderEmailModalId = $event.id"
@createShippingNote="createShippingNote($event.id)"
@showTrackingHistory="openTrackingHistoryModal = true; trackingHistoryModalId = $event.id"
ref="table">
template: `
<tt-card>
<tt-table-crud @openHistory="historyModal = true; historyModalId = $event.id"
@openSingleOrderEmail="openSingleOrderEmailModal = true; singleOrderEmailModalId = $event.id"
@createShippingNote="createShippingNote($event.id)"
@showTrackingHistory="openTrackingHistoryModal = true; trackingHistoryModalId = $event.id"
ref="table">
<template v-slot:table-top-buttons>
<button @click="createCSVExportAndMarkAsAccepted" type="button" class="btn btn-outline-success">
<i class="fas fa-file-excel"></i>
Excel Export für neue Bestellungen
</button>
</template>
<template v-slot:table-top-buttons>
<button @click="createCSVExportAndMarkAsAccepted" type="button" class="btn btn-outline-success">
<i class="fas fa-file-excel"></i>
Excel Export für neue Bestellungen
</button>
</template>
<template v-slot:create="{ row }">
{{ window.moment(row.create * 1000).format('DD.MM.YYYY HH:mm:ss') }}
</template>
<template v-slot:create="{ row }">
{{ window.moment(row.create * 1000).format('DD.MM.YYYY HH:mm:ss') }}
</template>
<template v-slot:expandedRow="{ row }">
<div>
<ul class="list-group" v-if="articleItems && articleItems[row.id]">
<li class="list-group-item" v-for="item in articleItems[row.id]">
Menge: {{ item.quantity }} | {{ item.articlePacketTitle || item.articleTitle }}
<template v-slot:expandedRow="{ row }">
<div>
<ul class="list-group" v-if="articleItems && articleItems[row.id]">
<li class="list-group-item" v-for="item in articleItems[row.id]">
Menge: {{ item.quantity }} | {{ item.articlePacketTitle || item.articleTitle }}
</li>
</ul>
</div>
</template>
<template v-slot:positions-modal="{ crudModalData }">
<div>
<warehouse-e-shop-order-modal-positions-mgmt :id="crudModalData.id"/>
</div>
</template>
</tt-table-crud>
<warehouse-history-modal :show.sync="historyModal" :id="historyModalId"/>
<!-- add a tt-modal which just shows some text from the api to show a email for a single order-->
<tt-modal :show.sync="openSingleOrderEmailModal"
:title="'Email für Bestellung ' + singleOrderEmailModalId"
@close="openSingleOrderEmailModal = false"
save-text="E-Mail senden"
@submit="sendSingleOrderEmail"
:delete="false"
>
<div v-if="singleOrderEmailModalId && singleOrderEmailModalText">
<p v-html="singleOrderEmailModalText.body.replaceAll('\\n', '<br>')"></p>
</div>
<!-- else show loader-->
<div v-else>
<tt-loader/>
</div>
</tt-modal>
<tt-modal :show.sync="openTrackingHistoryModal"
:title="'Tracking Historie für Bestellung ' + trackingHistoryModalId"
@close="openTrackingHistoryModal = false"
:delete="false"
:save="false"
>
<div v-if="trackingHistoryModalData">
<ul class="list-group">
<li class="list-group item" v-for="entry in trackingHistoryModalData.history">
<strong>{{ entry.date }} {{ entry.time }}</strong> - {{ entry.evtDscr }}
</li>
</ul>
</div>
</template>
</tt-table-crud>
<warehouse-history-modal :show.sync="historyModal" :id="historyModalId"/>
<!-- add a tt-modal which just shows some text from the api to show a email for a single order-->
<tt-modal :show.sync="openSingleOrderEmailModal"
:title="'Email für Bestellung ' + singleOrderEmailModalId"
@close="openSingleOrderEmailModal = false"
save-text="E-Mail senden"
@submit="sendSingleOrderEmail"
:delete="false"
>
<div v-if="singleOrderEmailModalId && singleOrderEmailModalText">
<p v-html="singleOrderEmailModalText.body.replaceAll('\\n', '<br>')"></p>
</div>
<!-- else show loader-->
<div v-else>
<tt-loader/>
</div>
</tt-modal>
<tt-modal :show.sync="openTrackingHistoryModal"
:title="'Tracking Historie für Bestellung ' + trackingHistoryModalId"
@close="openTrackingHistoryModal = false"
:delete="false"
:save="false"
>
<div v-if="trackingHistoryModalData">
<ul class="list-group">
<li class="list-group item" v-for="entry in trackingHistoryModalData.history">
<strong>{{ entry.date }} {{ entry.time }}</strong> - {{ entry.evtDscr }}
</li>
</ul>
</div>
</tt-modal>
</tt-modal>
</tt-card>
`, data() {
</tt-card>
`, data() {
return {
window: window,
historyModal: false,
@@ -79,7 +240,10 @@ Vue.component('warehouse-e-shop-order', {
singleOrderEmailModalText: null,
openTrackingHistoryModal: false,
trackingHistoryModalId: null,
trackingHistoryModalData: null,
trackingHistoryModalData: null, // modal
positionsEntryArticleId: null,
positionsEntryArticlePacketId: null,
positionsEntryAmount: null,
}
}, async mounted() {
const response = await axios.get(`${window['TT_CONFIG']['BASE_PATH']}/WarehouseEShopOrder/getAllItemsPerOrder`);
@@ -92,8 +256,7 @@ Vue.component('warehouse-e-shop-order', {
if (response.data.shippingNoteId) {
window.open(`${window['TT_CONFIG']['BASE_PATH']}/WarehouseShippingNote/createPDF?id=${response.data.shippingNoteId}&price=true`, '_blank');
}
},
async sendSingleOrderEmail() {
}, async sendSingleOrderEmail() {
const response = await axios.get(`${window['TT_CONFIG']['BASE_PATH']}/WarehouseEShopOrder/singleOrderEmail?id=${this.singleOrderEmailModalId}`);
if (response.data.message) {
window.notify(response.data.success === true ? 'success' : 'error', response.data.message);
@@ -148,8 +311,7 @@ Vue.component('warehouse-e-shop-order', {
.then(response => this.singleOrderEmailModalText = response.data)
.catch(error => window.notify('error', error.response?.data?.message || 'Ein Fehler ist aufgetreten'));
}
},
async trackingHistoryModalId() {
}, async trackingHistoryModalId() {
this.openTrackingHistoryModal = !!this.trackingHistoryModalId;
this.trackingHistoryModalData = null;
if (this.trackingHistoryModalId) {

View File

@@ -1,9 +1,13 @@
.warehouse-shipping-note-modal-positions-entry-container {
display: grid;
grid-template-columns: 2fr 1fr 1fr 1fr;
grid-template-columns: 2fr 1fr 0.5fr 1fr 1fr;
grid-gap: 10px;
}
.warehouse-shipping-note-modal-positions-entry-container.hidePrice {
grid-template-columns: 2fr 1fr 0.5fr 1fr;
}
.warehouse-shipping-note-modal-positions-entry-actions, .warehouse-shipping-note-modal-hours-entry-actions {
display: flex;
flex-direction: column;
@@ -13,12 +17,16 @@
.warehouse-shipping-note-modal-hours-entry-container {
display: grid;
grid-template-columns: 2fr 1fr 1fr 2fr 1fr 1fr 1fr;
grid-template-columns: 2fr 1fr 1fr 1fr 2fr 1fr 1fr 1fr;
grid-gap: 10px;
}
.warehouse-shipping-note-modal-hours-entry-container.hideHourlyPrice {
grid-template-columns: 2fr 1fr 1fr 2fr 1fr 1fr;
grid-template-columns: 2fr 1fr 1fr 1fr 2fr 1fr 1fr;
}
.warehouse-shipping-note-modal-hours-entry-container.hideHourlyPrice.hideKilometer {
grid-template-columns: 2fr 1fr 1fr 1fr 2fr 1fr;
}
@media (min-width: 992px) {
@@ -36,10 +44,19 @@
grid-gap: 10px;
}
.warehouse-shipping-note-modal-positions-entry-actions, .warehouse-shipping-note-modal-hours-entry-actions {
.warehouse-shipping-note-modal-positions-entry-actions {
grid-column: 2;
}
.warehouse-shipping-note-modal-hours-entry-actions {
grid-column: 1 / span 2;
margin-bottom: 8px;
}
.modal .table.table-striped.table-sm button{
margin-bottom: 4px;
}
.signModal > div {
margin: 0;
width: 100vw;

View File

@@ -80,7 +80,10 @@ Vue.component('warehouse-shipping-note', {
signingShippingNoteId: null
}
},
methods: {
mounted() {
// check if get parameter doAction = createNew is set, if so open the modal with 'create' as id
if (window.location.search.includes('doAction=createNew')) {
this.shippingNoteModalId = 'create';
}
}
})

View File

@@ -47,17 +47,19 @@ Vue.component('warehouse-shipping-note-modal-hours-entry', {
hourCount: '',
kilometerCount: '',
hourlyPrice: '',
priceType: 'normal',
}
},
//language=Vue
template: `
<div class="warehouse-shipping-note-modal-hours-entry-container" v-bind:class="{ 'hideHourlyPrice': !showHourlyPrice }">
<div class="warehouse-shipping-note-modal-hours-entry-container" v-bind:class="{ 'hideHourlyPrice': !showHourlyPrice, 'hideKilometer': window.TT_CONFIG['WAREHOUSE_ADMIN'] != true }">
<tt-autocomplete v-model="userId" :api-url="userApiUrl" label="Mitarbeiter" sm/>
<tt-input v-model="date" label="Datum" type="date" sm/>
<tt-input v-model="hourCount" label="Stunden" sm/>
<tt-select v-model="priceType" label="Stundenart" sm :options="[{text: 'Normal', value: 'normal'}, {text: '+50%', value: '50'}, {text: '+100%', value: '100'}]"/>
<tt-autocomplete v-model="carId" :api-url="carApiUrl" label="Fahrzeug" sm/>
<tt-input v-model="hourlyPrice" label="Stundenlohn" type="number" sm v-if="showHourlyPrice"/>
<tt-input :disabled="carId === ''" v-model="kilometerCount" label="Kilometer" sm/>
<tt-input v-show="window.TT_CONFIG['WAREHOUSE_ADMIN'] == true" :disabled="carId === ''" v-model="kilometerCount" label="Kilometer" sm/>
<div class="warehouse-shipping-note-modal-hours-entry-actions">
<button @click="createOrUpdate" class="btn btn-sm btn-primary">Speichern</button>
</div>
@@ -74,6 +76,7 @@ Vue.component('warehouse-shipping-note-modal-hours-entry', {
userId: this.userId,
date: this.date,
hourCount: this.hourCount,
priceType: this.priceType,
hourlyPrice: this.hourlyPrice || null,
carId: this.carId ? this.carId : null,
kilometerCount: this.carId ? this.kilometerCount : null
@@ -103,7 +106,7 @@ Vue.component('warehouse-shipping-note-modal-hours-entry', {
if (!this.userId || this.carId) return;
const response = await axios.get(window.TT_CONFIG["BASE_PATH"] + '/WarehouseShippingNote/timerecordingCarForUser?userId=' + this.userId);
if (response.data.status === 'USER_NO_CAR') {
this.window.notify('info', 'Kein zugewiesenes Fahrzeug gefunden');
// this.window.notify('info', 'Kein zugewiesenes Fahrzeug gefunden');
this.carId = '';
return;
}
@@ -165,7 +168,7 @@ Vue.component('warehouse-shipping-note-modal-hours-view', {
<td>{{ userNames[entry.userId] }}</td>
<td>{{ window.moment(entry.date).format('DD.MM.YYYY') }}</td>
<td>{{ entry.hourCount }}</td>
<td>{{ entry.kilometerCount }}</td>
<td v-show="window.TT_CONFIG['WAREHOUSE_ADMIN'] == true">{{ entry.kilometerCount }}</td>
<td v-if="showHourlyPrice">{{ entry.hourlyPrice }}</td>
<td>
<button class="btn btn-sm btn-danger" @click="$emit('delete', entry)">Löschen</button>
@@ -254,21 +257,24 @@ Vue.component('warehouse-shipping-note-modal-positions-entry', {
data() {
return {
window: window,
isAdmin: false,
articleApiUrl: window.TT_CONFIG['BASE_PATH'] + '/WarehouseArticle/autoComplete',
articlePacketApiUrl: window.TT_CONFIG['BASE_PATH'] + '/WarehouseArticlePacket/autoComplete',
articleId: '',
articlePacketId: '',
amount: '',
price: '',
isEnergieMaterial: false,
}
},
//language=Vue
template: `
<div class="warehouse-shipping-note-modal-positions-entry-container">
<div class="warehouse-shipping-note-modal-positions-entry-container" :class="{ 'hidePrice': !isAdmin }">
<tt-autocomplete v-model="articleId" :api-url="articleApiUrl" label="Artikel" sm ref="article"/>
<!-- <tt-autocomplete v-model="articlePacketId" :api-url="articlePacketApiUrl" label="Artikel Packet" sm/>-->
<tt-input v-model="amount" label="Menge" sm/>
<tt-input v-model="price" label="Preis" type="number" sm/>
<tt-checkbox v-model="isEnergieMaterial" label="Energie Material" sm/>
<tt-input v-show="isAdmin" v-model="price" label="Preis" type="number" sm/>
<div class="warehouse-shipping-note-modal-positions-entry-actions">
<button @click="createOrUpdate" class="btn btn-sm btn-primary">Speichern</button>
</div>
@@ -280,8 +286,10 @@ Vue.component('warehouse-shipping-note-modal-positions-entry', {
if (!this.amount) return this.window.notify('error', 'Bitte füllen sie die Menge aus');
const data = {
amount: this.amount,
isEnergieMaterial: this.isEnergieMaterial,
price: parseFloat(this.price) ?? ''
}
if (isNaN(data.price)) data.price = '';
if (!this.articleId && this.$refs.article.displayValue) {
data.articleText = this.$refs.article.displayValue;
} else if (this.articleId) {
@@ -317,6 +325,7 @@ Vue.component('warehouse-shipping-note-modal-positions-view', {
data() {
return {
window: window,
isAdmin: false,
articleNames: {},
articlePacketNames: {},
}
@@ -329,7 +338,8 @@ Vue.component('warehouse-shipping-note-modal-positions-view', {
<tr>
<th>Artikel</th>
<th>Menge</th>
<th>Preis</th>
<th>Energie Material</th>
<th v-if="isAdmin">Preis</th>
<th>Aktionen</th>
</tr>
</thead>
@@ -342,7 +352,8 @@ Vue.component('warehouse-shipping-note-modal-positions-view', {
position.articleText }}
</td>
<td>{{ position.amount }}</td>
<td>{{ (position.price?.toFixed(2)) }}</td>
<td>{{ position?.isEnergieMaterial ? 'Ja' : 'Nein' }}</td>
<td v-if="isAdmin">{{ (position.price?.toFixed(2)) }} €</td>
<td>
<button class="btn btn-sm btn-danger" @click="$emit('delete', position)">Löschen</button>
<button class="btn btn-sm btn-primary" @click="$emit('edit', position)">Bearbeiten</button>
@@ -497,13 +508,13 @@ Vue.component('warehouse-shipping-note-modal', {
</div>
<!-- TODO: fix these buttons-->
<template v-slot:footer-prepend v-if="id !== 'create'">
<button v-if="window.TT_CONFIG['WAREHOUSE_ADMIN'] == true && status === 'new'" class="btn btn-warning" @click="alert('In Bearbeitung')">In
<button v-if="window.TT_CONFIG['WAREHOUSE_ADMIN'] == true && status === 'new'" class="btn btn-warning" @click="changeStatus('in_progress')">In
Bearbeitung
</button>
<button v-if="window.TT_CONFIG['WAREHOUSE_ADMIN'] == true && (status === 'new' || status === 'in_progress')" class="btn btn-success"
@click="alert('Accepted')">Akzeptieren
@click="changeStatus('accepted')">Akzeptieren
</button>
<button v-if="window.TT_CONFIG['WAREHOUSE_ADMIN'] == true && status === 'accepted'" class="btn btn-info" @click="alert('Invoiced')">
<button v-if="window.TT_CONFIG['WAREHOUSE_ADMIN'] == true && status === 'accepted'" class="btn btn-info" @click="changeStatus('invoiced')">
Verrechnet
</button>
<button class="btn btn-info" @click="$emit('open-signing-modal', id)">Unterschreiben</button>
@@ -532,6 +543,17 @@ Vue.component('warehouse-shipping-note-modal', {
this.textElements = [];
}
}
} else {
const knr = new URLSearchParams(window.location.search).get('knr');
if (knr) {
const response = await axios.get(window.TT_CONFIG["BASE_PATH"] + '/Address/Api?do=findAddress&fibu_primary_account=1&autocomplete=1&q=' + knr);
for (const address of response.data) {
if (address.text.endsWith(`[${knr}]`)) {
this.billAddrId = address.value;
break;
}
}
}
}
},
methods: {
@@ -544,6 +566,16 @@ Vue.component('warehouse-shipping-note-modal', {
this.window.notify('error', response.data.message || 'Ein Fehler ist aufgetreten');
}
},
async changeStatus(newStatus) {
const response = await axios.post(window.TT_CONFIG.BASE_PATH + '/WarehouseShippingNote/changeStatus', {id: this.id, status: newStatus});
if (response.data.success) {
this.window.notify('success', response.data.message || 'Erfolgreich aktualisiert');
this.status = newStatus;
this.$emit('close');
} else {
this.window.notify('error', response.data.message || 'Ein Fehler ist aufgetreten');
}
},
async submit() {
const data = {
billingAddressId: this.billAddrId,
@@ -677,8 +709,7 @@ Vue.component('warehouse-shipping-note-modal-address', {
address.deliveryAddressName === this.delAddrName &&
address.deliveryAddressLine === this.delAddrLine &&
address.deliveryAddressPLZ === this.delAddrPLZ &&
address.deliveryAddressCity === this.delAddrCity &&
address.deliveryAddressEMail === this.delAddrEMail);
address.deliveryAddressCity === this.delAddrCity);
if (foundAddress) {
this.addressMode = 'existing';
this.selectedAddr = foundAddress.id;

View File

@@ -6,7 +6,7 @@ document.addEventListener('DOMContentLoaded', () => {
// @formatter:on
const notyf = new Notyf({
duration: 10000, position: {
duration: 6000, position: {
x: 'right', y: 'bottom',
}, ripple: true, dismissible: true, fontSize: '32px',
types: [{

View File

@@ -1,6 +1,8 @@
//TODO: if autocomplete is focus and the input has not a single character still show "Bitte mindestens 3 Zeichen eingeben"
//TODO: fix the fetchSuggestions function to not show a loading spinner when the input gets cleared
//TODO: fix so we can use arrow keys to navigate the suggestions
// TODO: Implement giving the option without the need of an API || need to use computed property to filter the items
// TODO: Fix the weirdness with timeout and selecting the suggestion
Vue.component('tt-autocomplete', {
template: `
@@ -66,9 +68,8 @@ Vue.component('tt-autocomplete', {
</ul>
</div>
</div>
`, // TODO: Implement giving the option without the need of an API || need to use computed property to filter the items
// TODO: Fix the weirdness with timeout and selecting the suggestion
props: {
`,
props: {
value: {type: [String, Number]},
label: {type: String, required: false},
apiUrl: String,
@@ -76,19 +77,26 @@ Vue.component('tt-autocomplete', {
items: {type: Array, default: () => []},
sm: {type: Boolean, default: true},
row: {type: Boolean, default: false},
}, async mounted() {
this.updateDisplayValue().then();
}, data() {
return {
displayingItems: [], displayValue: '', isLoading: false, showSuggestions: false, cursor: -1, fetchSuggestionsDebounceTimer: null, disableIDFetch: false
displayingItems: [],
oldDisplayValue: '',
displayValue: '',
isLoading: false,
showSuggestions: false,
cursor: -1,
fetchSuggestionsDebounceTimer: null,
disableIDFetch: false
};
}, watch: {
value: {handler: 'updateDisplayValue', immediate: true},
apiUrl() {
this.fetchSuggestions();
},
async mounted() {
this.updateDisplayValue().then();
},
methods: {
setOldDisplayValue(newValue, oldValue) {
this.oldDisplayValue = oldValue;
},
}, methods: {
async updateDisplayValue(newValue) {
async updateDisplayValue(newValue, oldValue) {
if (this.disableIDFetch) {
this.disableIDFetch = false;
return;
@@ -106,7 +114,7 @@ Vue.component('tt-autocomplete', {
this.displayValue = selectedItem ? selectedItem.text : '';
} else {
this.$emit('input', '');
this.displayValue = '';
this.displayValue = this.displayValue.replace(this.oldDisplayValue, '');
}
},
onInput(event) {
@@ -175,4 +183,9 @@ Vue.component('tt-autocomplete', {
this.$emit('input', '');
}
},
watch: {
displayValue: {handler: 'setOldDisplayValue', immediate: true},
value: {handler: 'updateDisplayValue', immediate: true},
apiUrl: {handler: 'fetchSuggestions', immediate: true},
}
});

View File

@@ -30,6 +30,8 @@ Vue.component('tt-button', {
'btn-sm': this.sm,
}
if (!this.additionalClass) return classes
for (const className of this.additionalClass.split(' ')) {
classes[className] = true
}

View File

@@ -25,6 +25,7 @@ Vue.component('tt-checkbox', {
<div class="form-group" :class="{'row': row}">
<slot name="prepend"></slot>
<label
style="width: 100%;text-align: center;"
:class="{'col-form-label': row, 'col-sm-4': row, 'col-form-label-sm': sm && row}"
v-if="label"
:for="label">{{ label }}</label>

View File

@@ -44,14 +44,18 @@ Vue.component('tt-modal', {
this.$emit('submit')
}
}
}, //language=Vue
},
data() {
return {window: window, isMobile: navigator.userAgent.match(/(iPad)|(iPhone)|(iPod)|(android)|(webOS)/i) !== null}
},
//language=Vue
template: `
<div class="modal show d-block"
role="dialog"
tabindex="-1"
style="background: rgba(0, 0, 0, 0.5);"
ref="modal"
@mousedown="$emit('update:show', false)"
@mousedown="!isMobile ? $emit('update:show', false) : null"
@keydown.esc="$emit('update:show', false)"
v-if="show">
<div class="modal-dialog modal-lg modal-dialog-scrollable" role="document" @mousedown.stop>

View File

@@ -0,0 +1,109 @@
<?php
//require 'vendor/autoload.php';
require("../../config/config.php");
define('FRONKDB_SQLDEBUG', false);
error_reporting(E_ALL & ~(E_NOTICE | E_STRICT | E_DEPRECATED));
require_once(LIBDIR . "/mvcfronk/mfRouter/mfRouter.php");
require_once(LIBDIR . "/mvcfronk/mfBase/mfBaseModel.php");
require_once(LIBDIR . "/mvcfronk/mfBase/mfBaseController.php");
$me = new User(154);
define("INTERNAL_USER_ID", $me->id);
define("INTERNAL_USER_USERNAME", $me->username);
$calendar = CalendarModel::search(array("checkSubscriptions" => 1));
$adminUser = array(89, 173, 167, 8, 6, 9, 5,123);
foreach ($calendar as $cal) {
$allCalendar[$cal->go_calendar_id] = $cal->user_id;
}
foreach ($calendar as $cal) {
$update = 0;
$allCalendarUser = $allCalendar;
unset($newrights);
if (in_array($cal->user_id, $adminUser)) {
$admin = 1;
} else {
$admin = 0;
}
foreach ($allCalendarUser as $key => $value) {
if ($admin == 1 || $key == $cal->go_calendar_id) {
$newrights[$key] = "all";
} else {
$newrights[$key] = "read";
}
}
if (json_encode($newrights) != $cal->rights) {
$cal->update(array("rights" => json_encode($newrights)));
$update = 1;
}
if ($cal->colors) {
$stdColors = CalendarModel::$standardCalendarColors;
$colorsArray = json_decode($cal->colors, true);
$colors = $colorsArray;
foreach ($allCalendarUser as $key => $value) {
if ($colors[$key]) {
unset($colors[$key]);
} else {
$colorsArray[$key]['bgcolor'] = $stdColors[$key];
$colorsArray[$key]['txtcolor'] = "#000000";
}
}
if ($colors) {
foreach ($colors as $key => $value) {
if ($key != "998" && $key != "997") {
unset($colorsArray[$key]);
}
}
}
if (json_encode($colorsArray) != $cal->colors) {
$cal->update(array("colors" => json_encode($colorsArray)));
$update = 1;
}
}
if ($cal->groups) {
$groupArray = json_decode($cal->groups, true);
$groups = $groupArray;
foreach ($groups as $key => $value) {
foreach ($value["calendars"] as $key2 => $value2) {
if ((!$allCalendarUser[$value2['calendar_id']] && $value2['calendar_id'] != 997 && $value2['calendar_id'] != 998)) {
unset($groupArray[$key]["calendars"][$key2]);
} else {
unset($allCalendarUser[$value2['calendar_id']]);
}
}
}
if ($allCalendarUser) {
foreach ($groups as $key => $value) {
if ($value['name'] == "Persönlich") {
foreach ($allCalendarUser as $key2 => $value2) {
$groupArray[$key]["calendars"][] = array("calendar_id" => "$key2", "checked" => 0, "origin" => 1);
}
}
}
}
if (json_encode($groupArray, JSON_UNESCAPED_UNICODE) != $cal->groups) {
echo "groups";
$cal->update(array("groups" => json_encode($groupArray, JSON_UNESCAPED_UNICODE)));
$update = 1;
}
}
if ($update == 1) {
echo "save" . $cal->id . "\n";
$cal->save();
}
}