added new features for asset mgmt
This commit is contained in:
@@ -482,6 +482,15 @@ $siteTitle = "Benutzer";
|
||||
<label for="can_ADBExtended" class="form-check-label">Address-DB erweitert</label>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="col-4">
|
||||
<div class="form-group form-check">
|
||||
<input type="checkbox" class="form-check-input" name="can[AssetAdmin]"
|
||||
id="can_AssetAdmin"
|
||||
value="1" <?=($user && $user->can("AssetAdmin")) ? "checked='checked'" : ""?> />
|
||||
<label for="can_AssetAdmin" class="form-check-label">Asset-Admin</label>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<hr/>
|
||||
|
||||
@@ -117,6 +117,7 @@
|
||||
<?php endif; ?>
|
||||
|
||||
<?php if($me->is(["Admin"])): ?><li class="border-top"><a href="<?=self::getUrl("MailtemplateDispatch")?>"><i class="far fa-fw fa-envelope text-info"></i> Emailaussendungen</a></li><?php endif; ?>
|
||||
<?php if($me->is(["Admin"])): ?><li class="border-top"><a href="<?=self::getUrl("AssetManagement")?>"><i class="far fa-fw fa-clipboard-list text-info"></i> Asset Management</a></li><?php endif; ?>
|
||||
</ul>
|
||||
</li>
|
||||
<?php endif; ?>
|
||||
|
||||
@@ -4,26 +4,47 @@ class AssetManagementController extends TTCrud
|
||||
{
|
||||
protected string $headerTitle = 'Anlagenverwaltung';
|
||||
protected string $singleText = 'Anlage';
|
||||
protected bool $createText = false;
|
||||
|
||||
protected array $columns = [
|
||||
['key' => 'id', 'text' => 'ID', 'modal' => false, 'table' => false],
|
||||
['key' => 'name', 'text' => 'Gerät', 'required' => true, 'modal' => ['type' => 'text']],
|
||||
['key' => 'assetNumber', 'text' => 'Kennzeichen / Nr.', 'required' => true, 'modal' => ['type' => 'text']],
|
||||
['key' => 'currentUser', 'text' => 'Akt. Mitarbeiter', 'modal' => false, 'table' => ['sortable' => false]],
|
||||
['key' => 'currentSite', 'text' => 'Akt. Baustelle', 'modal' => false, 'table' => ['sortable' => false]],
|
||||
['key' => 'borrowDate', 'text' => 'Ausgeliehen seit', 'modal' => false, 'table' => ['sortable' => false]],
|
||||
['key' => 'currentUser', 'text' => 'Akt. Mitarbeiter', 'modal' => false, 'table' => ['sortable' => false, 'filter' => false]],
|
||||
['key' => 'currentSite', 'text' => 'Akt. Baustelle', 'modal' => false, 'table' => ['sortable' => false, 'filter' => false]],
|
||||
['key' => 'borrowDate', 'text' => 'Ausgeliehen seit', 'modal' => false, 'table' => ['sortable' => false, 'filter' => false]],
|
||||
['key' => 'location', 'text' => 'Lagerort', 'required' => true, 'modal' => ['type' => 'text']],
|
||||
['key' => 'serviceDueDate', 'text' => 'Service fällig', 'required' => false, 'modal' => ['type' => 'datepicker']],
|
||||
['key' => 'description', 'text' => 'Beschreibung', 'modal' => ['type' => 'text'], 'table' => false],
|
||||
['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]],
|
||||
];
|
||||
|
||||
protected array $permissionCheck = ['WarehouseAdmin']; // Or a new permission
|
||||
protected array $additionalJSVariables = ['ASSET_ADMIN' => true];
|
||||
|
||||
protected function prepareCrudConfig() {
|
||||
if ($this->user->can('AssetAdmin')) return;
|
||||
$this->columns = array_filter($this->columns, function ($column) {
|
||||
return $column['key'] != 'actions';
|
||||
});
|
||||
|
||||
$this->additionalJSVariables['ASSET_ADMIN'] = false;
|
||||
}
|
||||
|
||||
|
||||
protected function getAction()
|
||||
{
|
||||
$filter = $this->postData['filters'] ?? [];
|
||||
$order = $this->postData['order'] ?? ['key' => null, 'order' => 'ASC'];
|
||||
$page = $this->postData['pagination']['page'] ?? 1;
|
||||
$perPage = $this->postData['pagination']['per_page'] ?? 10;
|
||||
|
||||
if ($order['key'] === null && isset($this->defaultOrder)) {
|
||||
$order = $this->defaultOrder;
|
||||
}
|
||||
|
||||
$json = json_decode(file_get_contents('php://input'), true);
|
||||
$assets = AssetManagementModel::getAll([], $this->request->order);
|
||||
$assets = AssetManagementModel::getAll($filter, $perPage, ($page - 1) * $perPage, $order);
|
||||
$assetIds = array_map(fn($asset) => $asset->id, $assets);
|
||||
|
||||
if (empty($assetIds)) {
|
||||
@@ -79,7 +100,7 @@ class AssetManagementController extends TTCrud
|
||||
|
||||
protected function suggestAssetNumberAction()
|
||||
{
|
||||
$lastAsset = AssetManagementModel::getOne([], ['order' => 'DESC', 'key' => 'id']);
|
||||
$lastAsset = AssetManagementModel::getAll(['assetNumber' => 'XI%'],1,0, ['order' => 'DESC', 'key' => 'id'])[0];
|
||||
if (!$lastAsset || !preg_match('/XI(\d+)/', $lastAsset->assetNumber, $matches)) {
|
||||
$nextNumber = 1;
|
||||
} else {
|
||||
@@ -92,7 +113,7 @@ class AssetManagementController extends TTCrud
|
||||
protected function borrowAction()
|
||||
{
|
||||
$post = json_decode(file_get_contents('php://input'), true);
|
||||
if (empty($post['assetId']) || empty($post['userId']) || empty($post['site']) || empty($post['reason'])) {
|
||||
if (empty($post['assetId']) || empty($post['userId']) || empty($post['site'])) {
|
||||
self::sendError("Alle Felder sind erforderlich.");
|
||||
}
|
||||
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
class AssetManagementModel extends TTCrudBaseModel {
|
||||
public int $id;
|
||||
public string $name;
|
||||
public ?string $description;
|
||||
public string $assetNumber;
|
||||
public string $location;
|
||||
public ?string $serviceDueDate;
|
||||
|
||||
@@ -7,7 +7,7 @@ class AssetManagementJournalModel extends TTCrudBaseModel {
|
||||
public string $site;
|
||||
public int $borrowDate;
|
||||
public ?int $returnDate;
|
||||
public string $borrowReason;
|
||||
public ?string $borrowReason;
|
||||
public ?string $returnReason;
|
||||
public int $createBy;
|
||||
public int $create;
|
||||
|
||||
@@ -263,6 +263,7 @@ class UserController extends mfBaseController
|
||||
$user->permissions->canWarehouseEShop = "false";
|
||||
$user->permissions->canWarehouseUser = "false";
|
||||
$user->permissions->canADBExtended = "false";
|
||||
$user->permissions->canAssetAdmin = "false";
|
||||
|
||||
if($r->get("can") && is_array($r->can)) {
|
||||
foreach($r->can as $key => $can) {
|
||||
|
||||
@@ -0,0 +1,30 @@
|
||||
<?php
|
||||
declare(strict_types=1);
|
||||
|
||||
use Phinx\Migration\AbstractMigration;
|
||||
|
||||
final class AddDescriptionToAssetManagement extends AbstractMigration
|
||||
{
|
||||
/**
|
||||
* Adds the 'description' column to the 'AssetManagement' table.
|
||||
*/
|
||||
public function up(): void
|
||||
{
|
||||
$table = $this->table('AssetManagement');
|
||||
$table->addColumn('description', 'text', [
|
||||
'null' => true,
|
||||
'after' => 'name',
|
||||
]);
|
||||
$table->update();
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes the 'description' column from the 'AssetManagement' table.
|
||||
*/
|
||||
public function down(): void
|
||||
{
|
||||
$table = $this->table('AssetManagement');
|
||||
$table->removeColumn('description');
|
||||
$table->update();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,36 @@
|
||||
<?php
|
||||
declare(strict_types=1);
|
||||
|
||||
use Phinx\Migration\AbstractMigration;
|
||||
|
||||
final class ModifyBorrowReasonInAssetManagementJournal extends AbstractMigration
|
||||
{
|
||||
/**
|
||||
* Changes the 'borrowReason' column to be nullable, adds a comment, and sets its position.
|
||||
*/
|
||||
public function up(): void
|
||||
{
|
||||
$table = $this->table('AssetManagementJournal');
|
||||
$table->changeColumn('borrowReason', 'text', [
|
||||
'null' => true,
|
||||
'comment' => 'Reason for borrowing the asset',
|
||||
'after' => 'returnDate',
|
||||
]);
|
||||
$table->update();
|
||||
}
|
||||
|
||||
/**
|
||||
* Reverts the 'borrowReason' column to its previous state (not nullable and without a comment).
|
||||
*/
|
||||
public function down(): void
|
||||
{
|
||||
// Reverting the changes made in the up() method.
|
||||
// This assumes the column was NOT NULL and had no comment previously.
|
||||
$table = $this->table('AssetManagementJournal');
|
||||
$table->changeColumn('borrowReason', 'text', [
|
||||
'null' => false,
|
||||
'comment' => 'Reason for borrowing the asset', // Set comment back to empty
|
||||
]);
|
||||
$table->update();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,30 @@
|
||||
<?php /** @noinspection ALL */
|
||||
declare(strict_types=1);
|
||||
|
||||
use Phinx\Migration\AbstractMigration;
|
||||
|
||||
final class WorkerPermissionAddCanAssetAdmin extends AbstractMigration {
|
||||
public function up(): void {
|
||||
if ($this->getEnvironment() == "thetool") {
|
||||
$table = $this->table("WorkerPermission");
|
||||
$table->addColumn("canAssetAdmin", "enum", ["null" => false, "values" => 'false,true', "default" => "false", "after" => "canSuperexpert"]);
|
||||
$table->update();
|
||||
}
|
||||
|
||||
if ($this->getEnvironment() == "addressdb") {
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
public function down(): void {
|
||||
if ($this->getEnvironment() == "thetool") {
|
||||
$table = $this->table("WorkerPermission");
|
||||
$table->removeColumn("canAssetAdmin");
|
||||
$table->save();
|
||||
}
|
||||
|
||||
if ($this->getEnvironment() == "addressdb") {
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -7,21 +7,28 @@ Vue.component('asset-management', {
|
||||
<asset-management-modal
|
||||
v-if="modalId"
|
||||
:id="modalId"
|
||||
@close="modalId = null; $refs.table.refreshTable()"/>
|
||||
@close="modalId = null; $refs.table.$refs.table.refreshTable()"/>
|
||||
<asset-management-journal-modal
|
||||
v-if="journalModalAssetId"
|
||||
:asset-id="journalModalAssetId"
|
||||
@close="journalModalAssetId = null"/>
|
||||
|
||||
<button @click="modalId = 'create'" class="btn btn-primary">Anlage erstellen</button>
|
||||
|
||||
<tt-table-crud
|
||||
ref="table"
|
||||
@edit="modalId = $event.id"
|
||||
@edit="window.console.log($event.id);modalId = $event.id"
|
||||
emit-edit
|
||||
:crud-config="crudConfig">
|
||||
|
||||
<template v-slot:currentuser="{ row }">
|
||||
<asset-borrow-return-widget
|
||||
v-if="window.TT_CONFIG.ASSET_ADMIN === '1'"
|
||||
:row-data="row"
|
||||
@update="$refs.table.refreshTable()"/>
|
||||
@update="$refs.table.$refs.table.refreshTable()"/>
|
||||
<span v-else>
|
||||
{{ row.currentUser || 'Nicht ausgeliehen' }}
|
||||
</span>
|
||||
</template>
|
||||
|
||||
<template v-slot:journal="{ row }">
|
||||
@@ -40,7 +47,7 @@ Vue.component('asset-management', {
|
||||
|
||||
<template v-slot:borrowdate="{ row }">
|
||||
<span v-if="row.borrowDate">
|
||||
{{ window.moment.unix(row.borrowDate).format('DD.MM.YYYY') }}
|
||||
{{ window.moment.unix(row.borrowDate).format('DD.MM.YYYY HH:mm') }}
|
||||
</span>
|
||||
</template>
|
||||
|
||||
@@ -91,7 +98,7 @@ Vue.component('asset-borrow-return-widget', {
|
||||
<p><strong>Gerät:</strong> {{ rowData.name }}</p>
|
||||
<p><strong>Mitarbeiter:</strong> {{ selectedUserName }}</p>
|
||||
<tt-input label="Baustelle / Projekt" v-model="borrowSite" sm row required/>
|
||||
<tt-textarea label="Grund" v-model="borrowReason" sm row required/>
|
||||
<tt-textarea label="Grund (optional)" v-model="borrowReason" sm row required/>
|
||||
</tt-modal>
|
||||
|
||||
<tt-modal v-if="showReturnModal" :show="true" title="Gerät zurückgeben" @update:show="showReturnModal = false" @submit="returnAsset">
|
||||
@@ -126,8 +133,8 @@ Vue.component('asset-borrow-return-widget', {
|
||||
this.showBorrowModal = true;
|
||||
},
|
||||
async borrowAsset() {
|
||||
if (!this.borrowSite || !this.borrowReason) {
|
||||
return window.notify('error', 'Bitte Baustelle und Grund angeben.');
|
||||
if (!this.borrowSite) {
|
||||
return window.notify('error', 'Bitte Baustelle/Projekt angeben.');
|
||||
}
|
||||
try {
|
||||
const response = await axios.post(`${window.TT_CONFIG.BASE_PATH}/AssetManagement/borrow`, {
|
||||
@@ -183,23 +190,18 @@ Vue.component('asset-management-modal', {
|
||||
@delete="deleteAsset"
|
||||
>
|
||||
<tt-input label="Gerätename" v-model="asset.name" sm row required/>
|
||||
<tt-input label="Kennzeichen / Nr." v-model="asset.assetNumber" sm row required>
|
||||
<template v-slot:prepend>
|
||||
<button class="btn btn-sm btn-link" @click="suggestAssetNumber" title="Nächste Nummer vorschlagen">
|
||||
<i class="fas fa-magic"></i>
|
||||
</button>
|
||||
</template>
|
||||
</tt-input>
|
||||
<tt-input label="Kennzeichen / Nr." v-model="asset.assetNumber" sm row required/>
|
||||
<tt-input label="Lagerort" v-model="asset.location" sm row required/>
|
||||
<tt-date-picker label="Nächstes Service" v-model="asset.serviceDueDate" sm row :date-range="false"/>
|
||||
<tt-textarea label="Beschreibung" v-model="asset.description" sm row/>
|
||||
</tt-modal>
|
||||
`,
|
||||
data() {
|
||||
data(){
|
||||
return {
|
||||
asset: {
|
||||
name: '',
|
||||
assetNumber: '',
|
||||
location: 'Hauptlager',
|
||||
location: 'Liftkammer',
|
||||
serviceDueDate: null
|
||||
},
|
||||
}
|
||||
@@ -210,6 +212,7 @@ Vue.component('asset-management-modal', {
|
||||
}
|
||||
},
|
||||
async mounted() {
|
||||
console.log('AssetManagementModal mounted with id:', this.id, this.isCreateMode);
|
||||
if (!this.isCreateMode) {
|
||||
const response = await axios.get(`${window.TT_CONFIG.BASE_PATH}/AssetManagement/getById`, { params: { id: this.id } });
|
||||
this.asset = response.data;
|
||||
|
||||
Reference in New Issue
Block a user