Merge branch 'master' into fronkdev
This commit is contained in:
@@ -146,6 +146,16 @@
|
|||||||
</select>
|
</select>
|
||||||
</div>
|
</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>
|
||||||
|
</div>
|
||||||
|
|
||||||
<div class="col-sm-12 col-md-2">
|
<div class="col-sm-12 col-md-2">
|
||||||
<label class="form-label" for="filter_status">Status</label>
|
<label class="form-label" for="filter_status">Status</label>
|
||||||
<select name="filter[status][]" id="filter_status" class="form-control" multiple="multiple">
|
<select name="filter[status][]" id="filter_status" class="form-control" multiple="multiple">
|
||||||
|
|||||||
@@ -148,5 +148,9 @@ TODO: enable option for showing prices
|
|||||||
</div>
|
</div>
|
||||||
<?php endif; ?>
|
<?php endif; ?>
|
||||||
|
|
||||||
|
<div style="padding-top: 16pt">
|
||||||
|
Die Ware bleibt bis zur vollständigen Bezahlung Eigentum der XINON GmbH.
|
||||||
|
</div>
|
||||||
|
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
@@ -562,10 +562,19 @@ class AddressController extends mfBaseController {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
if (isset($_GET['fibu_primary_account'])) {
|
||||||
|
$addresses = array_merge($addresses, AddressModel::search(["parents_only" => $po, "addresstype" => [$role], "mergedName" => $search, "fibu_primary_account" => true]));
|
||||||
|
$addresses = array_merge($addresses, AddressModel::search(["parents_only" => $po, "addresstype" => [$role], "company" => $search, "fibu_primary_account" => true]));
|
||||||
|
$addresses = array_merge($addresses, AddressModel::search(["parents_only" => $po, "addresstype" => [$role], "firstname" => $search, "fibu_primary_account" => true]));
|
||||||
|
$addresses = array_merge($addresses, AddressModel::search(["parents_only" => $po, "addresstype" => [$role], "lastname" => $search, "fibu_primary_account" => true]));
|
||||||
|
$addresses = array_merge($addresses, AddressModel::search(["parents_only" => $po, "addresstype" => [$role], "street" => $search, "fibu_primary_account" => true]));
|
||||||
|
|
||||||
|
} else {
|
||||||
$addresses = array_merge($addresses, AddressModel::search(["parents_only" => $po, "addresstype" => [$role], "mergedName" => $search]));
|
$addresses = array_merge($addresses, AddressModel::search(["parents_only" => $po, "addresstype" => [$role], "mergedName" => $search]));
|
||||||
$addresses = array_merge($addresses, AddressModel::search(["parents_only" => $po, "addresstype" => [$role], "company" => $search]));
|
$addresses = array_merge($addresses, AddressModel::search(["parents_only" => $po, "addresstype" => [$role], "company" => $search]));
|
||||||
$addresses = array_merge($addresses, AddressModel::search(["parents_only" => $po, "addresstype" => [$role], "firstname" => $search]));
|
$addresses = array_merge($addresses, AddressModel::search(["parents_only" => $po, "addresstype" => [$role], "firstname" => $search]));
|
||||||
$addresses = array_merge($addresses, AddressModel::search(["parents_only" => $po, "addresstype" => [$role], "lastname" => $search]));
|
$addresses = array_merge($addresses, AddressModel::search(["parents_only" => $po, "addresstype" => [$role], "lastname" => $search]));
|
||||||
|
}
|
||||||
|
|
||||||
if(!is_array($addresses) || !count($addresses)) {
|
if(!is_array($addresses) || !count($addresses)) {
|
||||||
return false;
|
return false;
|
||||||
|
|||||||
@@ -157,12 +157,17 @@ class CalendarModel
|
|||||||
$where .= " AND calendar_id IN (" . implode(",", $visibleCalendars) . ")";
|
$where .= " AND calendar_id IN (" . implode(",", $visibleCalendars) . ")";
|
||||||
$whereTimeRecording = " AND `Calendar`.`go_calendar_id` IN (" . implode(",", $visibleCalendars) . ")";
|
$whereTimeRecording = " AND `Calendar`.`go_calendar_id` IN (" . implode(",", $visibleCalendars) . ")";
|
||||||
}
|
}
|
||||||
$sql = "SELECT `cal_events`.id, uuid, calendar_id, `cal_events`.user_id, start_time, end_time, timezone, all_day_event, `cal_events`.name,`cal_calendars`.name calendar_name, description, location, repeat_end_time, reminder, ctime,cname, mtime,mname, muser_id, busy, status, resource_event_id, private, rrule, `cal_events`.background, `cal_events`.files_folder_id, read_only, category_id, exception_for_event_id, recurrence_id, is_organizer,event_type,busy,recurrence FROM cal_events INNER JOIN `cal_calendars` ON (`cal_calendars`.`id`=`cal_events`.`calendar_id`) WHERE 1=1 $where ";
|
$sql = "SELECT `cal_events`.id,`cal_events`.categories, uuid, calendar_id, `cal_events`.user_id, start_time, end_time, timezone, all_day_event, `cal_events`.name,`cal_calendars`.name calendar_name, description, location, repeat_end_time, reminder, ctime,cname, mtime,mname, muser_id, busy, status, resource_event_id, private, rrule, `cal_events`.background, `cal_events`.files_folder_id, read_only, category_id, exception_for_event_id, recurrence_id, is_organizer,event_type,busy,recurrence,rrule_events FROM cal_events INNER JOIN `cal_calendars` ON (`cal_calendars`.`id`=`cal_events`.`calendar_id`) WHERE 1=1 $where ";
|
||||||
|
|
||||||
$res = $dbcal->query($sql);
|
$res = $dbcal->query($sql);
|
||||||
if ($dbcal->num_rows($res)) {
|
if ($dbcal->num_rows($res)) {
|
||||||
|
|
||||||
while ($data = $dbcal->fetch_array($res)) {
|
while ($data = $dbcal->fetch_array($res)) {
|
||||||
|
if ($data['categories']) {
|
||||||
|
$categories = json_decode($data['categories'], true);
|
||||||
|
} else {
|
||||||
|
$categories = [];
|
||||||
|
}
|
||||||
unset($byweekday);
|
unset($byweekday);
|
||||||
$rrule = false;
|
$rrule = false;
|
||||||
if ($attachments[$data['uuid']]) {
|
if ($attachments[$data['uuid']]) {
|
||||||
@@ -173,7 +178,7 @@ class CalendarModel
|
|||||||
$attachmentLinks = "";
|
$attachmentLinks = "";
|
||||||
}
|
}
|
||||||
if ($data['all_day_event'] == 1) {
|
if ($data['all_day_event'] == 1) {
|
||||||
if (strpos($data['name'], "Bereitschaft") === false && strpos($data['name'], "Blocker") === false) {
|
if (in_array("Feiertag", $categories)) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
$starttime = date("Y-m-d", $data['start_time']);
|
$starttime = date("Y-m-d", $data['start_time']);
|
||||||
@@ -190,6 +195,7 @@ class CalendarModel
|
|||||||
|
|
||||||
if ($data['recurrence']) {
|
if ($data['recurrence']) {
|
||||||
$recurrence = json_decode($data['recurrence'], true);
|
$recurrence = json_decode($data['recurrence'], true);
|
||||||
|
$rrule_events= json_decode($data['rrule_events'], true);
|
||||||
if ($rrulefreq[$recurrence['pattern']['type']]) {
|
if ($rrulefreq[$recurrence['pattern']['type']]) {
|
||||||
unset ($byweekday);
|
unset ($byweekday);
|
||||||
$freq = $rrulefreq[$recurrence['pattern']['type']];
|
$freq = $rrulefreq[$recurrence['pattern']['type']];
|
||||||
@@ -220,6 +226,7 @@ class CalendarModel
|
|||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
$rrule = false;
|
$rrule = false;
|
||||||
|
$rrule_events = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
if ($calendarColors[$data['calendar_id']]['bgcolor']) {
|
if ($calendarColors[$data['calendar_id']]['bgcolor']) {
|
||||||
@@ -245,7 +252,9 @@ class CalendarModel
|
|||||||
'rights' => array('rights' => $rights, 'order' => $rights),
|
'rights' => array('rights' => $rights, 'order' => $rights),
|
||||||
'location' => array('location' => $data['location']),
|
'location' => array('location' => $data['location']),
|
||||||
'busy' => array('busy' => $data['busy']),
|
'busy' => array('busy' => $data['busy']),
|
||||||
|
'allDay' => array('allDay' => $data['all_day_event']),
|
||||||
'rrule' => array('rrule' => $rrule),
|
'rrule' => array('rrule' => $rrule),
|
||||||
|
'rrule_events' => array('rrule_events' => $rrule_events),
|
||||||
'duration' => array('duration' => $duration),
|
'duration' => array('duration' => $duration),
|
||||||
'event_type' => array('event_type' => $data['event_type']),
|
'event_type' => array('event_type' => $data['event_type']),
|
||||||
'description' => array('description' => ($data['description'])),
|
'description' => array('description' => ($data['description'])),
|
||||||
|
|||||||
@@ -0,0 +1,9 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @property mixed|null $name
|
||||||
|
*/
|
||||||
|
class Graphing extends mfBaseModel
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,64 @@
|
|||||||
|
<?php
|
||||||
|
class GraphingController extends mfBaseController{
|
||||||
|
private string $ZABBIX_API_URL = ZABBIX_API_URL;
|
||||||
|
private string $ZABBIX_API_KEY = ZABBIX_API_KEY;
|
||||||
|
private Zabbix $zabbix;
|
||||||
|
|
||||||
|
protected function init(): void {
|
||||||
|
$me = new User();
|
||||||
|
$me->loadMe();
|
||||||
|
$this->layout()->set("me", $me);
|
||||||
|
$this->me = $me;
|
||||||
|
|
||||||
|
if (!$this->me->isAdmin()) {
|
||||||
|
$this->redirect("dashboard");
|
||||||
|
}
|
||||||
|
|
||||||
|
$this->zabbix = new Zabbix($this->ZABBIX_API_URL, $this->ZABBIX_API_KEY);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected function indexAction() {
|
||||||
|
$this->layout()->set('additionalJS', ["plugins/chart.js/chart.4.4.6.js", "plugins/chart.js/chartjs-adapter-moment.min.js"]);
|
||||||
|
Helper::renderVue($this, "DeviceGraphing", $this->mod, []);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
protected function dataAction() {
|
||||||
|
header('Content-Type: application/json');
|
||||||
|
$hostId = $this->request->hostId;
|
||||||
|
$hostInterfaceItems = $this->zabbix->getHostInterfaceItems($hostId, '');
|
||||||
|
// limit to 25 items
|
||||||
|
$hostInterfaceItems = array_slice($hostInterfaceItems, 0, 25);
|
||||||
|
|
||||||
|
$itemIds = array_map(function($item) {
|
||||||
|
return $item['itemid'];
|
||||||
|
}, $hostInterfaceItems);
|
||||||
|
|
||||||
|
|
||||||
|
$itemValues = $this->zabbix->getItemValues($itemIds, 1000);
|
||||||
|
|
||||||
|
$out = [];
|
||||||
|
foreach ($hostInterfaceItems as $item) {
|
||||||
|
$out[$item['itemid']] = [
|
||||||
|
'name' => str_replace('Bits', 'Mbps', $item['name']),
|
||||||
|
'units' => $item['units'],
|
||||||
|
'values' => []
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
foreach ($itemValues as $itemValue) {
|
||||||
|
$out[$itemValue['itemid']]['values'][] = [
|
||||||
|
'clock' => $itemValue['clock'],
|
||||||
|
'value' => $itemValue['value'] / 1000000
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
// sort by name
|
||||||
|
uasort($out, function($a, $b) {
|
||||||
|
return strcmp($a['name'], $b['name']);
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
die(json_encode($out));
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -164,7 +164,7 @@ class PreorderController extends mfBaseController {
|
|||||||
}
|
}
|
||||||
$this->layout()->set("pagination", $pagination);
|
$this->layout()->set("pagination", $pagination);
|
||||||
$this->layout()->set("preorders", $preorders);
|
$this->layout()->set("preorders", $preorders);
|
||||||
|
$this->layout()->set("partners", PreorderModel::getAllPartners());
|
||||||
}
|
}
|
||||||
|
|
||||||
private function getPreparedFilter($filter) {
|
private function getPreparedFilter($filter) {
|
||||||
|
|||||||
@@ -178,6 +178,24 @@ class PreorderModel {
|
|||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static function getAllPartners(): array {
|
||||||
|
$items = [];
|
||||||
|
|
||||||
|
$db = FronkDB::singleton();
|
||||||
|
|
||||||
|
$res = $db->select("Preorder", "partner_id", "partner_id IS NOT NULL AND partner_id > 0 GROUP BY partner_id");
|
||||||
|
if($db->num_rows($res)) {
|
||||||
|
while($data = $db->fetch_object($res)) {
|
||||||
|
$partner = AddressModel::getOne($data->partner_id);
|
||||||
|
$items[] = [
|
||||||
|
"partner_id" => $data->partner_id,
|
||||||
|
"name" => $partner->getCompanyOrName()
|
||||||
|
];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return $items;
|
||||||
|
}
|
||||||
|
|
||||||
public static function countWithLogistics($filter) {
|
public static function countWithLogistics($filter) {
|
||||||
$db = FronkDB::singleton();
|
$db = FronkDB::singleton();
|
||||||
|
|
||||||
|
|||||||
@@ -4,50 +4,35 @@ class WarehouseShippingNoteController extends TTCrud {
|
|||||||
protected string $headerTitle = 'Lieferscheine';
|
protected string $headerTitle = 'Lieferscheine';
|
||||||
protected bool $createText = false;
|
protected bool $createText = false;
|
||||||
|
|
||||||
protected array $columns = [['key' => 'id', 'text' => 'LS-Nr.', 'required' => false, 'modal' => false, 'table' => ['class' => 'text-nowrap']],
|
//@formatter:off
|
||||||
['key' => 'billingAddressId',
|
protected array $columns = [
|
||||||
'text' => 'Rechnungsadresse',
|
['key' => 'id', 'text' => 'LS-Nr.', 'required' => false, 'modal' => false, 'table' => ['class' => 'text-nowrap']],
|
||||||
'required' => true,
|
['key' => 'billingAddressId', 'text' => 'Rechnungsadresse', 'required' => true, 'type' => 'autocomplete', 'table' => ['class' => 'text-nowrap', 'filter' => 'autocomplete'], 'modal' => ['apiUrl' => 'Address/api?do=findAddress', 'items' => '/Address/Api?do=findAddress', 'type' => 'autocomplete']],
|
||||||
'type' => 'autocomplete',
|
|
||||||
'table' => ['class' => 'text-nowrap', 'filter' => 'autocomplete'],
|
|
||||||
'modal' => ['apiUrl' => 'Address/api?do=findAddress', 'items' => '/Address/Api?do=findAddress', 'type' => 'autocomplete']],
|
|
||||||
['key' => 'deliveryAddressName', 'text' => 'L.-Adr. Name', 'required' => true],
|
['key' => 'deliveryAddressName', 'text' => 'L.-Adr. Name', 'required' => true],
|
||||||
['key' => 'deliveryAddressLine', 'text' => 'L.-Adr.', 'required' => true],
|
['key' => 'deliveryAddressLine', 'text' => 'L.-Adr.', 'required' => true],
|
||||||
['key' => 'deliveryAddressPLZ', 'text' => 'L.-Adr. PLZ', 'required' => true],
|
['key' => 'deliveryAddressPLZ', 'text' => 'L.-Adr. PLZ', 'required' => true],
|
||||||
['key' => 'deliveryAddressEMail', 'text' => 'L.-Adr. EMail', 'required' => true, 'table' => false],
|
['key' => 'deliveryAddressEMail', 'text' => 'L.-Adr. EMail', 'required' => false, 'table' => false],
|
||||||
['key' => 'note', 'text' => 'Notiz', 'required' => true, 'table' => false],
|
['key' => 'note', 'text' => 'Art der Arbeit', 'required' => true, 'table' => false],
|
||||||
['key' => 'status',
|
['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'],]]],
|
||||||
'text' => 'Status',
|
|
||||||
'required' => true,
|
|
||||||
'table' => ['filter' => 'select'],
|
|
||||||
'modal' => ['type' => 'select',
|
|
||||||
'items' => [['value' => 'new', 'text' => 'Neu'],
|
|
||||||
['value' => 'accepted', 'text' => 'Akzeptiert'],
|
|
||||||
['value' => 'invoiced', 'text' => 'In Rechnung gestellt'],]]],
|
|
||||||
['key' => 'positions', 'text' => 'Positionen', 'required' => true, 'table' => false, 'modal' => false],
|
['key' => 'positions', 'text' => 'Positionen', 'required' => true, 'table' => false, 'modal' => false],
|
||||||
['key' => 'create', 'text' => 'Erstellt', 'required' => false, 'modal' => false, 'table' => ['filter' => 'date']],
|
['key' => 'create', 'text' => 'Erstellt', 'required' => false, 'modal' => false, 'table' => ['filter' => 'date']],
|
||||||
['key' => 'createBy',
|
['key' => 'createBy', 'text' => 'Erstellt von', 'required' => true, 'type' => 'autocomplete', 'table' => ['class' => 'text-nowrap', 'filter' => 'select'], 'modal' => ['items' => [], 'type' => 'select',]],
|
||||||
'text' => 'Erstellt von',
|
['key' => 'actions', 'text' => 'Aktionen', 'required' => false, 'modal' => false, 'table' => ['filter' => false, 'sortable' => false, 'class' => 'text-center']],];
|
||||||
'required' => true,
|
|
||||||
'type' => 'autocomplete',
|
|
||||||
'table' => ['class' => 'text-nowrap', 'filter' => 'select'],
|
|
||||||
'modal' => ['items' => [], 'type' => 'select',]],
|
|
||||||
|
|
||||||
['key' => 'actions',
|
protected array $defaultOrder = ['key' => 'create', 'order' => 'DESC'];
|
||||||
'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'],
|
protected array $additionalActions = [['key' => 'openHistory', 'title' => 'Historie', 'class' => 'fas fa-history text-primary'],
|
||||||
['key' => 'print', 'title' => 'Drucken', 'class' => 'fas fa-print text-primary'],
|
['key' => 'print', 'title' => 'Drucken', 'class' => 'fas fa-print text-primary'],
|
||||||
['key' => 'printWithPrice', 'title' => 'Drucken mit Preis', 'class' => 'fas fa-print text-success'],
|
['key' => 'printWithPrice', 'title' => 'Drucken mit Preis', 'class' => 'fas fa-print text-success'],
|
||||||
];
|
];
|
||||||
|
|
||||||
|
protected array $additionalJSVariables = ['WAREHOUSE_ADMIN' => true];
|
||||||
|
|
||||||
protected array $infoMessages = ['create' => 'Lieferschein wurde erstellt.',
|
protected array $infoMessages = ['create' => 'Lieferschein wurde erstellt.',
|
||||||
'update' => 'Lieferschein wurde aktualisiert',
|
'update' => 'Lieferschein wurde aktualisiert',
|
||||||
'delete' => 'Lieferschein wurde gelöscht',
|
'delete' => 'Lieferschein wurde gelöscht',
|
||||||
'noChanges' => 'Keine Änderungen vorgenommen'];
|
'noChanges' => 'Keine Änderungen vorgenommen'];
|
||||||
|
//@formatter:on
|
||||||
|
|
||||||
protected function prepareCrudConfig() {
|
protected function prepareCrudConfig() {
|
||||||
$users = array_map(function ($user) {
|
$users = array_map(function ($user) {
|
||||||
@@ -55,6 +40,10 @@ class WarehouseShippingNoteController extends TTCrud {
|
|||||||
}, UserModel::search());
|
}, UserModel::search());
|
||||||
|
|
||||||
$this->columns[array_search('createBy', array_column($this->columns, 'key'))]['modal']['items'] = $users;
|
$this->columns[array_search('createBy', array_column($this->columns, 'key'))]['modal']['items'] = $users;
|
||||||
|
|
||||||
|
if (!$this->user->can('WarehouseAdmin')) {
|
||||||
|
$this->additionalJSVariables['WAREHOUSE_ADMIN'] = false;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
protected function beforeCreate($postData): bool {
|
protected function beforeCreate($postData): bool {
|
||||||
@@ -81,6 +70,14 @@ class WarehouseShippingNoteController extends TTCrud {
|
|||||||
}
|
}
|
||||||
|
|
||||||
protected function beforeUpdate($postData): bool {
|
protected function beforeUpdate($postData): bool {
|
||||||
|
$shippingNote = WarehouseShippingNoteModel::get($postData['id']);
|
||||||
|
if ($shippingNote->status === 'accepted' || $shippingNote->status === 'invoiced') {
|
||||||
|
http_response_code(500);
|
||||||
|
self::returnJson(['success' => false, 'message' => 'Änderungen nicht mehr möglich']);
|
||||||
|
die();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
$postData['positions'] = json_encode($postData['positions']);
|
$postData['positions'] = json_encode($postData['positions']);
|
||||||
(new WarehouseHistoryController)->create($postData, $this->mod);
|
(new WarehouseHistoryController)->create($postData, $this->mod);
|
||||||
return true;
|
return true;
|
||||||
@@ -242,9 +239,10 @@ class WarehouseShippingNoteController extends TTCrud {
|
|||||||
// json decode hoursEntries and add to positions
|
// json decode hoursEntries and add to positions
|
||||||
$hoursEntries = json_decode($shippingNote->hoursEntries, true);
|
$hoursEntries = json_decode($shippingNote->hoursEntries, true);
|
||||||
foreach ($hoursEntries as $hoursEntry) {
|
foreach ($hoursEntries as $hoursEntry) {
|
||||||
|
// die(json_encode($hoursEntry));
|
||||||
$positions[] = [
|
$positions[] = [
|
||||||
'articleTitle' => "Arbeitsstunden",
|
'articleTitle' => "Arbeitsstunden",
|
||||||
'articleDescription' => "Mitarbeiter: " . UserModel::getOne($hoursEntry['userId'])->name,
|
'articleDescription' => "Datum: ". date("d.m.Y", strtotime($hoursEntry['date'])) . " | Mitarbeiter: " . UserModel::getOne($hoursEntry['userId'])->name,
|
||||||
'articleUnit' => 'Std.',
|
'articleUnit' => 'Std.',
|
||||||
'amount' => $hoursEntry['hourCount'],
|
'amount' => $hoursEntry['hourCount'],
|
||||||
'price' => $hoursEntry['hourlyPrice'] * $hoursEntry['hourCount'] ?? 0,
|
'price' => $hoursEntry['hourlyPrice'] * $hoursEntry['hourCount'] ?? 0,
|
||||||
@@ -354,7 +352,8 @@ class WarehouseShippingNoteController extends TTCrud {
|
|||||||
//TODO: either move this to TimerecordingCarController or make it better
|
//TODO: either move this to TimerecordingCarController or make it better
|
||||||
protected function timerecordingCarAutoCompleteAction() {
|
protected function timerecordingCarAutoCompleteAction() {
|
||||||
$timerecordingCars = array_map(function ($timerecordingCar) {
|
$timerecordingCars = array_map(function ($timerecordingCar) {
|
||||||
return ['value' => $timerecordingCar->id, 'text' => $timerecordingCar->number_plate . " " . $timerecordingCar->brand . " " . $timerecordingCar->model];
|
return ['value' => $timerecordingCar->id,
|
||||||
|
'text' => $timerecordingCar->number_plate . " " . $timerecordingCar->brand . " " . $timerecordingCar->model];
|
||||||
}, TimerecordingCarModel::getAll());
|
}, TimerecordingCarModel::getAll());
|
||||||
|
|
||||||
$out = null;
|
$out = null;
|
||||||
@@ -387,6 +386,36 @@ class WarehouseShippingNoteController extends TTCrud {
|
|||||||
die(json_encode(['success' => true, 'status' => 'USER_NO_CAR']));
|
die(json_encode(['success' => true, 'status' => 'USER_NO_CAR']));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
protected function geoAutocompleteAction() {
|
||||||
|
$search = $this->request->q;
|
||||||
|
$search = urlencode($search);
|
||||||
|
$url = "https://nominatim.haid.in/search?q=$search&format=json";
|
||||||
|
$data = json_decode(file_get_contents($url), true);
|
||||||
|
$out = [];
|
||||||
|
|
||||||
|
|
||||||
|
foreach ($data as $entry) {
|
||||||
|
$parsedDisplayNameParts = [];
|
||||||
|
foreach (explode(',', $entry['display_name']) as $part) {
|
||||||
|
// if str_includes Bezirk remove it
|
||||||
|
if (strpos($part, 'Bezirk') !== false) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
$parsedDisplayNameParts[] = $part;
|
||||||
|
}
|
||||||
|
$out[] = ['value' => $entry['lat'] . "," . $entry['lon'], 'text' => implode(',', $parsedDisplayNameParts)];
|
||||||
|
}
|
||||||
|
self::returnJson($out);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected function geoReverseAction() {
|
||||||
|
$lat = $this->request->lat;
|
||||||
|
$lon = $this->request->lon;
|
||||||
|
$url = "https://nominatim.haid.in/reverse?lat=$lat&lon=$lon&format=json";
|
||||||
|
$data = json_decode(file_get_contents($url), true);
|
||||||
|
self::returnJson($data);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
//TODO: export this to an api class for openstreetmap
|
//TODO: export this to an api class for openstreetmap
|
||||||
protected function getDistanceAction() {
|
protected function getDistanceAction() {
|
||||||
@@ -413,7 +442,7 @@ class WarehouseShippingNoteController extends TTCrud {
|
|||||||
|
|
||||||
$curl = curl_init();
|
$curl = curl_init();
|
||||||
curl_setopt_array($curl, [
|
curl_setopt_array($curl, [
|
||||||
CURLOPT_URL => "https://nominatim.openstreetmap.org/search?q=$address&format=json",
|
CURLOPT_URL => "https://nominatim.haid.in/search?q=$address&format=json",
|
||||||
CURLOPT_RETURNTRANSFER => true,
|
CURLOPT_RETURNTRANSFER => true,
|
||||||
CURLOPT_FOLLOWLOCATION => true,
|
CURLOPT_FOLLOWLOCATION => true,
|
||||||
CURLOPT_ENCODING => "",
|
CURLOPT_ENCODING => "",
|
||||||
|
|||||||
@@ -9,7 +9,7 @@ class WarehouseShippingNoteModel extends TTCrudBaseModel {
|
|||||||
public string $deliveryAddressCity;
|
public string $deliveryAddressCity;
|
||||||
public string $deliveryAddressEMail;
|
public string $deliveryAddressEMail;
|
||||||
public string $note;
|
public string $note;
|
||||||
public string $status; // 'new'|'accepted'|'invoiced'
|
public string $status; // 'new'|'in_progress'|'accepted'|'invoiced' TODO: add to enum / migration
|
||||||
public string $positions;
|
public string $positions;
|
||||||
public string $textElements;
|
public string $textElements;
|
||||||
public string $hoursEntries;
|
public string $hoursEntries;
|
||||||
|
|||||||
@@ -0,0 +1,26 @@
|
|||||||
|
<?php /** @noinspection ALL */
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
|
use Phinx\Migration\AbstractMigration;
|
||||||
|
|
||||||
|
final class WarehouseModify3 extends AbstractMigration {
|
||||||
|
public function up(): void {
|
||||||
|
if ($this->getEnvironment() == "thetool") {
|
||||||
|
$WarehouseShippingNote = $this->table("WarehouseShippingNote", ["signed" => true]);
|
||||||
|
$WarehouseShippingNote->changeColumn("status", "enum", ["values" => ["new", "accepted", "invoiced", "in_progress"], "null" => false]);
|
||||||
|
$WarehouseShippingNote->save();
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($this->getEnvironment() == "addressdb") {
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public function down(): void {
|
||||||
|
if ($this->getEnvironment() == "thetool") {
|
||||||
|
$WarehouseShippingNote = $this->table("WarehouseShippingNote");
|
||||||
|
$WarehouseShippingNote->changeColumn("status", "enum", ["values" => ["new", "accepted", "invoiced"], "null" => false]);
|
||||||
|
$WarehouseShippingNote->save();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,27 @@
|
|||||||
|
<?php /** @noinspection ALL */
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
|
use Phinx\Migration\AbstractMigration;
|
||||||
|
|
||||||
|
final class WarehouseModify4 extends AbstractMigration {
|
||||||
|
public function up(): void {
|
||||||
|
if ($this->getEnvironment() == "thetool") {
|
||||||
|
$WarehouseShippingNote = $this->table("WarehouseShippingNote", ["signed" => true]);
|
||||||
|
// change column hoursEntries to text without limit
|
||||||
|
$WarehouseShippingNote->changeColumn("hoursEntries", "text", ["null" => true]);
|
||||||
|
$WarehouseShippingNote->save();
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($this->getEnvironment() == "addressdb") {
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public function down(): void {
|
||||||
|
if ($this->getEnvironment() == "thetool") {
|
||||||
|
$WarehouseShippingNote = $this->table("WarehouseShippingNote");
|
||||||
|
$WarehouseShippingNote->changeColumn("hoursEntries", "text", ["limit" => 255, "null" => true]);
|
||||||
|
$WarehouseShippingNote->save();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -28,6 +28,10 @@ class Helper {
|
|||||||
} else if (!empty($filterValue)) {
|
} else if (!empty($filterValue)) {
|
||||||
if ($exactMatch) {
|
if ($exactMatch) {
|
||||||
$sql .= " AND `$columnName` = '" . $filterValue . "'";
|
$sql .= " AND `$columnName` = '" . $filterValue . "'";
|
||||||
|
} else if ($filterValue[0] === "%") {
|
||||||
|
$sql .= " AND `$columnName` LIKE '" . $filterValue . "'";
|
||||||
|
} else if ($filterValue[strlen($filterValue) - 1] === "%") {
|
||||||
|
$sql .= " AND `$columnName` LIKE '" . $filterValue . "'";
|
||||||
} else {
|
} else {
|
||||||
$filterItems = explode(" ", $filterValue);
|
$filterItems = explode(" ", $filterValue);
|
||||||
foreach ($filterItems as $item) {
|
foreach ($filterItems as $item) {
|
||||||
|
|||||||
+13
-1
@@ -124,6 +124,10 @@ class TTCrud extends mfBaseController {
|
|||||||
$page = $this->postData['pagination']['page'] ?? 1;
|
$page = $this->postData['pagination']['page'] ?? 1;
|
||||||
$perPage = $this->postData['pagination']['per_page'] ?? 10;
|
$perPage = $this->postData['pagination']['per_page'] ?? 10;
|
||||||
|
|
||||||
|
if ($order['key'] === null && isset($this->defaultOrder)) {
|
||||||
|
$order = $this->defaultOrder;
|
||||||
|
}
|
||||||
|
|
||||||
$rows = $this->model::getAll($filter, $perPage, ($page - 1) * $perPage, $order);
|
$rows = $this->model::getAll($filter, $perPage, ($page - 1) * $perPage, $order);
|
||||||
$filteredAvailable = $this->model::count($filter);
|
$filteredAvailable = $this->model::count($filter);
|
||||||
$totalRows = $this->model::count();
|
$totalRows = $this->model::count();
|
||||||
@@ -240,11 +244,19 @@ class TTCrud extends mfBaseController {
|
|||||||
|
|
||||||
if (strlen($searchedID) > 0) {
|
if (strlen($searchedID) > 0) {
|
||||||
$filter = ['id' => $searchedID];
|
$filter = ['id' => $searchedID];
|
||||||
|
$data = $this->model::getAll($filter, 10);
|
||||||
} else {
|
} else {
|
||||||
|
$filter = [$textKey => $this->request->q . '%'];
|
||||||
|
$data = $this->model::getAll($filter, 10);
|
||||||
|
if (count($data) < 11) {
|
||||||
$filter = [$textKey => $this->request->q];
|
$filter = [$textKey => $this->request->q];
|
||||||
|
$lazyData = $this->model::getAll($filter, 10);
|
||||||
|
$data = array_merge($data, $lazyData);
|
||||||
|
$data = array_unique($data, SORT_REGULAR);
|
||||||
|
$data = array_slice($data, 0, 10);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
$data = $this->model::getAll($filter, 10);
|
|
||||||
|
|
||||||
self::returnJson(array_map(function ($item) use ($textKey) {
|
self::returnJson(array_map(function ($item) use ($textKey) {
|
||||||
return ['value' => $item->id, 'text' => $item->$textKey];
|
return ['value' => $item->id, 'text' => $item->$textKey];
|
||||||
|
|||||||
@@ -46,13 +46,13 @@ class Zabbix {
|
|||||||
return $response['result'];
|
return $response['result'];
|
||||||
}
|
}
|
||||||
|
|
||||||
public function getItemValues($itemIds) {
|
public function getItemValues($itemIds, $limit = 15) {
|
||||||
$response = $this->zabbixRequest('history.get', array(
|
$response = $this->zabbixRequest('history.get', array(
|
||||||
'itemids' => $itemIds,
|
'itemids' => $itemIds,
|
||||||
'output' => 'extend',
|
'output' => 'extend',
|
||||||
'sortfield' => 'clock',
|
'sortfield' => 'clock',
|
||||||
'sortorder' => 'DESC',
|
'sortorder' => 'DESC',
|
||||||
'limit' => 15
|
'limit' => $limit
|
||||||
));
|
));
|
||||||
return $response['result'];
|
return $response['result'];
|
||||||
}
|
}
|
||||||
@@ -64,10 +64,13 @@ class Zabbix {
|
|||||||
return $response['result'];
|
return $response['result'];
|
||||||
}
|
}
|
||||||
|
|
||||||
public function getInterfaceItems($hostId, $interfaceName) {
|
public function getHostInterfaceItems($hostId) {
|
||||||
$response = $this->zabbixRequest('item.get', array(
|
$response = $this->zabbixRequest('item.get', array(
|
||||||
'hostids' => $hostId,
|
'hostids' => $hostId,
|
||||||
'search' => array('name' => array($interfaceName, "Bits"))
|
'output' => ['itemid','name_resolved', 'key_', 'units'],
|
||||||
|
'search' => ['name' => ["Bits received", "Bits sent"]],
|
||||||
|
'searchByAny' => true,
|
||||||
|
'sortfield' => 'name'
|
||||||
));
|
));
|
||||||
return $response['result'];
|
return $response['result'];
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -85,6 +85,7 @@ document.addEventListener('DOMContentLoaded', function () {
|
|||||||
var rrule = null;
|
var rrule = null;
|
||||||
var duration = null;
|
var duration = null;
|
||||||
var rruleflag = false;
|
var rruleflag = false;
|
||||||
|
let allDAy;
|
||||||
$.each($('.calendar-check'), function (index, value) {
|
$.each($('.calendar-check'), function (index, value) {
|
||||||
if ($(this).prop('checked')) {
|
if ($(this).prop('checked')) {
|
||||||
rights = true;
|
rights = true;
|
||||||
@@ -102,16 +103,12 @@ document.addEventListener('DOMContentLoaded', function () {
|
|||||||
|
|
||||||
$.each(json.data, function (index, value) {
|
$.each(json.data, function (index, value) {
|
||||||
if (!value.timerecording.timerecording) {
|
if (!value.timerecording.timerecording) {
|
||||||
|
allDAy = false;
|
||||||
rrule = null;
|
rrule = null;
|
||||||
duration = null;
|
duration = null;
|
||||||
rruleflag = false;
|
rruleflag = false;
|
||||||
category = value.ccategory.ccategory;
|
category = value.ccategory.ccategory;
|
||||||
if (value.rrule.rrule) {
|
|
||||||
rrule = value.rrule.rrule;
|
|
||||||
duration = value.duration.duration;
|
|
||||||
rruleflag = true;
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
if (value.calendar_id.calendar_id in calendarRights) {
|
if (value.calendar_id.calendar_id in calendarRights) {
|
||||||
if (calendarRights[value.calendar_id.calendar_id] == 'all') {
|
if (calendarRights[value.calendar_id.calendar_id] == 'all') {
|
||||||
@@ -124,13 +121,28 @@ document.addEventListener('DOMContentLoaded', function () {
|
|||||||
} else {
|
} else {
|
||||||
movable = false;
|
movable = false;
|
||||||
}
|
}
|
||||||
|
if (value.allDay.allDay == "1") {
|
||||||
|
allDAy = true;
|
||||||
|
}
|
||||||
|
if (value.rrule.rrule) {
|
||||||
|
$.each(value.rrule_events.rrule_events, function (index, rrule_event) {
|
||||||
|
rruleflag = true;
|
||||||
|
let busy;
|
||||||
|
if (rrule_event.showAs == 'busy') {
|
||||||
|
busy = "1";
|
||||||
|
} else if (rrule_event.showAs == 'tentative') {
|
||||||
|
busy = '2';
|
||||||
|
} else if (rrule_event.showAs == 'free') {
|
||||||
|
busy = '0';
|
||||||
|
}
|
||||||
let event = {
|
let event = {
|
||||||
id: value.id.id,
|
id: value.id.id,
|
||||||
start: value.cstart.cstart,
|
start: rrule_event.start,
|
||||||
end: value.cend.cend,
|
end: rrule_event.end,
|
||||||
title: category,
|
title: rrule_event.subject,
|
||||||
description: value.description.description,
|
description: value.description.description,
|
||||||
location: value.location.location,
|
location: value.location.location,
|
||||||
|
allDay: allDAy,
|
||||||
attachment: value.attachment.attachment,
|
attachment: value.attachment.attachment,
|
||||||
attachments: value.attachments.attachments,
|
attachments: value.attachments.attachments,
|
||||||
calendar_id: value.calendar_id,
|
calendar_id: value.calendar_id,
|
||||||
@@ -140,7 +152,43 @@ document.addEventListener('DOMContentLoaded', function () {
|
|||||||
backgroundColor: value.bgColor.bgColor,
|
backgroundColor: value.bgColor.bgColor,
|
||||||
editable: rights,
|
editable: rights,
|
||||||
rruleflag: rruleflag,
|
rruleflag: rruleflag,
|
||||||
rrule: rrule,
|
dates: rrule,
|
||||||
|
duration: duration,
|
||||||
|
droppable: movable,
|
||||||
|
startEditable: movable,
|
||||||
|
durationEditable: movable,
|
||||||
|
resizableFromStart: movable,
|
||||||
|
resourceId: value.calendar_id.calendar_id,
|
||||||
|
calendar_name: value.calendar_name.calendar_name,
|
||||||
|
clickable: rights,
|
||||||
|
mtime: value.mtime.mtime,
|
||||||
|
mname: value.mname.mname,
|
||||||
|
ctime: value.ctime.ctime,
|
||||||
|
cname: value.cname.cname,
|
||||||
|
busy: busy
|
||||||
|
};
|
||||||
|
userevents.push(event);
|
||||||
|
});
|
||||||
|
|
||||||
|
} else {
|
||||||
|
let event = {
|
||||||
|
id: value.id.id,
|
||||||
|
start: value.cstart.cstart,
|
||||||
|
end: value.cend.cend,
|
||||||
|
title: category,
|
||||||
|
description: value.description.description,
|
||||||
|
location: value.location.location,
|
||||||
|
allDay: allDAy,
|
||||||
|
attachment: value.attachment.attachment,
|
||||||
|
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],
|
||||||
|
textColor: value.txtColor.txtColor,
|
||||||
|
backgroundColor: value.bgColor.bgColor,
|
||||||
|
editable: rights,
|
||||||
|
rruleflag: rruleflag,
|
||||||
|
dates: rrule,
|
||||||
duration: duration,
|
duration: duration,
|
||||||
droppable: movable,
|
droppable: movable,
|
||||||
startEditable: movable,
|
startEditable: movable,
|
||||||
@@ -156,6 +204,7 @@ document.addEventListener('DOMContentLoaded', function () {
|
|||||||
busy: value.busy.busy
|
busy: value.busy.busy
|
||||||
};
|
};
|
||||||
userevents.push(event);
|
userevents.push(event);
|
||||||
|
}
|
||||||
if (value.rrule.rrule) {
|
if (value.rrule.rrule) {
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
@@ -1767,6 +1816,7 @@ Xinon GMbH`;
|
|||||||
var rrule = null;
|
var rrule = null;
|
||||||
var duration = null;
|
var duration = null;
|
||||||
var rruleflag = false;
|
var rruleflag = false;
|
||||||
|
let allDAy = false;
|
||||||
$.each($('.calendar-check'), function (index, value) {
|
$.each($('.calendar-check'), function (index, value) {
|
||||||
if ($(this).prop('checked')) {
|
if ($(this).prop('checked')) {
|
||||||
rights = true;
|
rights = true;
|
||||||
@@ -1787,6 +1837,7 @@ Xinon GMbH`;
|
|||||||
rrule = null;
|
rrule = null;
|
||||||
duration = null;
|
duration = null;
|
||||||
rruleflag = false;
|
rruleflag = false;
|
||||||
|
allDAy = false;
|
||||||
category = value.ccategory.ccategory;
|
category = value.ccategory.ccategory;
|
||||||
if (value.rrule.rrule) {
|
if (value.rrule.rrule) {
|
||||||
rrule = value.rrule.rrule;
|
rrule = value.rrule.rrule;
|
||||||
@@ -1805,13 +1856,28 @@ Xinon GMbH`;
|
|||||||
} else {
|
} else {
|
||||||
movable = false;
|
movable = false;
|
||||||
}
|
}
|
||||||
userevents.push({
|
if (value.allDay.allDay == "1") {
|
||||||
|
allDAy = true;
|
||||||
|
}
|
||||||
|
if (value.rrule.rrule) {
|
||||||
|
$.each(value.rrule_events.rrule_events, function (index, rrule_event) {
|
||||||
|
rruleflag = true;
|
||||||
|
let busy;
|
||||||
|
if (rrule_event.showAs == 'busy') {
|
||||||
|
busy = "1";
|
||||||
|
} else if (rrule_event.showAs == 'tentative') {
|
||||||
|
busy = '2';
|
||||||
|
} else if (rrule_event.showAs == 'free') {
|
||||||
|
busy = '0';
|
||||||
|
}
|
||||||
|
let event = {
|
||||||
id: value.id.id,
|
id: value.id.id,
|
||||||
start: value.cstart.cstart,
|
start: rrule_event.start,
|
||||||
end: value.cend.cend,
|
end: rrule_event.end,
|
||||||
title: category,
|
title: rrule_event.subject,
|
||||||
description: value.description.description,
|
description: value.description.description,
|
||||||
location: value.location.location,
|
location: value.location.location,
|
||||||
|
allDay: allDAy,
|
||||||
attachment: value.attachment.attachment,
|
attachment: value.attachment.attachment,
|
||||||
attachments: value.attachments.attachments,
|
attachments: value.attachments.attachments,
|
||||||
calendar_id: value.calendar_id,
|
calendar_id: value.calendar_id,
|
||||||
@@ -1819,10 +1885,50 @@ Xinon GMbH`;
|
|||||||
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],
|
||||||
textColor: value.txtColor.txtColor,
|
textColor: value.txtColor.txtColor,
|
||||||
backgroundColor: value.bgColor.bgColor,
|
backgroundColor: value.bgColor.bgColor,
|
||||||
editable: movable,
|
editable: rights,
|
||||||
rruleflag: rruleflag,
|
rruleflag: rruleflag,
|
||||||
rrule: rrule,
|
dates: rrule,
|
||||||
duration: duration,
|
duration: duration,
|
||||||
|
droppable: movable,
|
||||||
|
startEditable: movable,
|
||||||
|
durationEditable: movable,
|
||||||
|
resizableFromStart: movable,
|
||||||
|
resourceId: value.calendar_id.calendar_id,
|
||||||
|
calendar_name: value.calendar_name.calendar_name,
|
||||||
|
clickable: rights,
|
||||||
|
mtime: value.mtime.mtime,
|
||||||
|
mname: value.mname.mname,
|
||||||
|
ctime: value.ctime.ctime,
|
||||||
|
cname: value.cname.cname,
|
||||||
|
busy: busy
|
||||||
|
};
|
||||||
|
userevents.push(event);
|
||||||
|
});
|
||||||
|
|
||||||
|
} else {
|
||||||
|
let event = {
|
||||||
|
id: value.id.id,
|
||||||
|
start: value.cstart.cstart,
|
||||||
|
end: value.cend.cend,
|
||||||
|
title: category,
|
||||||
|
description: value.description.description,
|
||||||
|
location: value.location.location,
|
||||||
|
allDay: allDAy,
|
||||||
|
attachment: value.attachment.attachment,
|
||||||
|
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],
|
||||||
|
textColor: value.txtColor.txtColor,
|
||||||
|
backgroundColor: value.bgColor.bgColor,
|
||||||
|
editable: rights,
|
||||||
|
rruleflag: rruleflag,
|
||||||
|
dates: rrule,
|
||||||
|
duration: duration,
|
||||||
|
droppable: movable,
|
||||||
|
startEditable: movable,
|
||||||
|
durationEditable: movable,
|
||||||
|
resizableFromStart: movable,
|
||||||
resourceId: value.calendar_id.calendar_id,
|
resourceId: value.calendar_id.calendar_id,
|
||||||
calendar_name: value.calendar_name.calendar_name,
|
calendar_name: value.calendar_name.calendar_name,
|
||||||
clickable: rights,
|
clickable: rights,
|
||||||
@@ -1831,7 +1937,9 @@ Xinon GMbH`;
|
|||||||
ctime: value.ctime.ctime,
|
ctime: value.ctime.ctime,
|
||||||
cname: value.cname.cname,
|
cname: value.cname.cname,
|
||||||
busy: value.busy.busy
|
busy: value.busy.busy
|
||||||
});
|
};
|
||||||
|
userevents.push(event);
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
otherevents.push({
|
otherevents.push({
|
||||||
id: value.id.id,
|
id: value.id.id,
|
||||||
|
|||||||
@@ -77,7 +77,7 @@ Vue.component('DeviceTable', {
|
|||||||
|
|
||||||
<template v-if="row.zabbix_host_id !== '0' && row.zabbix_host_id !== null">
|
<template v-if="row.zabbix_host_id !== '0' && row.zabbix_host_id !== null">
|
||||||
<a :href="window['TT_CONFIG']['ZABBIX_URL'] + '/zabbix.php?action=latest.view&hostids%5B%5D=' + row.zabbix_host_id" target="_blank" class="text-info" title="Zabbix"><i class="fas fa-server"></i></a>
|
<a :href="window['TT_CONFIG']['ZABBIX_URL'] + '/zabbix.php?action=latest.view&hostids%5B%5D=' + row.zabbix_host_id" target="_blank" class="text-info" title="Zabbix"><i class="fas fa-server"></i></a>
|
||||||
<a :href="window['TT_CONFIG']['GRAFANA_URL'] + '/d/Ta3PtRWZk/mikrotik-dashboard?orgId=1&var-host=' + row.name" target="_blank" class="text-info" title="Grafana"><i class="fas fa-chart-line"></i></a>
|
<a :href="window['TT_CONFIG']['BASE_URL'] + '/Graphing?id=' + row.zabbix_host_id + '&hostname=' + row.name" target="_blank" class="text-info" title="Graphen"><i class="fas fa-chart-line"></i></a>
|
||||||
</template>
|
</template>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,133 @@
|
|||||||
|
Vue.component('tt-graph', {
|
||||||
|
template: `
|
||||||
|
<!-- use chart js with datasets etc everything needed for chart.js here to be usable width and height aswell -->
|
||||||
|
<tt-card>
|
||||||
|
<template v-slot:header>
|
||||||
|
<h3 style="text-align: center;user-select: none">{{ header }}</h3>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<div ref="container">
|
||||||
|
<canvas ref="chart" :style="{width: width + 'px', height: height + 'px'}"></canvas></div>
|
||||||
|
</tt-card>
|
||||||
|
`,
|
||||||
|
props: ['data', 'labels', 'header'],
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
chart: null,
|
||||||
|
width: 400,
|
||||||
|
height: 220
|
||||||
|
}
|
||||||
|
},
|
||||||
|
mounted() {
|
||||||
|
const ctx = this.$refs.chart.getContext('2d');
|
||||||
|
this.chart = new Chart(ctx, {
|
||||||
|
type: 'line',
|
||||||
|
data: {
|
||||||
|
labels: this.labels,
|
||||||
|
datasets: this.data
|
||||||
|
},
|
||||||
|
options: {
|
||||||
|
scales: {
|
||||||
|
y: {
|
||||||
|
beginAtZero: true
|
||||||
|
},
|
||||||
|
x: {
|
||||||
|
type: 'time',
|
||||||
|
time: {
|
||||||
|
// time is epoch
|
||||||
|
parser: 'X',
|
||||||
|
// unit: 'hour',
|
||||||
|
displayFormats: {
|
||||||
|
minute: 'DD.M. HH:mm',
|
||||||
|
}
|
||||||
|
},
|
||||||
|
// min: '00:00:00',
|
||||||
|
// max: '24:00:00',
|
||||||
|
ticks: {
|
||||||
|
autoSkipPadding: 25,
|
||||||
|
autoSkip: true,
|
||||||
|
maxRotation: 0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
responsive: true,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
// set width and height to the canvas element actual width and height
|
||||||
|
this.width = this.$refs.container.width;
|
||||||
|
this.height = this.$refs.container.height;
|
||||||
|
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
Vue.component('device-graphing', {
|
||||||
|
template: `
|
||||||
|
<div style="display: grid; grid-template-columns: 45vw 45vw; gap: 1rem;">
|
||||||
|
<h3 style="text-align: center;user-select: none;grid-column: 1 / span 2;">{{ hostname }}</h3>
|
||||||
|
|
||||||
|
<tt-loader v-if="graphs.length === 0"></tt-loader>
|
||||||
|
|
||||||
|
<template v-for="graph in graphs">
|
||||||
|
<tt-graph :data="graph.data" :labels="graph.labels" :header="graph.name"></tt-graph>
|
||||||
|
</template>
|
||||||
|
</div>
|
||||||
|
`,
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
graphs: [],
|
||||||
|
hostname: ''
|
||||||
|
}
|
||||||
|
},
|
||||||
|
async mounted() {
|
||||||
|
// get hostname from url params
|
||||||
|
this.hostname = new URLSearchParams(window.location.search).get('hostname');
|
||||||
|
console.log(this.hostname);
|
||||||
|
// get id from url params
|
||||||
|
const id = new URLSearchParams(window.location.search).get('id');
|
||||||
|
const response = await axios.get('/Graphing/data?id=' + id);
|
||||||
|
const data = response.data;
|
||||||
|
|
||||||
|
const graphs = {};
|
||||||
|
|
||||||
|
for (const item in data) {
|
||||||
|
// Create graphs.[item.name.split(':')[0]] if it doesn't exist
|
||||||
|
if (!graphs[data[item].name.split(':')[0]]) {
|
||||||
|
graphs[data[item].name.split(':')[0]] = {
|
||||||
|
name: data[item].name.split(':')[0],
|
||||||
|
data: [],
|
||||||
|
labels: []
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Add the data to the graph but check if it's received or sent and already exists
|
||||||
|
|
||||||
|
if (data[item].name.split(':')[1].includes('received') && graphs[data[item].name.split(':')[0]].data.find(data => data.label.includes('received'))) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (data[item].name.split(':')[1].includes('sent') && graphs[data[item].name.split(':')[0]].data.find(data => data.label.includes('sent'))) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
graphs[data[item].name.split(':')[0]].data.push({
|
||||||
|
label: data[item].name.split(':')[1],
|
||||||
|
data: data[item].values.map(value => value.value),
|
||||||
|
fill: false,
|
||||||
|
borderColor: data[item].name.includes('received') ? 'rgb(75, 192, 192)' : 'rgb(192, 75, 75)',
|
||||||
|
tension: 0.1
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// Add the labels to the this.graphs
|
||||||
|
for (const graph in graphs) {
|
||||||
|
graphs[graph].labels = data[Object.keys(data).find(key => data[key].name.includes(graph))].values.map(value => value.clock);
|
||||||
|
this.graphs.push(graphs[graph]);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
});
|
||||||
@@ -246,6 +246,7 @@ Vue.component('warehouse-article-price-modal', {
|
|||||||
})
|
})
|
||||||
|
|
||||||
|
|
||||||
|
// noinspection EqualityComparisonWithCoercionJS
|
||||||
Vue.component('warehouse-article', {
|
Vue.component('warehouse-article', {
|
||||||
//language=Vue
|
//language=Vue
|
||||||
template: `
|
template: `
|
||||||
@@ -261,7 +262,7 @@ Vue.component('warehouse-article', {
|
|||||||
|
|
||||||
<template v-slot:cheapestsellprice="{ row }">
|
<template v-slot:cheapestsellprice="{ row }">
|
||||||
<template v-for="price in JSON.parse(row.cheapestSellPrice)">
|
<template v-for="price in JSON.parse(row.cheapestSellPrice)">
|
||||||
<span v-if="price && window.TT_CONFIG['WAREHOUSE_ADMIN'] === true">{{price.title}}: {{(price.price)}} €<br></span>
|
<span v-if="price && window.TT_CONFIG['WAREHOUSE_ADMIN'] == true">{{price.title}}: {{(price.price)}} €<br></span>
|
||||||
<span v-if="price && price.title === 'Verkauf'">{{(price.price)}} €</span>
|
<span v-if="price && price.title === 'Verkauf'">{{(price.price)}} €</span>
|
||||||
</template>
|
</template>
|
||||||
</template>
|
</template>
|
||||||
|
|||||||
@@ -36,6 +36,10 @@
|
|||||||
grid-gap: 10px;
|
grid-gap: 10px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.warehouse-shipping-note-modal-positions-entry-actions, .warehouse-shipping-note-modal-hours-entry-actions {
|
||||||
|
grid-column: 2;
|
||||||
|
}
|
||||||
|
|
||||||
.signModal > div {
|
.signModal > div {
|
||||||
margin: 0;
|
margin: 0;
|
||||||
width: 100vw;
|
width: 100vw;
|
||||||
|
|||||||
@@ -57,7 +57,7 @@ Vue.component('warehouse-shipping-note-modal-hours-entry', {
|
|||||||
<tt-input v-model="hourCount" label="Stunden" sm/>
|
<tt-input v-model="hourCount" label="Stunden" sm/>
|
||||||
<tt-autocomplete v-model="carId" :api-url="carApiUrl" label="Fahrzeug" sm/>
|
<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 v-model="hourlyPrice" label="Stundenlohn" type="number" sm v-if="showHourlyPrice"/>
|
||||||
<tt-input v-model="kilometerCount" label="Kilometer" sm/>
|
<tt-input :disabled="carId === ''" v-model="kilometerCount" label="Kilometer" sm/>
|
||||||
<div class="warehouse-shipping-note-modal-hours-entry-actions">
|
<div class="warehouse-shipping-note-modal-hours-entry-actions">
|
||||||
<button @click="createOrUpdate" class="btn btn-sm btn-primary">Speichern</button>
|
<button @click="createOrUpdate" class="btn btn-sm btn-primary">Speichern</button>
|
||||||
</div>
|
</div>
|
||||||
@@ -87,6 +87,10 @@ Vue.component('warehouse-shipping-note-modal-hours-entry', {
|
|||||||
this.updateCarId().then();
|
this.updateCarId().then();
|
||||||
},
|
},
|
||||||
async updateKilometerCount() {
|
async updateKilometerCount() {
|
||||||
|
if (!this.carId) {
|
||||||
|
this.kilometerCount = '';
|
||||||
|
return;
|
||||||
|
}
|
||||||
const delAddr = this.$parent.$parent.$parent.delAddrLine +
|
const delAddr = this.$parent.$parent.$parent.delAddrLine +
|
||||||
' ' +
|
' ' +
|
||||||
this.$parent.$parent.$parent.delAddrCity +
|
this.$parent.$parent.$parent.delAddrCity +
|
||||||
@@ -96,9 +100,11 @@ Vue.component('warehouse-shipping-note-modal-hours-entry', {
|
|||||||
this.kilometerCount = response.data.distance
|
this.kilometerCount = response.data.distance
|
||||||
},
|
},
|
||||||
async updateCarId() {
|
async updateCarId() {
|
||||||
|
if (!this.userId || this.carId) return;
|
||||||
const response = await axios.get(window.TT_CONFIG["BASE_PATH"] + '/WarehouseShippingNote/timerecordingCarForUser?userId=' + this.userId);
|
const response = await axios.get(window.TT_CONFIG["BASE_PATH"] + '/WarehouseShippingNote/timerecordingCarForUser?userId=' + this.userId);
|
||||||
if (response.data.status === 'USER_NO_CAR') {
|
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;
|
return;
|
||||||
}
|
}
|
||||||
this.carId = response.data.id;
|
this.carId = response.data.id;
|
||||||
@@ -114,12 +120,14 @@ Vue.component('warehouse-shipping-note-modal-hours-entry', {
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
async mounted() {
|
async mounted() {
|
||||||
if (!this.carId) this.updateCarId().then();
|
|
||||||
if (!this.userId) this.userId = this.window.TT_CONFIG['USER_ID'];
|
if (!this.userId) this.userId = this.window.TT_CONFIG['USER_ID'];
|
||||||
|
if (!this.carId) this.updateCarId().then();
|
||||||
if (!this.date) this.updateDate();
|
if (!this.date) this.updateDate();
|
||||||
if (!this.kilometerCount) this.updateKilometerCount().then();
|
if (!this.kilometerCount) this.updateKilometerCount().then();
|
||||||
|
|
||||||
this.$parent.$parent.$parent.$watch('delAddrLine', this.updateKilometerCount);
|
this.$parent.$parent.$parent.$watch('delAddrLine', this.updateKilometerCount);
|
||||||
|
this.$watch('carId', this.updateKilometerCount);
|
||||||
|
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
@@ -270,10 +278,9 @@ Vue.component('warehouse-shipping-note-modal-positions-entry', {
|
|||||||
// TODO: if articlePacket is needed we need to implement this
|
// TODO: if articlePacket is needed we need to implement this
|
||||||
async createOrUpdate() {
|
async createOrUpdate() {
|
||||||
if (!this.amount) return this.window.notify('error', 'Bitte füllen sie die Menge aus');
|
if (!this.amount) return this.window.notify('error', 'Bitte füllen sie die Menge aus');
|
||||||
if (!this.price) return this.window.notify('error', 'Bitte füllen sie den Preis aus');
|
|
||||||
const data = {
|
const data = {
|
||||||
amount: this.amount,
|
amount: this.amount,
|
||||||
price: parseFloat(this.price)
|
price: parseFloat(this.price) ?? ''
|
||||||
}
|
}
|
||||||
if (!this.articleId && this.$refs.article.displayValue) {
|
if (!this.articleId && this.$refs.article.displayValue) {
|
||||||
data.articleText = this.$refs.article.displayValue;
|
data.articleText = this.$refs.article.displayValue;
|
||||||
@@ -331,7 +338,9 @@ Vue.component('warehouse-shipping-note-modal-positions-view', {
|
|||||||
<td colspan="4" class="text-center">Keine Einträge</td>
|
<td colspan="4" class="text-center">Keine Einträge</td>
|
||||||
</tr>
|
</tr>
|
||||||
<tr v-for="position in positions">
|
<tr v-for="position in positions">
|
||||||
<td>{{ position.article ? articleNames[position.article] : position.articlePacket ? articlePacketNames[position.articlePacket] : position.articleText }}</td>
|
<td>{{ position.article ? articleNames[position.article] : position.articlePacket ? articlePacketNames[position.articlePacket] :
|
||||||
|
position.articleText }}
|
||||||
|
</td>
|
||||||
<td>{{ position.amount }}</td>
|
<td>{{ position.amount }}</td>
|
||||||
<td>{{ (position.price?.toFixed(2)) }} €</td>
|
<td>{{ (position.price?.toFixed(2)) }} €</td>
|
||||||
<td>
|
<td>
|
||||||
@@ -425,6 +434,7 @@ Vue.component('warehouse-shipping-note-modal-positions', {
|
|||||||
})
|
})
|
||||||
|
|
||||||
|
|
||||||
|
// noinspection EqualityComparisonWithCoercionJS
|
||||||
Vue.component('warehouse-shipping-note-modal', {
|
Vue.component('warehouse-shipping-note-modal', {
|
||||||
props: {
|
props: {
|
||||||
id: {type: [String, Number], required: true},
|
id: {type: [String, Number], required: true},
|
||||||
@@ -434,7 +444,7 @@ Vue.component('warehouse-shipping-note-modal', {
|
|||||||
data() {
|
data() {
|
||||||
return {
|
return {
|
||||||
window: window,
|
window: window,
|
||||||
billAddrAutoCompleteUrl: window.TT_CONFIG['BASE_PATH'] + '/Address/Api?do=findAddress',
|
billAddrAutoCompleteUrl: window.TT_CONFIG['BASE_PATH'] + '/Address/Api?do=findAddress&fibu_primary_account=1',
|
||||||
billAddrId: '',
|
billAddrId: '',
|
||||||
delAddrName: '',
|
delAddrName: '',
|
||||||
delAddrLine: '',
|
delAddrLine: '',
|
||||||
@@ -451,7 +461,7 @@ Vue.component('warehouse-shipping-note-modal', {
|
|||||||
|
|
||||||
//language=Vue
|
//language=Vue
|
||||||
template: `
|
template: `
|
||||||
<tt-modal :show="true" @submit="submit" :delete="false" :title="title" @update:show="$emit('close')">
|
<tt-modal :show="true" @submit="submit" @delete="reqDelete" :delete="id !== 'create'" :title="title" @update:show="$emit('close')">
|
||||||
<div style="width: 99%">
|
<div style="width: 99%">
|
||||||
<h4 class="text-center">Liefer- und Rechnungsadresse</h4>
|
<h4 class="text-center">Liefer- und Rechnungsadresse</h4>
|
||||||
<tt-autocomplete v-model="billAddrId" :api-url="billAddrAutoCompleteUrl" label="Rechnungsadresse" sm row/>
|
<tt-autocomplete v-model="billAddrId" :api-url="billAddrAutoCompleteUrl" label="Rechnungsadresse" sm row/>
|
||||||
@@ -460,14 +470,16 @@ Vue.component('warehouse-shipping-note-modal', {
|
|||||||
:del-addr-e-mail.sync="delAddrEMail"/>
|
:del-addr-e-mail.sync="delAddrEMail"/>
|
||||||
|
|
||||||
|
|
||||||
<template v-if="billAddrId && delAddrName && delAddrLine && delAddrPLZ && delAddrCity">
|
<div v-show="delAddrFilled === true">
|
||||||
|
<template v-if="window.TT_CONFIG['WAREHOUSE_ADMIN'] == true && 1 < 0">
|
||||||
<hr>
|
<hr>
|
||||||
<h4 class="text-center">Textelemente</h4>
|
<h4 class="text-center">Textelemente</h4>
|
||||||
<warehouse-shipping-note-modal-text-elements :text-elements="textElements"/>
|
<warehouse-shipping-note-modal-text-elements :text-elements="textElements"/>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
|
||||||
<hr>
|
<hr>
|
||||||
<tt-textarea label="Einleitender Text" v-model="note" sm row/>
|
<tt-textarea label="Art der Arbeit" v-model="note" sm row/>
|
||||||
|
|
||||||
|
|
||||||
<hr>
|
<hr>
|
||||||
@@ -478,16 +490,23 @@ Vue.component('warehouse-shipping-note-modal', {
|
|||||||
<hr>
|
<hr>
|
||||||
<h4 class="text-center">Positionen</h4>
|
<h4 class="text-center">Positionen</h4>
|
||||||
<warehouse-shipping-note-modal-positions :positions.sync="positions" :bill-addr-id="billAddrId"/>
|
<warehouse-shipping-note-modal-positions :positions.sync="positions" :bill-addr-id="billAddrId"/>
|
||||||
</template>
|
</div>
|
||||||
|
|
||||||
<div v-else class="text-center">Bitte füllen Sie die Rechnungs- und Lieferadresse aus</div>
|
<div v-show="delAddrFilled === false" class="text-center">Bitte füllen Sie die Rechnungs- und Lieferadresse aus</div>
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
<!-- TODO: fix these buttons-->
|
<!-- TODO: fix these buttons-->
|
||||||
<template v-slot:footer-prepend v-if="id !== 'create'">
|
<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
|
||||||
|
Bearbeitung
|
||||||
|
</button>
|
||||||
|
<button v-if="window.TT_CONFIG['WAREHOUSE_ADMIN'] == true && (status === 'new' || status === 'in_progress')" class="btn btn-success"
|
||||||
|
@click="alert('Accepted')">Akzeptieren
|
||||||
|
</button>
|
||||||
|
<button v-if="window.TT_CONFIG['WAREHOUSE_ADMIN'] == true && status === 'accepted'" class="btn btn-info" @click="alert('Invoiced')">
|
||||||
|
Verrechnet
|
||||||
|
</button>
|
||||||
<button class="btn btn-info" @click="$emit('open-signing-modal', id)">Unterschreiben</button>
|
<button class="btn btn-info" @click="$emit('open-signing-modal', id)">Unterschreiben</button>
|
||||||
<!-- <button class="btn btn-success" @click="alert('Accept')">Akzeptieren</button>-->
|
|
||||||
<!-- <button class="btn btn-warning" @click="alert('Invoiced')">Verrechnet</button>-->
|
|
||||||
</template>
|
</template>
|
||||||
</tt-modal>
|
</tt-modal>
|
||||||
`,
|
`,
|
||||||
@@ -516,8 +535,14 @@ Vue.component('warehouse-shipping-note-modal', {
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
openSigningModal() {
|
async reqDelete() {
|
||||||
|
const response = await axios.post(window.TT_CONFIG['DELETE_URL'], {id: this.id});
|
||||||
|
if (response.data.success) {
|
||||||
|
this.window.notify('success', response.data.message || 'Erfolgreich gelöscht');
|
||||||
|
this.$emit('close');
|
||||||
|
} else {
|
||||||
|
this.window.notify('error', response.data.message || 'Ein Fehler ist aufgetreten');
|
||||||
|
}
|
||||||
},
|
},
|
||||||
async submit() {
|
async submit() {
|
||||||
const data = {
|
const data = {
|
||||||
@@ -553,13 +578,16 @@ Vue.component('warehouse-shipping-note-modal', {
|
|||||||
computed: {
|
computed: {
|
||||||
title() {
|
title() {
|
||||||
return this.id === 'create' ? 'Lieferschein erstellen' : `Lieferschein #${this.id} bearbeiten`;
|
return this.id === 'create' ? 'Lieferschein erstellen' : `Lieferschein #${this.id} bearbeiten`;
|
||||||
|
},
|
||||||
|
delAddrFilled() {
|
||||||
|
if (this.id !== 'create') return true;
|
||||||
|
return !!this.delAddrName && !!this.delAddrLine && !!this.delAddrPLZ && !!this.delAddrCity;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
})
|
})
|
||||||
|
|
||||||
Vue.component('warehouse-shipping-note-modal-address', {
|
Vue.component('warehouse-shipping-note-modal-address', {
|
||||||
// also add props for delAddrName, delAddrLine, delAddrPLZ, delAddrCity which we will sync with the parent component
|
|
||||||
props: {
|
props: {
|
||||||
billAddrId: {type: [String, Number], required: true},
|
billAddrId: {type: [String, Number], required: true},
|
||||||
delAddrName: {type: String, required: true},
|
delAddrName: {type: String, required: true},
|
||||||
@@ -578,6 +606,7 @@ Vue.component('warehouse-shipping-note-modal-address', {
|
|||||||
addresses: [],
|
addresses: [],
|
||||||
fetchedBillAddr: null,
|
fetchedBillAddr: null,
|
||||||
selectedAddr: '',
|
selectedAddr: '',
|
||||||
|
newAddrGeoLatLon: '',
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
//language=Vue
|
//language=Vue
|
||||||
@@ -590,11 +619,15 @@ Vue.component('warehouse-shipping-note-modal-address', {
|
|||||||
</template>
|
</template>
|
||||||
|
|
||||||
<template v-else-if="addressMode === 'new'">
|
<template v-else-if="addressMode === 'new'">
|
||||||
<tt-input v-model="delAddrName" label="Lieferadresse Name" sm row/>
|
<tt-input :value="delAddrName" @input="$emit('update:delAddrName', $event)" label="Lieferadresse Name*" sm row/>
|
||||||
<tt-input v-model="delAddrLine" label="Lieferadresse" sm row/>
|
<tt-input :value="delAddrEMail" @input="$emit('update:delAddrEMail', $event)" label="Lieferadresse E-Mail" sm row/>
|
||||||
<tt-input v-model="delAddrPLZ" label="Lieferadresse PLZ" sm row/>
|
<tt-autocomplete :api-url="window.TT_CONFIG['BASE_PATH'] + '/WarehouseShippingNote/geoAutocomplete'" @input="newAddrGeoLatLon = $event"
|
||||||
<tt-input v-model="delAddrCity" label="Lieferadresse Ort" sm row/>
|
label="Adresse*" sm row/>
|
||||||
<tt-input v-model="delAddrEMail" label="Lieferadresse E-Mail" sm row/>
|
|
||||||
|
<span v-if="delAddrLine && delAddrPLZ && delAddrCity">Adresse: {{ delAddrLine }}, {{ delAddrPLZ }} {{ delAddrCity }}</span>
|
||||||
|
<!-- <tt-input :value="delAddrLine" @input="$emit('update:delAddrLine', $event)" label="Lieferadresse" sm row/>-->
|
||||||
|
<!-- <tt-input :value="delAddrPLZ" @input="$emit('update:delAddrPLZ', $event)" label="Lieferadresse PLZ" sm row/>-->
|
||||||
|
<!-- <tt-input :value="delAddrCity" @input="$emit('update:delAddrCity', $event)" label="Lieferadresse Ort" sm row/>-->
|
||||||
</template>
|
</template>
|
||||||
</div>
|
</div>
|
||||||
`,
|
`,
|
||||||
@@ -602,24 +635,50 @@ Vue.component('warehouse-shipping-note-modal-address', {
|
|||||||
billAddrId: {handler: 'updateBillingMode', immediate: false},
|
billAddrId: {handler: 'updateBillingMode', immediate: false},
|
||||||
addressMode: {handler: 'fetchDeliveryAddresses', immediate: false},
|
addressMode: {handler: 'fetchDeliveryAddresses', immediate: false},
|
||||||
selectedAddr: {handler: 'setSelectedAddrValues', immediate: false},
|
selectedAddr: {handler: 'setSelectedAddrValues', immediate: false},
|
||||||
|
newAddrGeoLatLon: {handler: 'fetchGeoAddress', immediate: false},
|
||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
|
async fetchGeoAddress() {
|
||||||
|
if (!this.newAddrGeoLatLon) {
|
||||||
|
this.$emit('update:delAddrLine', '');
|
||||||
|
this.$emit('update:delAddrPLZ', '');
|
||||||
|
this.$emit('update:delAddrCity', '');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const [lat, lon] = this.newAddrGeoLatLon.split(',');
|
||||||
|
const response = await axios.get(window.TT_CONFIG["BASE_PATH"] + '/WarehouseShippingNote/geoReverse?lat=' + lat + '&lon=' + lon);
|
||||||
|
|
||||||
|
if (response.data.address.road) {
|
||||||
|
this.$emit('update:delAddrLine',
|
||||||
|
`${response.data.address.road}${response.data.address.house_number ? ' ' + response.data.address.house_number : ''}`);
|
||||||
|
} else if(response.data.address.village) {
|
||||||
|
this.$emit('update:delAddrLine',
|
||||||
|
`${response.data.address.village}${response.data.address.house_number ? ' ' + response.data.address.house_number : ''}`);
|
||||||
|
} else if(response.data.address.hamlet) {
|
||||||
|
this.$emit('update:delAddrLine',
|
||||||
|
`${response.data.address.hamlet}${response.data.address.house_number ? ' ' + response.data.address.house_number : ''}`);
|
||||||
|
} else if(response.data.address.residential) {
|
||||||
|
this.$emit('update:delAddrLine',
|
||||||
|
`${response.data.address.residential}${response.data.address.house_number ? ' ' + response.data.address.house_number : ''}`);
|
||||||
|
} else if(response.data.address.city) {
|
||||||
|
this.$emit('update:delAddrLine',
|
||||||
|
`${response.data.address.city}${response.data.address.house_number ? ' ' + response.data.address.house_number : ''}`);
|
||||||
|
}
|
||||||
|
|
||||||
|
this.$emit('update:delAddrPLZ', response.data.address.postcode);
|
||||||
|
this.$emit('update:delAddrCity', response.data.address.village || response.data.address.city || response.data.address.town);
|
||||||
|
},
|
||||||
async updateBillingMode() {
|
async updateBillingMode() {
|
||||||
await this.fetchDeliveryAddresses();
|
await this.fetchDeliveryAddresses();
|
||||||
// this.addressMode = 'billing';
|
|
||||||
|
|
||||||
console.log('updateBillingMode');
|
|
||||||
|
|
||||||
// Here we check if the address is already in the list of addresses, if not we will set the addressMode to billing and fetch the billing address
|
// Here we check if the address is already in the list of addresses, if not we will set the addressMode to billing and fetch the billing address
|
||||||
if (this.delAddrName && this.delAddrLine && this.delAddrPLZ && this.delAddrCity) {
|
if (this.delAddrName && this.delAddrLine && this.delAddrPLZ && this.delAddrCity) {
|
||||||
const foundAddress = this.addresses.find(address => address.deliveryAddressName ===
|
const foundAddress = this.addresses.find(address =>
|
||||||
this.delAddrName &&
|
address.deliveryAddressName === this.delAddrName &&
|
||||||
address.deliveryAddressLine ===
|
address.deliveryAddressLine === this.delAddrLine &&
|
||||||
this.delAddrLine &&
|
address.deliveryAddressPLZ === this.delAddrPLZ &&
|
||||||
address.deliveryAddressPLZ ===
|
address.deliveryAddressCity === this.delAddrCity &&
|
||||||
this.delAddrPLZ &&
|
address.deliveryAddressEMail === this.delAddrEMail);
|
||||||
address.deliveryAddressCity ===
|
|
||||||
this.delAddrCity && address.deliveryAddressEMail === this.delAddrEMail);
|
|
||||||
if (foundAddress) {
|
if (foundAddress) {
|
||||||
this.addressMode = 'existing';
|
this.addressMode = 'existing';
|
||||||
this.selectedAddr = foundAddress.id;
|
this.selectedAddr = foundAddress.id;
|
||||||
@@ -631,11 +690,21 @@ Vue.component('warehouse-shipping-note-modal-address', {
|
|||||||
await this.fetchBillingAddress();
|
await this.fetchBillingAddress();
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
async fetchDeliveryAddresses() {
|
async fetchDeliveryAddresses(newVal, oldVal) {
|
||||||
|
if ((oldVal === 'billing' || oldVal === 'existing') && newVal === 'new') {
|
||||||
|
this.$emit('update:delAddrName', '');
|
||||||
|
this.$emit('update:delAddrLine', '');
|
||||||
|
this.$emit('update:delAddrPLZ', '');
|
||||||
|
this.$emit('update:delAddrCity', '');
|
||||||
|
this.$emit('update:delAddrEMail', '');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
if (this.addressMode === 'billing' && this.billAddrId) {
|
if (this.addressMode === 'billing' && this.billAddrId) {
|
||||||
await this.fetchBillingAddress();
|
await this.fetchBillingAddress();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!this.billAddrId || this.addressMode !== 'existing' || this.fetchedBillAddr === this.billAddrId) return;
|
if (!this.billAddrId || this.addressMode !== 'existing' || this.fetchedBillAddr === this.billAddrId) return;
|
||||||
|
|
||||||
const response = await axios.get(window.TT_CONFIG["BASE_PATH"] + '/WarehouseShippingNote/getDeliveryAddresses?billingAddressId=' + this.billAddrId);
|
const response = await axios.get(window.TT_CONFIG["BASE_PATH"] + '/WarehouseShippingNote/getDeliveryAddresses?billingAddressId=' + this.billAddrId);
|
||||||
@@ -667,7 +736,8 @@ Vue.component('warehouse-shipping-note-modal-address', {
|
|||||||
this.window.notify('error', 'Rechnungsadresse konnte nicht gefunden werden');
|
this.window.notify('error', 'Rechnungsadresse konnte nicht gefunden werden');
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
this.window.notify('success', 'Rechnungsadresse gefunden');
|
// TODO: here is still a bug that we fetch the billing address twice
|
||||||
|
// this.window.notify('success', 'Rechnungsadresse gefunden');
|
||||||
|
|
||||||
this.$emit('update:delAddrName',
|
this.$emit('update:delAddrName',
|
||||||
response.data.result.address.company || response.data.result.address.firstname + ' ' + response.data.result.address.lastname);
|
response.data.result.address.company || response.data.result.address.firstname + ' ' + response.data.result.address.lastname);
|
||||||
@@ -700,8 +770,12 @@ Vue.component('warehouse-shipping-note-signature-pad', {
|
|||||||
template: `
|
template: `
|
||||||
<tt-modal class="signModal" :show="true" :delete="false" :submit="false" @update:show="$emit('close')" :title="'Unterschrift'">
|
<tt-modal class="signModal" :show="true" :delete="false" :submit="false" @update:show="$emit('close')" :title="'Unterschrift'">
|
||||||
<div style="max-width: 520px;display: flex; flex-direction: column; align-items: center;">
|
<div style="max-width: 520px;display: flex; flex-direction: column; align-items: center;">
|
||||||
<div style="width: 480px"><tt-input v-model="signatureName" label="Name" row/></div>
|
<div style="width: 480px">
|
||||||
<div><canvas id="signature-pad" width="500" height="200" style="border: 1px solid black"></canvas></div>
|
<tt-input v-model="signatureName" label="Name" row/>
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<canvas id="signature-pad" width="500" height="200" style="border: 1px solid black"></canvas>
|
||||||
|
</div>
|
||||||
<div>
|
<div>
|
||||||
<button class="btn btn-primary" @click="submit()">Speichern</button>
|
<button class="btn btn-primary" @click="submit()">Speichern</button>
|
||||||
<button class="btn btn-primary" @click="signaturePad.clear()">Leeren</button>
|
<button class="btn btn-primary" @click="signaturePad.clear()">Leeren</button>
|
||||||
@@ -712,7 +786,8 @@ Vue.component('warehouse-shipping-note-signature-pad', {
|
|||||||
methods: {
|
methods: {
|
||||||
async submit() {
|
async submit() {
|
||||||
const data = this.signaturePad.toDataURL();
|
const data = this.signaturePad.toDataURL();
|
||||||
const response = await axios.post(window.TT_CONFIG['BASE_PATH'] + '/WarehouseShippingNote/sign?id=' + this.shippingNoteId, {signature: data, signatureName: this.signatureName});
|
const response = await axios.post(window.TT_CONFIG['BASE_PATH'] + '/WarehouseShippingNote/sign?id=' + this.shippingNoteId,
|
||||||
|
{signature: data, signatureName: this.signatureName});
|
||||||
if (response.data.success) {
|
if (response.data.success) {
|
||||||
this.window.notify('success', response.data.message || 'Erfolgreich unterschrieben');
|
this.window.notify('success', response.data.message || 'Erfolgreich unterschrieben');
|
||||||
this.$emit('close');
|
this.$emit('close');
|
||||||
|
|||||||
File diff suppressed because one or more lines are too long
@@ -0,0 +1,7 @@
|
|||||||
|
/*!
|
||||||
|
* chartjs-adapter-moment v1.0.1
|
||||||
|
* https://www.chartjs.org
|
||||||
|
* (c) 2022 chartjs-adapter-moment Contributors
|
||||||
|
* Released under the MIT license
|
||||||
|
*/
|
||||||
|
!function(e,t){"object"==typeof exports&&"undefined"!=typeof module?t(require("moment"),require("chart.js")):"function"==typeof define&&define.amd?define(["moment","chart.js"],t):t((e="undefined"!=typeof globalThis?globalThis:e||self).moment,e.Chart)}(this,(function(e,t){"use strict";function n(e){return e&&"object"==typeof e&&"default"in e?e:{default:e}}var f=n(e);const a={datetime:"MMM D, YYYY, h:mm:ss a",millisecond:"h:mm:ss.SSS a",second:"h:mm:ss a",minute:"h:mm a",hour:"hA",day:"MMM D",week:"ll",month:"MMM YYYY",quarter:"[Q]Q - YYYY",year:"YYYY"};t._adapters._date.override("function"==typeof f.default?{_id:"moment",formats:function(){return a},parse:function(e,t){return"string"==typeof e&&"string"==typeof t?e=f.default(e,t):e instanceof f.default||(e=f.default(e)),e.isValid()?e.valueOf():null},format:function(e,t){return f.default(e).format(t)},add:function(e,t,n){return f.default(e).add(t,n).valueOf()},diff:function(e,t,n){return f.default(e).diff(f.default(t),n)},startOf:function(e,t,n){return e=f.default(e),"isoWeek"===t?(n=Math.trunc(Math.min(Math.max(0,n),6)),e.isoWeekday(n).startOf("day").valueOf()):e.startOf(t).valueOf()},endOf:function(e,t){return f.default(e).endOf(t).valueOf()}}:{})}));
|
||||||
@@ -99,7 +99,7 @@ Vue.component('tt-autocomplete', {
|
|||||||
|
|
||||||
|
|
||||||
if (this.value && this.apiUrl) {
|
if (this.value && this.apiUrl) {
|
||||||
const response = await axios.get(`${this.apiUrl}&autocomplete=1&searchedID=${this.value}`);
|
const response = await axios.get(`${this.apiUrl}${this.apiUrl.includes('?') ? '&' : '?'}autocomplete=1&searchedID=${this.value}`);
|
||||||
this.displayValue = response.data[0].text;
|
this.displayValue = response.data[0].text;
|
||||||
} else if (this.value) {
|
} else if (this.value) {
|
||||||
const selectedItem = this.items.find(item => item.value === this.value);
|
const selectedItem = this.items.find(item => item.value === this.value);
|
||||||
@@ -145,7 +145,6 @@ Vue.component('tt-autocomplete', {
|
|||||||
|
|
||||||
this.isLoading = true;
|
this.isLoading = true;
|
||||||
clearTimeout(this.fetchSuggestionsDebounceTimer);
|
clearTimeout(this.fetchSuggestionsDebounceTimer);
|
||||||
console.log(this.displayValue);
|
|
||||||
|
|
||||||
this.fetchSuggestionsDebounceTimer = setTimeout(() => {
|
this.fetchSuggestionsDebounceTimer = setTimeout(() => {
|
||||||
setTimeout(async () => {
|
setTimeout(async () => {
|
||||||
@@ -155,7 +154,7 @@ Vue.component('tt-autocomplete', {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const response = await axios.get(`${this.apiUrl}&autocomplete=1&q=${encodeURIComponent(this.displayValue)}`);
|
const response = await axios.get(`${this.apiUrl}${this.apiUrl.includes('?') ? '&' : '?'}autocomplete=1&q=${encodeURIComponent(this.displayValue)}`);
|
||||||
if (response.data?.status === 'error') {
|
if (response.data?.status === 'error') {
|
||||||
this.displayingItems = [];
|
this.displayingItems = [];
|
||||||
} else {
|
} else {
|
||||||
|
|||||||
@@ -38,7 +38,9 @@ Vue.component('tt-modal', {
|
|||||||
this.$emit('update:show', false)
|
this.$emit('update:show', false)
|
||||||
}
|
}
|
||||||
if (event.key === 'Enter' && this.save) {
|
if (event.key === 'Enter' && this.save) {
|
||||||
// only submit
|
if (event.target.tagName === 'TEXTAREA' || event.target.tagName === 'INPUT') {
|
||||||
|
return
|
||||||
|
}
|
||||||
this.$emit('submit')
|
this.$emit('submit')
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user