Add print label functionality and enhance asset management actions
This commit is contained in:
52
Layout/default/AssetManagement/LABEL.php
Normal file
52
Layout/default/AssetManagement/LABEL.php
Normal file
@@ -0,0 +1,52 @@
|
|||||||
|
<style>
|
||||||
|
body {
|
||||||
|
font-family: Arial, sans-serif;
|
||||||
|
text-align: center;
|
||||||
|
margin: 0;
|
||||||
|
padding: 0;
|
||||||
|
}
|
||||||
|
.label-container {
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
justify-content: center;
|
||||||
|
align-items: center;
|
||||||
|
}
|
||||||
|
.logo-25 {
|
||||||
|
max-height: 45px;
|
||||||
|
margin-bottom: 5px;
|
||||||
|
}
|
||||||
|
.logo-50 {
|
||||||
|
max-height: 70px;
|
||||||
|
margin-bottom: 10px;
|
||||||
|
}
|
||||||
|
.address {
|
||||||
|
font-size: 8px;
|
||||||
|
line-height: 1.2;
|
||||||
|
}
|
||||||
|
|
||||||
|
.address-size-50 { font-size: 16px }
|
||||||
|
|
||||||
|
.inv-number-25 {
|
||||||
|
font-size: 14px;
|
||||||
|
font-weight: bold;
|
||||||
|
margin-top: 5px;
|
||||||
|
}
|
||||||
|
.inv-number-50 {
|
||||||
|
font-size: 28px;
|
||||||
|
font-weight: bold;
|
||||||
|
margin-top: 10px;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
|
||||||
|
<div class="label-container">
|
||||||
|
<img src="<?php echo BASEDIR ?>/public/assets/images/xinon-full.png" class="logo-<?php echo $size; ?>">
|
||||||
|
<div class="address address-size-<?php echo $size; ?>">
|
||||||
|
<?php echo $companyAddress; ?><br>
|
||||||
|
<?php echo $companyPhone; ?>
|
||||||
|
</div>
|
||||||
|
<div class="inv-number-<?php echo $size; ?>">
|
||||||
|
<?php echo $invNumber; ?>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
@@ -11,7 +11,6 @@ class AssetManagementController extends TTCrud
|
|||||||
['key' => 'currentUser', 'text' => 'Status', 'modal' => false, 'table' => ['sortable' => false, 'filter' => false]],
|
['key' => 'currentUser', 'text' => 'Status', 'modal' => false, 'table' => ['sortable' => false, 'filter' => false]],
|
||||||
['key' => 'location', 'text' => 'Lagerort', 'required' => true, 'modal' => ['type' => 'text'], 'table' => ['filter' => 'search']],
|
['key' => 'location', 'text' => 'Lagerort', 'required' => true, 'modal' => ['type' => 'text'], 'table' => ['filter' => 'search']],
|
||||||
['key' => 'serviceDueDate', 'text' => 'Service fällig', 'required' => false, 'modal' => ['type' => 'date'], 'table' => ['filter' => 'date']],
|
['key' => 'serviceDueDate', 'text' => 'Service fällig', 'required' => false, 'modal' => ['type' => 'date'], 'table' => ['filter' => 'date']],
|
||||||
['key' => 'journal', 'text' => 'Historie', 'modal' => false, 'table' => ['sortable' => false, 'filter' => false]],
|
|
||||||
['key' => 'actions', 'text' => 'Aktionen', 'modal' => false, 'table' => ['filter' => false, 'sortable' => false]],
|
['key' => 'actions', 'text' => 'Aktionen', 'modal' => false, 'table' => ['filter' => false, 'sortable' => false]],
|
||||||
];
|
];
|
||||||
|
|
||||||
@@ -21,7 +20,7 @@ class AssetManagementController extends TTCrud
|
|||||||
// Restrict actions if the user does not have the 'AssetAdmin' permission.
|
// Restrict actions if the user does not have the 'AssetAdmin' permission.
|
||||||
if (!$this->user->can('AssetAdmin')) {
|
if (!$this->user->can('AssetAdmin')) {
|
||||||
$this->additionalJSVariables['ASSET_ADMIN'] = '0';
|
$this->additionalJSVariables['ASSET_ADMIN'] = '0';
|
||||||
$this->columns = array_filter($this->columns, fn($col) => !in_array($col['key'], ['actions', 'journal']));
|
$this->columns = array_filter($this->columns, fn($col) => $col['key'] !== 'actions');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -277,4 +276,40 @@ class AssetManagementController extends TTCrud
|
|||||||
AssetManagementReservationModel::delete($post['id']);
|
AssetManagementReservationModel::delete($post['id']);
|
||||||
self::returnJson(['success' => true, 'message' => 'Reservierung gelöscht.']);
|
self::returnJson(['success' => true, 'message' => 'Reservierung gelöscht.']);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
protected function printLabelAction() {
|
||||||
|
if (!$this->user->can('AssetAdmin')) {
|
||||||
|
self::sendError("Permission denied", 403);
|
||||||
|
}
|
||||||
|
|
||||||
|
$assetId = $this->request->id;
|
||||||
|
$size = $this->request->size ?? '25'; // Default to 25mm
|
||||||
|
|
||||||
|
$asset = AssetManagementModel::get($assetId);
|
||||||
|
if (!$asset) {
|
||||||
|
self::sendError("Asset not found", 404);
|
||||||
|
}
|
||||||
|
|
||||||
|
$pdf_vars = [
|
||||||
|
'companyAddress' => 'Fladnitz 150, 8322 Studenzen',
|
||||||
|
'companyPhone' => '+43 3115 40800',
|
||||||
|
'invNumber' => $asset->assetNumber,
|
||||||
|
'size' => $size
|
||||||
|
];
|
||||||
|
|
||||||
|
$pdf = new PdfForm("AssetManagement/LABEL", $pdf_vars);
|
||||||
|
|
||||||
|
if ($size == '50') {
|
||||||
|
$wkhtmltopdfArgs = "--page-height 50mm --page-width 80mm --margin-top 1mm --margin-bottom 0 --margin-left 0 --margin-right 0 --disable-smart-shrinking --encoding utf-8";
|
||||||
|
} else { // 25mm
|
||||||
|
$wkhtmltopdfArgs = "--page-height 25mm --page-width 50mm --margin-top 1mm --margin-bottom 0 --margin-left 0 --margin-right 0 --disable-smart-shrinking --encoding utf-8";
|
||||||
|
}
|
||||||
|
|
||||||
|
$filename = $pdf->render($wkhtmltopdfArgs);
|
||||||
|
|
||||||
|
header('Content-Type: application/pdf');
|
||||||
|
header('Content-Disposition: inline; filename="label-' . $asset->assetNumber . '.pdf"');
|
||||||
|
readfile($filename);
|
||||||
|
die();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
@@ -1,10 +1,22 @@
|
|||||||
window.TT_CONFIG["CRUD_CONFIG"]["additionalActions"] = [
|
window.TT_CONFIG["CRUD_CONFIG"]["additionalActions"] = [
|
||||||
|
{
|
||||||
|
"key": "openHistory",
|
||||||
|
"title": "Historie",
|
||||||
|
"class": "fas fa-history text-info",
|
||||||
|
"condition": (row) => window.TT_CONFIG.ASSET_ADMIN === '1',
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"key": "reserve",
|
"key": "reserve",
|
||||||
"title": "Reservieren",
|
"title": "Reservieren",
|
||||||
"class": "fas fa-calendar-alt btn-outline-warning",
|
"class": "fas fa-calendar-alt text-warning",
|
||||||
"condition": (row) => window.TT_CONFIG.ASSET_ADMIN === '1',
|
"condition": (row) => window.TT_CONFIG.ASSET_ADMIN === '1',
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"key": "print",
|
||||||
|
"title": "Label drucken",
|
||||||
|
"class": "fas fa-print text-secondary",
|
||||||
|
"condition": (row) => window.TT_CONFIG.ASSET_ADMIN === '1',
|
||||||
|
}
|
||||||
];
|
];
|
||||||
// =================================================================================
|
// =================================================================================
|
||||||
// Main Asset Management Component
|
// Main Asset Management Component
|
||||||
@@ -24,6 +36,10 @@ Vue.component('asset-management', {
|
|||||||
v-if="reservationModalAsset"
|
v-if="reservationModalAsset"
|
||||||
:asset="reservationModalAsset"
|
:asset="reservationModalAsset"
|
||||||
@close="reservationModalAsset = null; $refs.table.$refs.table.refreshTable()"/>
|
@close="reservationModalAsset = null; $refs.table.$refs.table.refreshTable()"/>
|
||||||
|
<asset-print-label-modal
|
||||||
|
v-if="printModalAsset"
|
||||||
|
:asset="printModalAsset"
|
||||||
|
@close="printModalAsset = null"/>
|
||||||
|
|
||||||
<button v-if="window.TT_CONFIG.ASSET_ADMIN === '1'" @click="modalId = 'create'" class="btn btn-primary">Anlage erstellen</button>
|
<button v-if="window.TT_CONFIG.ASSET_ADMIN === '1'" @click="modalId = 'create'" class="btn btn-primary">Anlage erstellen</button>
|
||||||
|
|
||||||
@@ -31,7 +47,9 @@ Vue.component('asset-management', {
|
|||||||
ref="table"
|
ref="table"
|
||||||
emit-edit
|
emit-edit
|
||||||
@edit="modalId = $event.id"
|
@edit="modalId = $event.id"
|
||||||
|
@openHistory="journalModalAssetId = $event.id"
|
||||||
@reserve="reservationModalAsset = $event"
|
@reserve="reservationModalAsset = $event"
|
||||||
|
@print="printModalAsset = $event"
|
||||||
:crud-config="crudConfig">
|
:crud-config="crudConfig">
|
||||||
|
|
||||||
<template v-slot:assetdetails="{ row }">
|
<template v-slot:assetdetails="{ row }">
|
||||||
@@ -51,15 +69,6 @@ Vue.component('asset-management', {
|
|||||||
@update="updateTableWithoutModalsOpening"/>
|
@update="updateTableWithoutModalsOpening"/>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<template v-slot:journal="{ row }">
|
|
||||||
<tt-button
|
|
||||||
sm
|
|
||||||
icon="fas fa-history"
|
|
||||||
title="Historie"
|
|
||||||
@click="journalModalAssetId = row.id"
|
|
||||||
additional-class="btn-outline-info"/>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<template v-slot:serviceduedate="{ row }">
|
<template v-slot:serviceduedate="{ row }">
|
||||||
<span v-if="row.serviceDueDate" :class="{'text-danger font-weight-bold': isDatePast(row.serviceDueDate)}">
|
<span v-if="row.serviceDueDate" :class="{'text-danger font-weight-bold': isDatePast(row.serviceDueDate)}">
|
||||||
{{ formatDate(row.serviceDueDate, 'DD.MM.YYYY') }}
|
{{ formatDate(row.serviceDueDate, 'DD.MM.YYYY') }}
|
||||||
@@ -75,6 +84,7 @@ Vue.component('asset-management', {
|
|||||||
modalId: null,
|
modalId: null,
|
||||||
journalModalAssetId: null,
|
journalModalAssetId: null,
|
||||||
reservationModalAsset: null,
|
reservationModalAsset: null,
|
||||||
|
printModalAsset: null,
|
||||||
crudConfig: window.TT_CONFIG.CRUD_CONFIG,
|
crudConfig: window.TT_CONFIG.CRUD_CONFIG,
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@@ -97,6 +107,29 @@ Vue.component('asset-management', {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// =================================================================================
|
||||||
|
// Asset Print Label Modal
|
||||||
|
// =================================================================================
|
||||||
|
Vue.component('asset-print-label-modal', {
|
||||||
|
props: { asset: { type: Object, required: true } },
|
||||||
|
template: `
|
||||||
|
<tt-modal :show="true" :title="'Label für ' + asset.name + ' drucken'" :save="false" :delete="false" @update:show="$emit('close')">
|
||||||
|
<div class="text-center">
|
||||||
|
<p>Wählen Sie die gewünschte Label-Größe:</p>
|
||||||
|
<tt-button text="25mm" @click="printLabel(25)" additional-class="btn-primary mr-2"/>
|
||||||
|
<tt-button text="50mm" @click="printLabel(50)" additional-class="btn-primary"/>
|
||||||
|
</div>
|
||||||
|
</tt-modal>
|
||||||
|
`,
|
||||||
|
methods: {
|
||||||
|
printLabel(size) {
|
||||||
|
const url = window.TT_CONFIG.BASE_PATH + "/AssetManagement/printLabel?id=" + this.asset.id + "&size=" +size;
|
||||||
|
window.open(url, '_blank');
|
||||||
|
this.$emit('close');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
// =================================================================================
|
// =================================================================================
|
||||||
// Asset Image Component
|
// Asset Image Component
|
||||||
// =================================================================================
|
// =================================================================================
|
||||||
@@ -652,8 +685,8 @@ Vue.component('asset-reservation-modal', {
|
|||||||
<div class="card-body">
|
<div class="card-body">
|
||||||
<h5 class="card-title">Neue Reservierung</h5>
|
<h5 class="card-title">Neue Reservierung</h5>
|
||||||
<tt-autocomplete label="Mitarbeiter" :api-url="userAutoCompleteUrl" v-model="newReservation.userId" sm row/>
|
<tt-autocomplete label="Mitarbeiter" :api-url="userAutoCompleteUrl" v-model="newReservation.userId" sm row/>
|
||||||
<tt-date-picker label="Startdatum" v-model="newReservation.startDate" :date-range="false" sm row/>
|
<tt-date-picker label="Startdatum" v-model="newReservation.startDate" :date-range="false" :time-picker="false" sm row/>
|
||||||
<tt-date-picker label="Enddatum" v-model="newReservation.endDate" :date-range="false" :disabled="isPermanent === 1" sm row/>
|
<tt-date-picker label="Enddatum" v-model="newReservation.endDate" :date-range="false" :time-picker="false" :disabled="isPermanent === 1" sm row/>
|
||||||
<tt-checkbox label="Dauerhaft" v-model="isPermanent" sm row/>
|
<tt-checkbox label="Dauerhaft" v-model="isPermanent" sm row/>
|
||||||
<tt-textarea label="Notizen" v-model="newReservation.notes" sm row/>
|
<tt-textarea label="Notizen" v-model="newReservation.notes" sm row/>
|
||||||
<tt-button text="Reservierung speichern" @click="saveReservation" additional-class="btn-primary float-right"/>
|
<tt-button text="Reservierung speichern" @click="saveReservation" additional-class="btn-primary float-right"/>
|
||||||
|
|||||||
@@ -10,6 +10,7 @@ Vue.component('tt-date-picker', {
|
|||||||
additionalProps: Object,
|
additionalProps: Object,
|
||||||
sm: { type: Boolean, default: false },
|
sm: { type: Boolean, default: false },
|
||||||
dateRange: { type: Boolean, default: true },
|
dateRange: { type: Boolean, default: true },
|
||||||
|
timePicker: { type: Boolean, default: true },
|
||||||
},
|
},
|
||||||
template: `
|
template: `
|
||||||
<div class="form-group" :class="{'row': row}">
|
<div class="form-group" :class="{'row': row}">
|
||||||
@@ -53,10 +54,14 @@ Vue.component('tt-date-picker', {
|
|||||||
await loadScript('/js/jquery.min.js', () => typeof jQuery !== 'undefined');
|
await loadScript('/js/jquery.min.js', () => typeof jQuery !== 'undefined');
|
||||||
await loadScript('/plugins/daterangepicker/daterangepicker.js', () => typeof $?.fn?.daterangepicker !== 'undefined');
|
await loadScript('/plugins/daterangepicker/daterangepicker.js', () => typeof $?.fn?.daterangepicker !== 'undefined');
|
||||||
|
|
||||||
|
if (!this.timePicker) {
|
||||||
|
this.locale.format = 'DD.MM.YYYY';
|
||||||
|
}
|
||||||
|
|
||||||
const pickerOptions = {
|
const pickerOptions = {
|
||||||
autoUpdateInput: false,
|
autoUpdateInput: false,
|
||||||
singleDatePicker: !this.dateRange,
|
singleDatePicker: !this.dateRange,
|
||||||
timePicker: true,
|
timePicker: this.timePicker,
|
||||||
timePicker24Hour: true,
|
timePicker24Hour: true,
|
||||||
locale: this.locale,
|
locale: this.locale,
|
||||||
startDate: this.dateRange ? (this.value?.from ? this.moment.unix(this.value.from) : this.moment()) : (this.value ? this.moment.unix(this.value) : this.moment()),
|
startDate: this.dateRange ? (this.value?.from ? this.moment.unix(this.value.from) : this.moment()) : (this.value ? this.moment.unix(this.value) : this.moment()),
|
||||||
|
|||||||
Reference in New Issue
Block a user