Merge branch 'master' into fronkdev
This commit is contained in:
@@ -136,15 +136,25 @@
|
||||
<div class="row">
|
||||
|
||||
|
||||
<div class="col-sm-12 col-md-2">
|
||||
<label class="form-label" for="filter_preordercampaign_id">Kampagne</label>
|
||||
<select name="filter[preordercampaign_id]" id="filter_preordercampaign_id" class="form-control">
|
||||
<option value="">Alle</option>
|
||||
<?php foreach($my_campaigns as $c): ?>
|
||||
<option value="<?=$c->id?>" <?=(isset($campaign) && $c->id == $campaign->id) ? "selected='selected'" : ""?>><?=$c->name?></option>
|
||||
<?php endforeach; ?>
|
||||
</select>
|
||||
</div>
|
||||
<div class="col-sm-12 col-md-2">
|
||||
<label class="form-label" for="filter_preordercampaign_id">Kampagne</label>
|
||||
<select name="filter[preordercampaign_id]" id="filter_preordercampaign_id" class="form-control">
|
||||
<option value="">Alle</option>
|
||||
<?php foreach($my_campaigns as $c): ?>
|
||||
<option value="<?=$c->id?>" <?=(isset($campaign) && $c->id == $campaign->id) ? "selected='selected'" : ""?>><?=$c->name?></option>
|
||||
<?php endforeach; ?>
|
||||
</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>
|
||||
</div>
|
||||
|
||||
<div class="col-sm-12 col-md-2">
|
||||
<label class="form-label" for="filter_status">Status</label>
|
||||
|
||||
@@ -148,5 +148,9 @@ TODO: enable option for showing prices
|
||||
</div>
|
||||
<?php endif; ?>
|
||||
|
||||
<div style="padding-top: 16pt">
|
||||
Die Ware bleibt bis zur vollständigen Bezahlung Eigentum der XINON GmbH.
|
||||
</div>
|
||||
|
||||
</body>
|
||||
</html>
|
||||
@@ -562,10 +562,19 @@ class AddressController extends mfBaseController {
|
||||
}
|
||||
|
||||
|
||||
$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], "firstname" => $search]));
|
||||
$addresses = array_merge($addresses, AddressModel::search(["parents_only" => $po, "addresstype" => [$role], "lastname" => $search]));
|
||||
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], "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], "lastname" => $search]));
|
||||
}
|
||||
|
||||
if(!is_array($addresses) || !count($addresses)) {
|
||||
return false;
|
||||
|
||||
@@ -157,12 +157,17 @@ class CalendarModel
|
||||
$where .= " AND 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);
|
||||
if ($dbcal->num_rows($res)) {
|
||||
|
||||
while ($data = $dbcal->fetch_array($res)) {
|
||||
if ($data['categories']) {
|
||||
$categories = json_decode($data['categories'], true);
|
||||
} else {
|
||||
$categories = [];
|
||||
}
|
||||
unset($byweekday);
|
||||
$rrule = false;
|
||||
if ($attachments[$data['uuid']]) {
|
||||
@@ -173,7 +178,7 @@ class CalendarModel
|
||||
$attachmentLinks = "";
|
||||
}
|
||||
if ($data['all_day_event'] == 1) {
|
||||
if (strpos($data['name'], "Bereitschaft") === false && strpos($data['name'], "Blocker") === false) {
|
||||
if (in_array("Feiertag", $categories)) {
|
||||
continue;
|
||||
}
|
||||
$starttime = date("Y-m-d", $data['start_time']);
|
||||
@@ -190,6 +195,7 @@ class CalendarModel
|
||||
|
||||
if ($data['recurrence']) {
|
||||
$recurrence = json_decode($data['recurrence'], true);
|
||||
$rrule_events= json_decode($data['rrule_events'], true);
|
||||
if ($rrulefreq[$recurrence['pattern']['type']]) {
|
||||
unset ($byweekday);
|
||||
$freq = $rrulefreq[$recurrence['pattern']['type']];
|
||||
@@ -220,6 +226,7 @@ class CalendarModel
|
||||
}
|
||||
} else {
|
||||
$rrule = false;
|
||||
$rrule_events = false;
|
||||
}
|
||||
|
||||
if ($calendarColors[$data['calendar_id']]['bgcolor']) {
|
||||
@@ -245,7 +252,9 @@ class CalendarModel
|
||||
'rights' => array('rights' => $rights, 'order' => $rights),
|
||||
'location' => array('location' => $data['location']),
|
||||
'busy' => array('busy' => $data['busy']),
|
||||
'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'])),
|
||||
|
||||
@@ -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("preorders", $preorders);
|
||||
|
||||
$this->layout()->set("partners", PreorderModel::getAllPartners());
|
||||
}
|
||||
|
||||
private function getPreparedFilter($filter) {
|
||||
|
||||
@@ -178,6 +178,24 @@ class PreorderModel {
|
||||
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) {
|
||||
$db = FronkDB::singleton();
|
||||
|
||||
|
||||
@@ -4,50 +4,35 @@ class WarehouseShippingNoteController extends TTCrud {
|
||||
protected string $headerTitle = 'Lieferscheine';
|
||||
protected bool $createText = false;
|
||||
|
||||
protected array $columns = [['key' => 'id', 'text' => 'LS-Nr.', 'required' => false, 'modal' => false, 'table' => ['class' => 'text-nowrap']],
|
||||
['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']],
|
||||
['key' => 'deliveryAddressName', 'text' => 'L.-Adr. Name', 'required' => true],
|
||||
['key' => 'deliveryAddressLine', 'text' => 'L.-Adr.', 'required' => true],
|
||||
['key' => 'deliveryAddressPLZ', 'text' => 'L.-Adr. PLZ', 'required' => true],
|
||||
['key' => 'deliveryAddressEMail', 'text' => 'L.-Adr. EMail', 'required' => true, 'table' => false],
|
||||
['key' => 'note', 'text' => 'Notiz', 'required' => true, 'table' => false],
|
||||
['key' => 'status',
|
||||
'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' => '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',]],
|
||||
//@formatter:off
|
||||
protected array $columns = [
|
||||
['key' => 'id', 'text' => 'LS-Nr.', 'required' => false, 'modal' => false, 'table' => ['class' => 'text-nowrap']],
|
||||
['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']],
|
||||
['key' => 'deliveryAddressName', 'text' => 'L.-Adr. Name', 'required' => true],
|
||||
['key' => 'deliveryAddressLine', 'text' => 'L.-Adr.', 'required' => true],
|
||||
['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' => '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' => 'actions', 'text' => 'Aktionen', 'required' => false, 'modal' => false, 'table' => ['filter' => false, 'sortable' => false, 'class' => 'text-center']],];
|
||||
|
||||
['key' => 'actions',
|
||||
'text' => 'Aktionen',
|
||||
'required' => false,
|
||||
'modal' => false,
|
||||
'table' => ['filter' => false, 'sortable' => false, 'class' => 'text-center']],];
|
||||
protected array $defaultOrder = ['key' => 'create', 'order' => 'DESC'];
|
||||
|
||||
protected array $additionalActions = [['key' => 'openHistory', 'title' => 'Historie', 'class' => 'fas fa-history text-primary'],
|
||||
['key' => 'print', 'title' => 'Drucken', 'class' => 'fas fa-print text-primary'],
|
||||
['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.',
|
||||
'update' => 'Lieferschein wurde aktualisiert',
|
||||
'delete' => 'Lieferschein wurde gelöscht',
|
||||
'noChanges' => 'Keine Änderungen vorgenommen'];
|
||||
//@formatter:on
|
||||
|
||||
protected function prepareCrudConfig() {
|
||||
$users = array_map(function ($user) {
|
||||
@@ -55,6 +40,10 @@ class WarehouseShippingNoteController extends TTCrud {
|
||||
}, UserModel::search());
|
||||
|
||||
$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 {
|
||||
@@ -81,6 +70,14 @@ class WarehouseShippingNoteController extends TTCrud {
|
||||
}
|
||||
|
||||
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']);
|
||||
(new WarehouseHistoryController)->create($postData, $this->mod);
|
||||
return true;
|
||||
@@ -242,9 +239,10 @@ class WarehouseShippingNoteController extends TTCrud {
|
||||
// json decode hoursEntries and add to positions
|
||||
$hoursEntries = json_decode($shippingNote->hoursEntries, true);
|
||||
foreach ($hoursEntries as $hoursEntry) {
|
||||
// die(json_encode($hoursEntry));
|
||||
$positions[] = [
|
||||
'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.',
|
||||
'amount' => $hoursEntry['hourCount'],
|
||||
'price' => $hoursEntry['hourlyPrice'] * $hoursEntry['hourCount'] ?? 0,
|
||||
@@ -277,8 +275,8 @@ class WarehouseShippingNoteController extends TTCrud {
|
||||
}
|
||||
|
||||
$pdf_vars = ["shippingNote" => $shippingNote,
|
||||
"positions" => $positions,
|
||||
"textElements" => $textElements,
|
||||
"positions" => $positions,
|
||||
"textElements" => $textElements,
|
||||
"showPrices" => isset($_GET['price']) && $_GET['price'] == "true",
|
||||
"bank_iban" => TT_INVOICE_BANK_IBAN,
|
||||
"bank_bic" => TT_INVOICE_BANK_BIC,
|
||||
@@ -328,7 +326,7 @@ class WarehouseShippingNoteController extends TTCrud {
|
||||
|
||||
// TODO: either move this to UserController or make it better
|
||||
protected function userAutoCompleteAction() {
|
||||
$users = array_map(function($user) {
|
||||
$users = array_map(function ($user) {
|
||||
return ['value' => $user->id, 'text' => $user->name];
|
||||
}, UserModel::search(['employee' => true]));
|
||||
|
||||
@@ -336,11 +334,11 @@ class WarehouseShippingNoteController extends TTCrud {
|
||||
$searchedID = $this->request->searchedID;
|
||||
if (strlen($searchedID) > 0) {
|
||||
// find user with value searchedID
|
||||
$out = array_filter($users, function($user) use ($searchedID) {
|
||||
$out = array_filter($users, function ($user) use ($searchedID) {
|
||||
return $user['value'] == $searchedID;
|
||||
});
|
||||
} else {
|
||||
$out = array_filter($users, function($user) {
|
||||
$out = array_filter($users, function ($user) {
|
||||
;
|
||||
return strpos(strtolower($user['text']), strtolower($this->request->q)) !== false;
|
||||
});
|
||||
@@ -353,19 +351,20 @@ class WarehouseShippingNoteController extends TTCrud {
|
||||
|
||||
//TODO: either move this to TimerecordingCarController or make it better
|
||||
protected function timerecordingCarAutoCompleteAction() {
|
||||
$timerecordingCars = array_map(function($timerecordingCar) {
|
||||
return ['value' => $timerecordingCar->id, 'text' => $timerecordingCar->number_plate . " " . $timerecordingCar->brand . " " . $timerecordingCar->model];
|
||||
$timerecordingCars = array_map(function ($timerecordingCar) {
|
||||
return ['value' => $timerecordingCar->id,
|
||||
'text' => $timerecordingCar->number_plate . " " . $timerecordingCar->brand . " " . $timerecordingCar->model];
|
||||
}, TimerecordingCarModel::getAll());
|
||||
|
||||
$out = null;
|
||||
$searchedID = $this->request->searchedID;
|
||||
if (strlen($searchedID) > 0) {
|
||||
// find user with value searchedID
|
||||
$out = array_filter($timerecordingCars, function($timerecordingCar) use ($searchedID) {
|
||||
$out = array_filter($timerecordingCars, function ($timerecordingCar) use ($searchedID) {
|
||||
return $timerecordingCar['value'] == $searchedID;
|
||||
});
|
||||
} else {
|
||||
$out = array_filter($timerecordingCars, function($timerecordingCar) {
|
||||
$out = array_filter($timerecordingCars, function ($timerecordingCar) {
|
||||
return strpos(strtolower($timerecordingCar['text']), strtolower($this->request->q)) !== false;
|
||||
});
|
||||
|
||||
@@ -387,6 +386,36 @@ class WarehouseShippingNoteController extends TTCrud {
|
||||
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
|
||||
protected function getDistanceAction() {
|
||||
@@ -413,7 +442,7 @@ class WarehouseShippingNoteController extends TTCrud {
|
||||
|
||||
$curl = curl_init();
|
||||
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_FOLLOWLOCATION => true,
|
||||
CURLOPT_ENCODING => "",
|
||||
|
||||
@@ -9,7 +9,7 @@ class WarehouseShippingNoteModel extends TTCrudBaseModel {
|
||||
public string $deliveryAddressCity;
|
||||
public string $deliveryAddressEMail;
|
||||
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 $textElements;
|
||||
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)) {
|
||||
if ($exactMatch) {
|
||||
$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 {
|
||||
$filterItems = explode(" ", $filterValue);
|
||||
foreach ($filterItems as $item) {
|
||||
|
||||
+14
-2
@@ -124,6 +124,10 @@ class TTCrud extends mfBaseController {
|
||||
$page = $this->postData['pagination']['page'] ?? 1;
|
||||
$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);
|
||||
$filteredAvailable = $this->model::count($filter);
|
||||
$totalRows = $this->model::count();
|
||||
@@ -240,11 +244,19 @@ class TTCrud extends mfBaseController {
|
||||
|
||||
if (strlen($searchedID) > 0) {
|
||||
$filter = ['id' => $searchedID];
|
||||
$data = $this->model::getAll($filter, 10);
|
||||
} else {
|
||||
$filter = [$textKey => $this->request->q];
|
||||
$filter = [$textKey => $this->request->q . '%'];
|
||||
$data = $this->model::getAll($filter, 10);
|
||||
if (count($data) < 11) {
|
||||
$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) {
|
||||
return ['value' => $item->id, 'text' => $item->$textKey];
|
||||
|
||||
@@ -46,13 +46,13 @@ class Zabbix {
|
||||
return $response['result'];
|
||||
}
|
||||
|
||||
public function getItemValues($itemIds) {
|
||||
public function getItemValues($itemIds, $limit = 15) {
|
||||
$response = $this->zabbixRequest('history.get', array(
|
||||
'itemids' => $itemIds,
|
||||
'output' => 'extend',
|
||||
'sortfield' => 'clock',
|
||||
'sortorder' => 'DESC',
|
||||
'limit' => 15
|
||||
'limit' => $limit
|
||||
));
|
||||
return $response['result'];
|
||||
}
|
||||
@@ -64,10 +64,13 @@ class Zabbix {
|
||||
return $response['result'];
|
||||
}
|
||||
|
||||
public function getInterfaceItems($hostId, $interfaceName) {
|
||||
public function getHostInterfaceItems($hostId) {
|
||||
$response = $this->zabbixRequest('item.get', array(
|
||||
'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'];
|
||||
}
|
||||
|
||||
@@ -85,6 +85,7 @@ document.addEventListener('DOMContentLoaded', function () {
|
||||
var rrule = null;
|
||||
var duration = null;
|
||||
var rruleflag = false;
|
||||
let allDAy;
|
||||
$.each($('.calendar-check'), function (index, value) {
|
||||
if ($(this).prop('checked')) {
|
||||
rights = true;
|
||||
@@ -102,16 +103,12 @@ document.addEventListener('DOMContentLoaded', function () {
|
||||
|
||||
$.each(json.data, function (index, value) {
|
||||
if (!value.timerecording.timerecording) {
|
||||
allDAy = false;
|
||||
rrule = null;
|
||||
duration = null;
|
||||
rruleflag = false;
|
||||
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 (calendarRights[value.calendar_id.calendar_id] == 'all') {
|
||||
@@ -124,38 +121,90 @@ document.addEventListener('DOMContentLoaded', function () {
|
||||
} else {
|
||||
movable = false;
|
||||
}
|
||||
let event = {
|
||||
id: value.id.id,
|
||||
start: value.cstart.cstart,
|
||||
end: value.cend.cend,
|
||||
title: category,
|
||||
description: value.description.description,
|
||||
location: value.location.location,
|
||||
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,
|
||||
rrule: 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: value.busy.busy
|
||||
};
|
||||
userevents.push(event);
|
||||
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,
|
||||
start: rrule_event.start,
|
||||
end: rrule_event.end,
|
||||
title: rrule_event.subject,
|
||||
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,
|
||||
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,
|
||||
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: value.busy.busy
|
||||
};
|
||||
userevents.push(event);
|
||||
}
|
||||
if (value.rrule.rrule) {
|
||||
}
|
||||
} else {
|
||||
@@ -1767,6 +1816,7 @@ Xinon GMbH`;
|
||||
var rrule = null;
|
||||
var duration = null;
|
||||
var rruleflag = false;
|
||||
let allDAy = false;
|
||||
$.each($('.calendar-check'), function (index, value) {
|
||||
if ($(this).prop('checked')) {
|
||||
rights = true;
|
||||
@@ -1787,6 +1837,7 @@ Xinon GMbH`;
|
||||
rrule = null;
|
||||
duration = null;
|
||||
rruleflag = false;
|
||||
allDAy = false;
|
||||
category = value.ccategory.ccategory;
|
||||
if (value.rrule.rrule) {
|
||||
rrule = value.rrule.rrule;
|
||||
@@ -1805,33 +1856,90 @@ Xinon GMbH`;
|
||||
} else {
|
||||
movable = false;
|
||||
}
|
||||
userevents.push({
|
||||
id: value.id.id,
|
||||
start: value.cstart.cstart,
|
||||
end: value.cend.cend,
|
||||
title: category,
|
||||
description: value.description.description,
|
||||
location: value.location.location,
|
||||
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: movable,
|
||||
rruleflag: rruleflag,
|
||||
rrule: rrule,
|
||||
duration: duration,
|
||||
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: value.busy.busy
|
||||
});
|
||||
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,
|
||||
start: rrule_event.start,
|
||||
end: rrule_event.end,
|
||||
title: rrule_event.subject,
|
||||
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,
|
||||
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,
|
||||
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: value.busy.busy
|
||||
};
|
||||
userevents.push(event);
|
||||
}
|
||||
} else {
|
||||
otherevents.push({
|
||||
id: value.id.id,
|
||||
|
||||
@@ -77,7 +77,7 @@ Vue.component('DeviceTable', {
|
||||
|
||||
<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']['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>
|
||||
|
||||
|
||||
@@ -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', {
|
||||
//language=Vue
|
||||
template: `
|
||||
@@ -261,7 +262,7 @@ Vue.component('warehouse-article', {
|
||||
|
||||
<template v-slot:cheapestsellprice="{ row }">
|
||||
<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>
|
||||
</template>
|
||||
</template>
|
||||
|
||||
@@ -36,6 +36,10 @@
|
||||
grid-gap: 10px;
|
||||
}
|
||||
|
||||
.warehouse-shipping-note-modal-positions-entry-actions, .warehouse-shipping-note-modal-hours-entry-actions {
|
||||
grid-column: 2;
|
||||
}
|
||||
|
||||
.signModal > div {
|
||||
margin: 0;
|
||||
width: 100vw;
|
||||
|
||||
@@ -57,7 +57,7 @@ Vue.component('warehouse-shipping-note-modal-hours-entry', {
|
||||
<tt-input v-model="hourCount" label="Stunden" 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="kilometerCount" label="Kilometer" sm/>
|
||||
<tt-input :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>
|
||||
@@ -87,6 +87,10 @@ Vue.component('warehouse-shipping-note-modal-hours-entry', {
|
||||
this.updateCarId().then();
|
||||
},
|
||||
async updateKilometerCount() {
|
||||
if (!this.carId) {
|
||||
this.kilometerCount = '';
|
||||
return;
|
||||
}
|
||||
const delAddr = this.$parent.$parent.$parent.delAddrLine +
|
||||
' ' +
|
||||
this.$parent.$parent.$parent.delAddrCity +
|
||||
@@ -96,9 +100,11 @@ Vue.component('warehouse-shipping-note-modal-hours-entry', {
|
||||
this.kilometerCount = response.data.distance
|
||||
},
|
||||
async updateCarId() {
|
||||
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.carId = '';
|
||||
return;
|
||||
}
|
||||
this.carId = response.data.id;
|
||||
@@ -114,12 +120,14 @@ Vue.component('warehouse-shipping-note-modal-hours-entry', {
|
||||
}
|
||||
},
|
||||
async mounted() {
|
||||
if (!this.carId) this.updateCarId().then();
|
||||
if (!this.userId) this.userId = this.window.TT_CONFIG['USER_ID'];
|
||||
if (!this.carId) this.updateCarId().then();
|
||||
if (!this.date) this.updateDate();
|
||||
if (!this.kilometerCount) this.updateKilometerCount().then();
|
||||
|
||||
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
|
||||
async createOrUpdate() {
|
||||
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 = {
|
||||
amount: this.amount,
|
||||
price: parseFloat(this.price)
|
||||
price: parseFloat(this.price) ?? ''
|
||||
}
|
||||
if (!this.articleId && 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>
|
||||
</tr>
|
||||
<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.price?.toFixed(2)) }} €</td>
|
||||
<td>
|
||||
@@ -415,7 +424,7 @@ Vue.component('warehouse-shipping-note-modal-positions', {
|
||||
},
|
||||
editEntry(entry) {
|
||||
this.selectedUpdateIndex = this.positions.indexOf(entry);
|
||||
if (entry.article)this.$refs.entry.articleId = entry.article;
|
||||
if (entry.article) this.$refs.entry.articleId = entry.article;
|
||||
if (entry.articlePacket) this.$refs.entry.articlePacketId = entry.articlePacket;
|
||||
if (entry.articleText) this.$refs.entry.$refs.article.displayValue = entry.articleText;
|
||||
this.$refs.entry.amount = entry.amount;
|
||||
@@ -425,6 +434,7 @@ Vue.component('warehouse-shipping-note-modal-positions', {
|
||||
})
|
||||
|
||||
|
||||
// noinspection EqualityComparisonWithCoercionJS
|
||||
Vue.component('warehouse-shipping-note-modal', {
|
||||
props: {
|
||||
id: {type: [String, Number], required: true},
|
||||
@@ -434,7 +444,7 @@ Vue.component('warehouse-shipping-note-modal', {
|
||||
data() {
|
||||
return {
|
||||
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: '',
|
||||
delAddrName: '',
|
||||
delAddrLine: '',
|
||||
@@ -451,7 +461,7 @@ Vue.component('warehouse-shipping-note-modal', {
|
||||
|
||||
//language=Vue
|
||||
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%">
|
||||
<h4 class="text-center">Liefer- und Rechnungsadresse</h4>
|
||||
<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"/>
|
||||
|
||||
|
||||
<template v-if="billAddrId && delAddrName && delAddrLine && delAddrPLZ && delAddrCity">
|
||||
<hr>
|
||||
<h4 class="text-center">Textelemente</h4>
|
||||
<warehouse-shipping-note-modal-text-elements :text-elements="textElements"/>
|
||||
<div v-show="delAddrFilled === true">
|
||||
<template v-if="window.TT_CONFIG['WAREHOUSE_ADMIN'] == true && 1 < 0">
|
||||
<hr>
|
||||
<h4 class="text-center">Textelemente</h4>
|
||||
<warehouse-shipping-note-modal-text-elements :text-elements="textElements"/>
|
||||
</template>
|
||||
|
||||
|
||||
<hr>
|
||||
<tt-textarea label="Einleitender Text" v-model="note" sm row/>
|
||||
<tt-textarea label="Art der Arbeit" v-model="note" sm row/>
|
||||
|
||||
|
||||
<hr>
|
||||
@@ -478,16 +490,23 @@ Vue.component('warehouse-shipping-note-modal', {
|
||||
<hr>
|
||||
<h4 class="text-center">Positionen</h4>
|
||||
<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>
|
||||
<!-- 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
|
||||
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-success" @click="alert('Accept')">Akzeptieren</button>-->
|
||||
<!-- <button class="btn btn-warning" @click="alert('Invoiced')">Verrechnet</button>-->
|
||||
</template>
|
||||
</tt-modal>
|
||||
`,
|
||||
@@ -516,8 +535,14 @@ Vue.component('warehouse-shipping-note-modal', {
|
||||
}
|
||||
},
|
||||
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() {
|
||||
const data = {
|
||||
@@ -553,13 +578,16 @@ Vue.component('warehouse-shipping-note-modal', {
|
||||
computed: {
|
||||
title() {
|
||||
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', {
|
||||
// also add props for delAddrName, delAddrLine, delAddrPLZ, delAddrCity which we will sync with the parent component
|
||||
props: {
|
||||
billAddrId: {type: [String, Number], required: true},
|
||||
delAddrName: {type: String, required: true},
|
||||
@@ -570,14 +598,15 @@ Vue.component('warehouse-shipping-note-modal-address', {
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
window: window,
|
||||
addressModes: [{text: 'Wie Rechnungsadresse', value: 'billing'},
|
||||
{text: 'Bestehende Lieferadresse', value: 'existing'},
|
||||
{text: 'Andere Lieferadresse', value: 'new'}],
|
||||
addressMode: 'existing',
|
||||
addresses: [],
|
||||
fetchedBillAddr: null,
|
||||
selectedAddr: '',
|
||||
window: window,
|
||||
addressModes: [{text: 'Wie Rechnungsadresse', value: 'billing'},
|
||||
{text: 'Bestehende Lieferadresse', value: 'existing'},
|
||||
{text: 'Andere Lieferadresse', value: 'new'}],
|
||||
addressMode: 'existing',
|
||||
addresses: [],
|
||||
fetchedBillAddr: null,
|
||||
selectedAddr: '',
|
||||
newAddrGeoLatLon: '',
|
||||
}
|
||||
},
|
||||
//language=Vue
|
||||
@@ -590,36 +619,66 @@ Vue.component('warehouse-shipping-note-modal-address', {
|
||||
</template>
|
||||
|
||||
<template v-else-if="addressMode === 'new'">
|
||||
<tt-input v-model="delAddrName" label="Lieferadresse Name" sm row/>
|
||||
<tt-input v-model="delAddrLine" label="Lieferadresse" sm row/>
|
||||
<tt-input v-model="delAddrPLZ" label="Lieferadresse PLZ" sm row/>
|
||||
<tt-input v-model="delAddrCity" label="Lieferadresse Ort" sm row/>
|
||||
<tt-input v-model="delAddrEMail" label="Lieferadresse E-Mail" sm row/>
|
||||
<tt-input :value="delAddrName" @input="$emit('update:delAddrName', $event)" label="Lieferadresse Name*" sm row/>
|
||||
<tt-input :value="delAddrEMail" @input="$emit('update:delAddrEMail', $event)" label="Lieferadresse E-Mail" sm row/>
|
||||
<tt-autocomplete :api-url="window.TT_CONFIG['BASE_PATH'] + '/WarehouseShippingNote/geoAutocomplete'" @input="newAddrGeoLatLon = $event"
|
||||
label="Adresse*" 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>
|
||||
</div>
|
||||
`,
|
||||
watch: {
|
||||
billAddrId: {handler: 'updateBillingMode', immediate: false},
|
||||
addressMode: {handler: 'fetchDeliveryAddresses', immediate: false},
|
||||
selectedAddr: {handler: 'setSelectedAddrValues', immediate: false},
|
||||
billAddrId: {handler: 'updateBillingMode', immediate: false},
|
||||
addressMode: {handler: 'fetchDeliveryAddresses', immediate: false},
|
||||
selectedAddr: {handler: 'setSelectedAddrValues', immediate: false},
|
||||
newAddrGeoLatLon: {handler: 'fetchGeoAddress', immediate: false},
|
||||
},
|
||||
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() {
|
||||
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
|
||||
if (this.delAddrName && this.delAddrLine && this.delAddrPLZ && this.delAddrCity) {
|
||||
const foundAddress = this.addresses.find(address => address.deliveryAddressName ===
|
||||
this.delAddrName &&
|
||||
address.deliveryAddressLine ===
|
||||
this.delAddrLine &&
|
||||
address.deliveryAddressPLZ ===
|
||||
this.delAddrPLZ &&
|
||||
address.deliveryAddressCity ===
|
||||
this.delAddrCity && address.deliveryAddressEMail === this.delAddrEMail);
|
||||
const foundAddress = this.addresses.find(address =>
|
||||
address.deliveryAddressName === this.delAddrName &&
|
||||
address.deliveryAddressLine === this.delAddrLine &&
|
||||
address.deliveryAddressPLZ === this.delAddrPLZ &&
|
||||
address.deliveryAddressCity === this.delAddrCity &&
|
||||
address.deliveryAddressEMail === this.delAddrEMail);
|
||||
if (foundAddress) {
|
||||
this.addressMode = 'existing';
|
||||
this.selectedAddr = foundAddress.id;
|
||||
@@ -631,11 +690,21 @@ Vue.component('warehouse-shipping-note-modal-address', {
|
||||
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) {
|
||||
await this.fetchBillingAddress();
|
||||
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);
|
||||
@@ -667,7 +736,8 @@ Vue.component('warehouse-shipping-note-modal-address', {
|
||||
this.window.notify('error', 'Rechnungsadresse konnte nicht gefunden werden');
|
||||
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',
|
||||
response.data.result.address.company || response.data.result.address.firstname + ' ' + response.data.result.address.lastname);
|
||||
@@ -690,9 +760,9 @@ Vue.component('warehouse-shipping-note-signature-pad', {
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
window: window,
|
||||
signaturePad: null,
|
||||
shippingNote: null,
|
||||
window: window,
|
||||
signaturePad: null,
|
||||
shippingNote: null,
|
||||
signatureName: '',
|
||||
}
|
||||
},
|
||||
@@ -700,19 +770,24 @@ Vue.component('warehouse-shipping-note-signature-pad', {
|
||||
template: `
|
||||
<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="width: 480px"><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>
|
||||
<button class="btn btn-primary" @click="submit()">Speichern</button>
|
||||
<button class="btn btn-primary" @click="signaturePad.clear()">Leeren</button>
|
||||
</div>
|
||||
<div style="width: 480px">
|
||||
<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>
|
||||
<button class="btn btn-primary" @click="submit()">Speichern</button>
|
||||
<button class="btn btn-primary" @click="signaturePad.clear()">Leeren</button>
|
||||
</div>
|
||||
</div>
|
||||
</tt-modal>
|
||||
`,
|
||||
methods: {
|
||||
async submit() {
|
||||
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) {
|
||||
this.window.notify('success', response.data.message || 'Erfolgreich unterschrieben');
|
||||
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) {
|
||||
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;
|
||||
} else if (this.value) {
|
||||
const selectedItem = this.items.find(item => item.value === this.value);
|
||||
@@ -145,7 +145,6 @@ Vue.component('tt-autocomplete', {
|
||||
|
||||
this.isLoading = true;
|
||||
clearTimeout(this.fetchSuggestionsDebounceTimer);
|
||||
console.log(this.displayValue);
|
||||
|
||||
this.fetchSuggestionsDebounceTimer = setTimeout(() => {
|
||||
setTimeout(async () => {
|
||||
@@ -155,7 +154,7 @@ Vue.component('tt-autocomplete', {
|
||||
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') {
|
||||
this.displayingItems = [];
|
||||
} else {
|
||||
|
||||
@@ -38,7 +38,9 @@ Vue.component('tt-modal', {
|
||||
this.$emit('update:show', false)
|
||||
}
|
||||
if (event.key === 'Enter' && this.save) {
|
||||
// only submit
|
||||
if (event.target.tagName === 'TEXTAREA' || event.target.tagName === 'INPUT') {
|
||||
return
|
||||
}
|
||||
this.$emit('submit')
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user