diff --git a/application/AssetManagement/AssetManagementController.php b/application/AssetManagement/AssetManagementController.php
index af710d615..ff7d00cc6 100644
--- a/application/AssetManagement/AssetManagementController.php
+++ b/application/AssetManagement/AssetManagementController.php
@@ -8,6 +8,7 @@ class AssetManagementController extends TTCrud
// Simplified columns for better layout, details are in the 'assetDetails' slot
protected array $columns = [
['key' => 'assetDetails', 'text' => 'Gerät', 'modal' => false, 'table' => ['filter' => 'search', 'sortable' => true]],
+ ['key' => 'category', 'text' => 'Kategorie', 'modal' => false, 'table' => ['filter' => 'select', 'filterOptions' => []]],
['key' => 'currentUser', 'text' => 'Status', 'modal' => false, 'table' => ['sortable' => false, 'filter' => false]],
['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']],
@@ -22,6 +23,15 @@ class AssetManagementController extends TTCrud
$this->additionalJSVariables['ASSET_ADMIN'] = '0';
$this->columns = array_filter($this->columns, fn($col) => $col['key'] !== 'actions');
}
+
+ $categories = AssetManagementModel::getDistinctCategories();
+ $categoryOptions = array_map(fn($cat) => ['value' => $cat, 'text' => $cat], $categories);
+ foreach ($this->columns as &$column) {
+ if ($column['key'] === 'category') {
+ $column['table']['filterOptions'] = $categoryOptions;
+ break;
+ }
+ }
}
/**
@@ -282,6 +292,18 @@ class AssetManagementController extends TTCrud
self::returnJson(['success' => true, 'message' => 'Reservierung gelöscht.']);
}
+ protected function getCategoriesAction()
+ {
+ $searchTerm = $this->request->q ?? '';
+ $categories = AssetManagementModel::getDistinctCategories($searchTerm);
+
+ $result = array_map(function($category) {
+ return ['value' => $category, 'text' => $category];
+ }, $categories);
+
+ self::returnJson($result);
+ }
+
protected function printLabelAction() {
if (!$this->user->can('AssetAdmin')) {
self::sendError("Permission denied", 403);
diff --git a/application/AssetManagement/AssetManagementModel.php b/application/AssetManagement/AssetManagementModel.php
index 9cef32f94..793e54f10 100644
--- a/application/AssetManagement/AssetManagementModel.php
+++ b/application/AssetManagement/AssetManagementModel.php
@@ -4,6 +4,7 @@ class AssetManagementModel extends TTCrudBaseModel {
public int $id;
public string $name;
public ?string $description;
+ public ?string $category;
public ?int $mainImageId; // Renamed from imageId
public ?string $imageIds; // Changed to JSON (will be a string in PHP)
public string $assetNumber;
@@ -35,8 +36,7 @@ class AssetManagementModel extends TTCrudBaseModel {
foreach ($searchTerms as $term) {
if (empty(trim($term))) continue;
$escapedTerm = $db->real_escape_string($term);
- // For each term, search in name, assetNumber, and description.
- $searchConditions[] = "(`name` LIKE '%$escapedTerm%' OR `assetNumber` LIKE '%$escapedTerm%' OR `description` LIKE '%$escapedTerm%')";
+ $searchConditions[] = "(`name` LIKE '%$escapedTerm%' OR `assetNumber` LIKE '%$escapedTerm%' OR `description` LIKE '%$escapedTerm%' OR `category` LIKE '%$escapedTerm%')";
}
if (!empty($searchConditions)) {
@@ -99,8 +99,8 @@ class AssetManagementModel extends TTCrudBaseModel {
foreach ($searchTerms as $term) {
if (empty(trim($term))) continue;
$escapedTerm = $db->real_escape_string($term);
- // For each term, search in name, assetNumber, and description.
- $searchConditions[] = "(`name` LIKE '%$escapedTerm%' OR `assetNumber` LIKE '%$escapedTerm%' OR `description` LIKE '%$escapedTerm%')";
+ // For each term, search in name, assetNumber, description, and category.
+ $searchConditions[] = "(`name` LIKE '%$escapedTerm%' OR `assetNumber` LIKE '%$escapedTerm%' OR `description` LIKE '%$escapedTerm%' OR `category` LIKE '%$escapedTerm%')";
}
if (!empty($searchConditions)) {
@@ -128,4 +128,26 @@ class AssetManagementModel extends TTCrudBaseModel {
return $result->fetch_assoc()['count'];
}
+
+ public static function getDistinctCategories(string $searchTerm = ''): array {
+ $db = self::getDB();
+ $table = self::getFullyQualifiedTable();
+
+ $sql = "SELECT DISTINCT `category` FROM $table WHERE `category` IS NOT NULL AND `category` != ''";
+
+ if (!empty($searchTerm)) {
+ $escapedTerm = $db->real_escape_string($searchTerm);
+ $sql .= " AND `category` LIKE '%$escapedTerm%'";
+ }
+
+ $sql .= " ORDER BY `category` ASC LIMIT 20";
+
+ $result = $db->query($sql);
+ $categories = [];
+ while ($row = $result->fetch_assoc()) {
+ $categories[] = $row['category'];
+ }
+
+ return $categories;
+ }
}
\ No newline at end of file
diff --git a/db/migrations/20260119140000_asset_management_add_category.php b/db/migrations/20260119140000_asset_management_add_category.php
new file mode 100644
index 000000000..47dac3c34
--- /dev/null
+++ b/db/migrations/20260119140000_asset_management_add_category.php
@@ -0,0 +1,33 @@
+getEnvironment() == "thetool") {
+ $table = $this->table("AssetManagement");
+ $table->addColumn('category', 'string', [
+ 'limit' => 255,
+ 'null' => true,
+ 'default' => null,
+ 'after' => 'description',
+ 'comment' => 'Free text category for the asset with autocomplete support',
+ ]);
+ $table->addIndex(['category'], ['name' => 'idx_category']);
+ $table->update();
+ }
+ }
+
+ public function down(): void
+ {
+ if ($this->getEnvironment() == "thetool") {
+ $table = $this->table("AssetManagement");
+ $table->removeIndex(['category']);
+ $table->removeColumn('category');
+ $table->update();
+ }
+ }
+}
diff --git a/public/js/pages/AssetManagement/AssetManagement.js b/public/js/pages/AssetManagement/AssetManagement.js
index a26bd81d1..64ab588d0 100644
--- a/public/js/pages/AssetManagement/AssetManagement.js
+++ b/public/js/pages/AssetManagement/AssetManagement.js
@@ -454,6 +454,14 @@ Vue.component('asset-management-modal', {
+
@@ -488,9 +496,11 @@ Vue.component('asset-management-modal', {
`,
data(){
return {
+ categoryAutoCompleteUrl: window.TT_CONFIG.BASE_PATH + '/AssetManagement/getCategories',
asset: {
name: '',
description: '',
+ category: '',
assetNumber: '',
location: 'Liftkammer',
serviceDueDate: null,