Rename the equipment domain to machines; retype the models catalog (ADR-011)
The equipment plugin is now the machines plugin, ending the UI-vs-code vocabulary split while the contract is pre-1.0 and nothing external depends on the old names. - plugins/equipment -> plugins/machines: manifest, class, /api/machines, machines.* permissions, registry key (with an auto-migrating load shim for existing installs). - Tables: equipment -> machines (equipmentid -> machineid) and equipmenttypes -> machinetypes, renamed in the plugin's own migration chain (machines0002rename), idempotent for both upgrading and fresh installs. - The legacy core machinetypes lookup actually types the vendor MODELS catalog, so it is renamed losslessly to modeltypes (models.modeltypeid, /api/modeltypes, Model Types settings page) rather than collapsed, freeing the machinetypes name. Core migration 7d17_machines_rename also flips data in place: assettypes row equipment -> machine, auditlog entitytype, identifier_/search_ settings keys, permission rows, and renames alembic_version_equipment. - Frontend: machinesApi/modeltypesApi, item.machine response shape, assettype value compares 'equipment' -> 'machine' (map, search, custom fields, relationships), routes machines.js with plugin gating retagged, /print/machine-badge, Machine Types (subtypes) and Model Types (catalog) settings pages, machines-by-type report id. - Docs swept; ADRs left as history per the authoring rule. Upgrade: flask db upgrade then flask plugin upgrade-all. Verified: dev DB flipped live (262 machines, 35 modeltypes, 95 models retyped, zero equipment tables remain); fresh scratch-MySQL install produces the new names; 341 tests green; naming/style green; frontend builds; live E2E on machines list/detail, PC relationships, map, reports, and both settings pages. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
@@ -2,7 +2,7 @@
|
||||
|
||||
from .auth import auth_bp
|
||||
from .assets import assets_bp
|
||||
from .machinetypes import machinetypes_bp
|
||||
from .modeltypes import modeltypes_bp
|
||||
from .plugins import plugins_bp
|
||||
from .vendors import vendors_bp
|
||||
from .models import models_bp
|
||||
@@ -24,7 +24,7 @@ from .setup import setup_bp
|
||||
__all__ = [
|
||||
'auth_bp',
|
||||
'assets_bp',
|
||||
'machinetypes_bp',
|
||||
'modeltypes_bp',
|
||||
'plugins_bp',
|
||||
'vendors_bp',
|
||||
'models_bp',
|
||||
|
||||
@@ -332,7 +332,7 @@ def list_assets():
|
||||
- per_page: Items per page (default: 20, max: 100)
|
||||
- active: Filter by active status (default: true)
|
||||
- search: Search by assetnumber or name
|
||||
- type: Filter by asset type name (e.g., 'equipment', 'computer')
|
||||
- type: Filter by asset type name (e.g., 'machine', 'computer')
|
||||
- type_id: Filter by asset type ID
|
||||
- status_id: Filter by status ID
|
||||
- location_id: Filter by location ID
|
||||
@@ -699,8 +699,8 @@ def get_assets_map():
|
||||
Returns assets with mapx/mapy coordinates, joined with type-specific data.
|
||||
|
||||
Query parameters:
|
||||
- assettype: Filter by asset type name (equipment, computer, network_device, printer)
|
||||
- subtype: Filter by subtype ID (machinetype for equipment/computer, networkdevicetype for network, printertype for printer)
|
||||
- assettype: Filter by asset type name (machine, computer, network_device, printer)
|
||||
- subtype: Filter by subtype ID (machinetype for machines, computertype for PCs, networkdevicetype for network, printertype for printer)
|
||||
- businessunitid: Filter by business unit ID
|
||||
- statusid: Filter by status ID
|
||||
- locationid: Filter by location ID
|
||||
@@ -720,26 +720,26 @@ def get_assets_map():
|
||||
|
||||
# Eager-load plugin extension tables AND their relationships
|
||||
try:
|
||||
from plugins.equipment.models import Equipment
|
||||
from plugins.machines.models import Machine
|
||||
eager_options.append(
|
||||
subqueryload(Asset.equipment)
|
||||
.joinedload(Equipment.equipmenttype)
|
||||
subqueryload(Asset.machine)
|
||||
.joinedload(Machine.machinetype)
|
||||
)
|
||||
eager_options.append(
|
||||
subqueryload(Asset.equipment)
|
||||
.joinedload(Equipment.vendor)
|
||||
subqueryload(Asset.machine)
|
||||
.joinedload(Machine.vendor)
|
||||
)
|
||||
eager_options.append(
|
||||
subqueryload(Asset.equipment)
|
||||
.joinedload(Equipment.model)
|
||||
subqueryload(Asset.machine)
|
||||
.joinedload(Machine.model)
|
||||
)
|
||||
eager_options.append(
|
||||
subqueryload(Asset.equipment)
|
||||
.joinedload(Equipment.controllervendor)
|
||||
subqueryload(Asset.machine)
|
||||
.joinedload(Machine.controllervendor)
|
||||
)
|
||||
eager_options.append(
|
||||
subqueryload(Asset.equipment)
|
||||
.joinedload(Equipment.controllermodel)
|
||||
subqueryload(Asset.machine)
|
||||
.joinedload(Machine.controllermodel)
|
||||
)
|
||||
except (ImportError, AttributeError):
|
||||
pass
|
||||
@@ -800,11 +800,11 @@ def get_assets_map():
|
||||
if subtype_id := request.args.get('subtype'):
|
||||
subtype_id = int(subtype_id)
|
||||
asset_type_lower = selected_assettype.lower() if selected_assettype else ''
|
||||
if asset_type_lower == 'equipment':
|
||||
if asset_type_lower == 'machine':
|
||||
try:
|
||||
from plugins.equipment.models import Equipment
|
||||
query = query.join(Equipment, Equipment.assetid == Asset.assetid).filter(
|
||||
Equipment.equipmenttypeid == subtype_id
|
||||
from plugins.machines.models import Machine
|
||||
query = query.join(Machine, Machine.assetid == Asset.assetid).filter(
|
||||
Machine.machinetypeid == subtype_id
|
||||
)
|
||||
except ImportError:
|
||||
pass
|
||||
@@ -926,11 +926,11 @@ def get_assets_map():
|
||||
subtypes = {}
|
||||
|
||||
try:
|
||||
from plugins.equipment.models import EquipmentType
|
||||
equipment_types = EquipmentType.query.filter(EquipmentType.isactive == True).order_by(EquipmentType.equipmenttype).all()
|
||||
subtypes['Equipment'] = [{'id': et.equipmenttypeid, 'name': et.equipmenttype, 'color': et.color} for et in equipment_types]
|
||||
from plugins.machines.models import MachineType
|
||||
machine_types = MachineType.query.filter(MachineType.isactive == True).order_by(MachineType.machinetype).all()
|
||||
subtypes['Machine'] = [{'id': mt.machinetypeid, 'name': mt.machinetype, 'color': mt.color} for mt in machine_types]
|
||||
except ImportError:
|
||||
subtypes['Equipment'] = []
|
||||
subtypes['Machine'] = []
|
||||
|
||||
try:
|
||||
from plugins.computers.models import ComputerType
|
||||
|
||||
@@ -11,7 +11,7 @@ dashboard_bp = Blueprint('dashboard', __name__)
|
||||
|
||||
# Map asset type name -> dashboard category label
|
||||
_TYPE_CATEGORY = {
|
||||
'equipment': 'Equipment',
|
||||
'machine': 'Machine',
|
||||
'computer': 'PC',
|
||||
'printer': 'Printer',
|
||||
'network_device': 'Network',
|
||||
@@ -30,11 +30,11 @@ def _count_by_type(assettype):
|
||||
@jwt_required(optional=True)
|
||||
def get_dashboard():
|
||||
"""Get dashboard summary data (asset-based)."""
|
||||
equipment_count = _count_by_type('equipment')
|
||||
machine_count = _count_by_type('machine')
|
||||
pc_count = _count_by_type('computer')
|
||||
network_count = _count_by_type('network_device')
|
||||
printer_count = _count_by_type('printer')
|
||||
total = equipment_count + pc_count + network_count + printer_count
|
||||
total = machine_count + pc_count + network_count + printer_count
|
||||
|
||||
# Count by status
|
||||
status_counts = db.session.query(
|
||||
@@ -52,17 +52,18 @@ def get_dashboard():
|
||||
).limit(10).all()
|
||||
|
||||
return success_response({
|
||||
# Fields expected by frontend
|
||||
'totalmachines': total,
|
||||
'totalequipment': equipment_count,
|
||||
# Fields expected by frontend (totalmachines now means machines,
|
||||
# totalassets is the grand total - renamed with the machines plugin)
|
||||
'totalassets': total,
|
||||
'totalmachines': machine_count,
|
||||
'totalpc': pc_count,
|
||||
'totalnetwork': network_count,
|
||||
'totalprinter': printer_count,
|
||||
'activemachines': status_dict.get('In Use', 0),
|
||||
'activeassets': status_dict.get('In Use', 0),
|
||||
'inrepair': status_dict.get('In Repair', 0),
|
||||
# Structured data
|
||||
'counts': {
|
||||
'equipment': equipment_count,
|
||||
'machines': machine_count,
|
||||
'pcs': pc_count,
|
||||
'networkdevices': network_count,
|
||||
'printers': printer_count,
|
||||
|
||||
@@ -1,153 +0,0 @@
|
||||
"""Machine Types API endpoints - Full CRUD."""
|
||||
|
||||
from flask import Blueprint, request
|
||||
from flask_jwt_extended import jwt_required, current_user
|
||||
|
||||
from shopdb.extensions import db
|
||||
from shopdb.core.models import MachineType
|
||||
from shopdb.utils.responses import (
|
||||
success_response,
|
||||
error_response,
|
||||
paginated_response,
|
||||
ErrorCodes
|
||||
)
|
||||
from shopdb.utils.pagination import get_pagination_params, paginate_query
|
||||
|
||||
from shopdb.utils.authz import require_permission, require_role
|
||||
|
||||
machinetypes_bp = Blueprint('machinetypes', __name__)
|
||||
|
||||
|
||||
@machinetypes_bp.route('', methods=['GET'])
|
||||
@jwt_required(optional=True)
|
||||
def list_machinetypes():
|
||||
"""List all machine types with optional filtering."""
|
||||
page, per_page = get_pagination_params(request)
|
||||
|
||||
query = MachineType.query
|
||||
|
||||
if request.args.get('active', 'true').lower() != 'false':
|
||||
query = query.filter(MachineType.isactive == True)
|
||||
|
||||
if category := request.args.get('category'):
|
||||
query = query.filter(MachineType.category == category)
|
||||
|
||||
if search := request.args.get('search'):
|
||||
query = query.filter(MachineType.machinetype.ilike(f'%{search}%'))
|
||||
|
||||
query = query.order_by(MachineType.machinetype)
|
||||
|
||||
items, total = paginate_query(query, page, per_page)
|
||||
data = [mt.to_dict() for mt in items]
|
||||
|
||||
return paginated_response(data, page, per_page, total)
|
||||
|
||||
|
||||
@machinetypes_bp.route('/<int:type_id>', methods=['GET'])
|
||||
@jwt_required(optional=True)
|
||||
def get_machinetype(type_id: int):
|
||||
"""Get a single machine type."""
|
||||
mt = db.session.get(MachineType, type_id)
|
||||
|
||||
if not mt:
|
||||
return error_response(
|
||||
ErrorCodes.NOT_FOUND,
|
||||
f'Machine type with ID {type_id} not found',
|
||||
http_code=404
|
||||
)
|
||||
|
||||
return success_response(mt.to_dict())
|
||||
|
||||
|
||||
@machinetypes_bp.route('', methods=['POST'])
|
||||
@jwt_required()
|
||||
@require_role('admin')
|
||||
def create_machinetype():
|
||||
"""Create a new machine type."""
|
||||
data = request.get_json()
|
||||
|
||||
if not data or not data.get('machinetype'):
|
||||
return error_response(ErrorCodes.VALIDATION_ERROR, 'machinetype is required')
|
||||
|
||||
if MachineType.query.filter_by(machinetype=data['machinetype']).first():
|
||||
return error_response(
|
||||
ErrorCodes.CONFLICT,
|
||||
f"Machine type '{data['machinetype']}' already exists",
|
||||
http_code=409
|
||||
)
|
||||
|
||||
mt = MachineType(
|
||||
machinetype=data['machinetype'],
|
||||
category=data.get('category', 'Equipment'),
|
||||
description=data.get('description'),
|
||||
icon=data.get('icon')
|
||||
)
|
||||
|
||||
db.session.add(mt)
|
||||
db.session.commit()
|
||||
|
||||
return success_response(mt.to_dict(), message='Machine type created', http_code=201)
|
||||
|
||||
|
||||
@machinetypes_bp.route('/<int:type_id>', methods=['PUT'])
|
||||
@jwt_required()
|
||||
@require_role('admin')
|
||||
def update_machinetype(type_id: int):
|
||||
"""Update a machine type."""
|
||||
mt = db.session.get(MachineType, type_id)
|
||||
|
||||
if not mt:
|
||||
return error_response(
|
||||
ErrorCodes.NOT_FOUND,
|
||||
f'Machine type with ID {type_id} not found',
|
||||
http_code=404
|
||||
)
|
||||
|
||||
data = request.get_json()
|
||||
if not data:
|
||||
return error_response(ErrorCodes.VALIDATION_ERROR, 'No data provided')
|
||||
|
||||
# Check duplicate name
|
||||
if 'machinetype' in data and data['machinetype'] != mt.machinetype:
|
||||
if MachineType.query.filter_by(machinetype=data['machinetype']).first():
|
||||
return error_response(
|
||||
ErrorCodes.CONFLICT,
|
||||
f"Machine type '{data['machinetype']}' already exists",
|
||||
http_code=409
|
||||
)
|
||||
|
||||
for key in ['machinetype', 'category', 'description', 'icon', 'isactive']:
|
||||
if key in data:
|
||||
setattr(mt, key, data[key])
|
||||
|
||||
db.session.commit()
|
||||
return success_response(mt.to_dict(), message='Machine type updated')
|
||||
|
||||
|
||||
@machinetypes_bp.route('/<int:type_id>', methods=['DELETE'])
|
||||
@jwt_required()
|
||||
@require_role('admin')
|
||||
def delete_machinetype(type_id: int):
|
||||
"""Delete (deactivate) a machine type."""
|
||||
mt = db.session.get(MachineType, type_id)
|
||||
|
||||
if not mt:
|
||||
return error_response(
|
||||
ErrorCodes.NOT_FOUND,
|
||||
f'Machine type with ID {type_id} not found',
|
||||
http_code=404
|
||||
)
|
||||
|
||||
# Check if any model uses this type
|
||||
from shopdb.core.models import Model
|
||||
if Model.query.filter_by(machinetypeid=type_id).first():
|
||||
return error_response(
|
||||
ErrorCodes.CONFLICT,
|
||||
'Cannot delete machine type: models are using it',
|
||||
http_code=409
|
||||
)
|
||||
|
||||
mt.isactive = False
|
||||
db.session.commit()
|
||||
|
||||
return success_response(message='Machine type deleted')
|
||||
@@ -1,4 +1,4 @@
|
||||
"""Models (equipment models) API endpoints - Full CRUD."""
|
||||
"""Models (vendor model catalog) API endpoints - Full CRUD."""
|
||||
|
||||
from flask import Blueprint, request
|
||||
from flask_jwt_extended import jwt_required
|
||||
@@ -21,7 +21,7 @@ models_bp = Blueprint('models', __name__)
|
||||
@models_bp.route('', methods=['GET'])
|
||||
@jwt_required(optional=True)
|
||||
def list_models():
|
||||
"""List all equipment models."""
|
||||
"""List all vendor catalog models."""
|
||||
page, per_page = get_pagination_params(request)
|
||||
|
||||
query = Model.query
|
||||
@@ -32,8 +32,8 @@ def list_models():
|
||||
if vendor_id := request.args.get('vendor', type=int):
|
||||
query = query.filter(Model.vendorid == vendor_id)
|
||||
|
||||
if machinetype_id := request.args.get('machinetype', type=int):
|
||||
query = query.filter(Model.machinetypeid == machinetype_id)
|
||||
if modeltype_id := request.args.get('modeltype', type=int):
|
||||
query = query.filter(Model.modeltypeid == modeltype_id)
|
||||
|
||||
if search := request.args.get('search'):
|
||||
query = query.filter(Model.modelnumber.ilike(f'%{search}%'))
|
||||
@@ -46,7 +46,7 @@ def list_models():
|
||||
for m in items:
|
||||
d = m.to_dict()
|
||||
d['vendor'] = m.vendor.vendor if m.vendor else None
|
||||
d['machinetype'] = m.machinetype.machinetype if m.machinetype else None
|
||||
d['modeltype'] = m.modeltype.modeltype if m.modeltype else None
|
||||
data.append(d)
|
||||
|
||||
return paginated_response(data, page, per_page, total)
|
||||
@@ -67,7 +67,7 @@ def get_model(model_id: int):
|
||||
|
||||
data = m.to_dict()
|
||||
data['vendor'] = m.vendor.to_dict() if m.vendor else None
|
||||
data['machinetype'] = m.machinetype.to_dict() if m.machinetype else None
|
||||
data['modeltype'] = m.modeltype.to_dict() if m.modeltype else None
|
||||
|
||||
return success_response(data)
|
||||
|
||||
@@ -97,7 +97,7 @@ def create_model():
|
||||
m = Model(
|
||||
modelnumber=data['modelnumber'],
|
||||
vendorid=data.get('vendorid'),
|
||||
machinetypeid=data.get('machinetypeid'),
|
||||
modeltypeid=data.get('modeltypeid'),
|
||||
description=data.get('description'),
|
||||
imageurl=data.get('imageurl'),
|
||||
documentationurl=data.get('documentationurl'),
|
||||
@@ -128,7 +128,7 @@ def update_model(model_id: int):
|
||||
if not data:
|
||||
return error_response(ErrorCodes.VALIDATION_ERROR, 'No data provided')
|
||||
|
||||
for key in ['modelnumber', 'vendorid', 'machinetypeid', 'description', 'imageurl', 'documentationurl', 'notes', 'isactive']:
|
||||
for key in ['modelnumber', 'vendorid', 'modeltypeid', 'description', 'imageurl', 'documentationurl', 'notes', 'isactive']:
|
||||
if key in data:
|
||||
setattr(m, key, data[key])
|
||||
|
||||
|
||||
157
shopdb/core/api/modeltypes.py
Normal file
157
shopdb/core/api/modeltypes.py
Normal file
@@ -0,0 +1,157 @@
|
||||
"""Model Types API endpoints - Full CRUD.
|
||||
|
||||
Types the vendor MODELS catalog (models.modeltypeid). Renamed from
|
||||
machinetypes; the machines plugin now owns the "machinetypes" name.
|
||||
"""
|
||||
|
||||
from flask import Blueprint, request
|
||||
from flask_jwt_extended import jwt_required, current_user
|
||||
|
||||
from shopdb.extensions import db
|
||||
from shopdb.core.models import ModelType
|
||||
from shopdb.utils.responses import (
|
||||
success_response,
|
||||
error_response,
|
||||
paginated_response,
|
||||
ErrorCodes
|
||||
)
|
||||
from shopdb.utils.pagination import get_pagination_params, paginate_query
|
||||
|
||||
from shopdb.utils.authz import require_permission, require_role
|
||||
|
||||
modeltypes_bp = Blueprint('modeltypes', __name__)
|
||||
|
||||
|
||||
@modeltypes_bp.route('', methods=['GET'])
|
||||
@jwt_required(optional=True)
|
||||
def list_modeltypes():
|
||||
"""List all model types with optional filtering."""
|
||||
page, per_page = get_pagination_params(request)
|
||||
|
||||
query = ModelType.query
|
||||
|
||||
if request.args.get('active', 'true').lower() != 'false':
|
||||
query = query.filter(ModelType.isactive == True)
|
||||
|
||||
if category := request.args.get('category'):
|
||||
query = query.filter(ModelType.category == category)
|
||||
|
||||
if search := request.args.get('search'):
|
||||
query = query.filter(ModelType.modeltype.ilike(f'%{search}%'))
|
||||
|
||||
query = query.order_by(ModelType.modeltype)
|
||||
|
||||
items, total = paginate_query(query, page, per_page)
|
||||
data = [mt.to_dict() for mt in items]
|
||||
|
||||
return paginated_response(data, page, per_page, total)
|
||||
|
||||
|
||||
@modeltypes_bp.route('/<int:type_id>', methods=['GET'])
|
||||
@jwt_required(optional=True)
|
||||
def get_modeltype(type_id: int):
|
||||
"""Get a single model type."""
|
||||
mt = db.session.get(ModelType, type_id)
|
||||
|
||||
if not mt:
|
||||
return error_response(
|
||||
ErrorCodes.NOT_FOUND,
|
||||
f'Model type with ID {type_id} not found',
|
||||
http_code=404
|
||||
)
|
||||
|
||||
return success_response(mt.to_dict())
|
||||
|
||||
|
||||
@modeltypes_bp.route('', methods=['POST'])
|
||||
@jwt_required()
|
||||
@require_role('admin')
|
||||
def create_modeltype():
|
||||
"""Create a new model type."""
|
||||
data = request.get_json()
|
||||
|
||||
if not data or not data.get('modeltype'):
|
||||
return error_response(ErrorCodes.VALIDATION_ERROR, 'modeltype is required')
|
||||
|
||||
if ModelType.query.filter_by(modeltype=data['modeltype']).first():
|
||||
return error_response(
|
||||
ErrorCodes.CONFLICT,
|
||||
f"Model type '{data['modeltype']}' already exists",
|
||||
http_code=409
|
||||
)
|
||||
|
||||
mt = ModelType(
|
||||
modeltype=data['modeltype'],
|
||||
category=data.get('category', 'Equipment'),
|
||||
description=data.get('description'),
|
||||
icon=data.get('icon')
|
||||
)
|
||||
|
||||
db.session.add(mt)
|
||||
db.session.commit()
|
||||
|
||||
return success_response(mt.to_dict(), message='Model type created', http_code=201)
|
||||
|
||||
|
||||
@modeltypes_bp.route('/<int:type_id>', methods=['PUT'])
|
||||
@jwt_required()
|
||||
@require_role('admin')
|
||||
def update_modeltype(type_id: int):
|
||||
"""Update a model type."""
|
||||
mt = db.session.get(ModelType, type_id)
|
||||
|
||||
if not mt:
|
||||
return error_response(
|
||||
ErrorCodes.NOT_FOUND,
|
||||
f'Model type with ID {type_id} not found',
|
||||
http_code=404
|
||||
)
|
||||
|
||||
data = request.get_json()
|
||||
if not data:
|
||||
return error_response(ErrorCodes.VALIDATION_ERROR, 'No data provided')
|
||||
|
||||
# Check duplicate name
|
||||
if 'modeltype' in data and data['modeltype'] != mt.modeltype:
|
||||
if ModelType.query.filter_by(modeltype=data['modeltype']).first():
|
||||
return error_response(
|
||||
ErrorCodes.CONFLICT,
|
||||
f"Model type '{data['modeltype']}' already exists",
|
||||
http_code=409
|
||||
)
|
||||
|
||||
for key in ['modeltype', 'category', 'description', 'icon', 'isactive']:
|
||||
if key in data:
|
||||
setattr(mt, key, data[key])
|
||||
|
||||
db.session.commit()
|
||||
return success_response(mt.to_dict(), message='Model type updated')
|
||||
|
||||
|
||||
@modeltypes_bp.route('/<int:type_id>', methods=['DELETE'])
|
||||
@jwt_required()
|
||||
@require_role('admin')
|
||||
def delete_modeltype(type_id: int):
|
||||
"""Delete (deactivate) a model type."""
|
||||
mt = db.session.get(ModelType, type_id)
|
||||
|
||||
if not mt:
|
||||
return error_response(
|
||||
ErrorCodes.NOT_FOUND,
|
||||
f'Model type with ID {type_id} not found',
|
||||
http_code=404
|
||||
)
|
||||
|
||||
# Check if any model uses this type
|
||||
from shopdb.core.models import Model
|
||||
if Model.query.filter_by(modeltypeid=type_id).first():
|
||||
return error_response(
|
||||
ErrorCodes.CONFLICT,
|
||||
'Cannot delete model type: models are using it',
|
||||
http_code=409
|
||||
)
|
||||
|
||||
mt.isactive = False
|
||||
db.session.commit()
|
||||
|
||||
return success_response(message='Model type deleted')
|
||||
File diff suppressed because it is too large
Load Diff
@@ -110,8 +110,8 @@ def _get_asset_result(asset, query, relevance=None):
|
||||
asset_type_name = asset.assettype.assettype if asset.assettype else 'asset'
|
||||
|
||||
plugin_id = asset.assetid
|
||||
if asset_type_name == 'equipment' and hasattr(asset, 'equipment') and asset.equipment:
|
||||
plugin_id = asset.equipment.equipmentid
|
||||
if asset_type_name == 'machine' and hasattr(asset, 'machine') and asset.machine:
|
||||
plugin_id = asset.machine.machineid
|
||||
elif asset_type_name == 'computer' and hasattr(asset, 'computer') and asset.computer:
|
||||
plugin_id = asset.computer.computerid
|
||||
elif asset_type_name == 'network_device' and hasattr(asset, 'network_device') and asset.network_device:
|
||||
@@ -120,7 +120,7 @@ def _get_asset_result(asset, query, relevance=None):
|
||||
plugin_id = asset.printer.printerid
|
||||
|
||||
url_map = {
|
||||
'equipment': f"/machines/{plugin_id}",
|
||||
'machine': f"/machines/{plugin_id}",
|
||||
'computer': f"/pcs/{plugin_id}",
|
||||
'network_device': f"/network/{plugin_id}",
|
||||
'printer': f"/printers/{plugin_id}",
|
||||
@@ -478,21 +478,21 @@ def _search_notifications(query, search_term):
|
||||
|
||||
|
||||
def _search_vendor_model_type(query, search_term):
|
||||
"""Search assets by vendor name, model name, or equipment/device type name."""
|
||||
"""Search assets by vendor name, model name, or machine/device type name."""
|
||||
results = []
|
||||
|
||||
# Equipment: vendor, model, equipmenttype
|
||||
# Machines: vendor, model, machinetype
|
||||
try:
|
||||
_require_enabled('equipment')
|
||||
from plugins.equipment.models import Equipment, EquipmentType
|
||||
equipment_assets = db.session.query(Asset).join(
|
||||
Equipment, Equipment.assetid == Asset.assetid
|
||||
_require_enabled('machines')
|
||||
from plugins.machines.models import Machine, MachineType
|
||||
machine_assets = db.session.query(Asset).join(
|
||||
Machine, Machine.assetid == Asset.assetid
|
||||
).outerjoin(
|
||||
Vendor, Equipment.vendorid == Vendor.vendorid
|
||||
Vendor, Machine.vendorid == Vendor.vendorid
|
||||
).outerjoin(
|
||||
Model, Equipment.modelnumberid == Model.modelnumberid
|
||||
Model, Machine.modelnumberid == Model.modelnumberid
|
||||
).outerjoin(
|
||||
EquipmentType, Equipment.equipmenttypeid == EquipmentType.equipmenttypeid
|
||||
MachineType, Machine.machinetypeid == MachineType.machinetypeid
|
||||
).options(
|
||||
joinedload(Asset.assettype),
|
||||
joinedload(Asset.location),
|
||||
@@ -501,16 +501,16 @@ def _search_vendor_model_type(query, search_term):
|
||||
db.or_(
|
||||
Vendor.vendor.ilike(search_term),
|
||||
Model.modelnumber.ilike(search_term),
|
||||
EquipmentType.equipmenttype.ilike(search_term)
|
||||
MachineType.machinetype.ilike(search_term)
|
||||
)
|
||||
).limit(10).all()
|
||||
|
||||
for asset in equipment_assets:
|
||||
for asset in machine_assets:
|
||||
results.append(_get_asset_result(asset, query, 30))
|
||||
except ImportError:
|
||||
pass
|
||||
except Exception as e:
|
||||
logger.error(f"Equipment vendor/model/type search failed: {e}")
|
||||
logger.error(f"Machine vendor/model/type search failed: {e}")
|
||||
|
||||
# Printers: vendor, model, printertype
|
||||
try:
|
||||
|
||||
@@ -53,7 +53,7 @@ IDENTIFIER_LABELS = {
|
||||
'maintenancereference': 'Maintenance Reference',
|
||||
'fqdn': 'FQDN / hostname',
|
||||
}
|
||||
IDENTIFIER_ASSETTYPES = ['equipment', 'computer', 'printer', 'network_device']
|
||||
IDENTIFIER_ASSETTYPES = ['machine', 'computer', 'printer', 'network_device']
|
||||
|
||||
# Global-search result types that can be toggled on/off independently of whether
|
||||
# the owning plugin is enabled. Keys match the `type` field on search results;
|
||||
@@ -63,7 +63,7 @@ SEARCH_DOMAINS = {
|
||||
'application': 'Applications',
|
||||
'knowledgebase': 'Knowledge Base',
|
||||
'employee': 'Employees',
|
||||
'equipment': 'Equipment',
|
||||
'machine': 'Machines',
|
||||
'computer': 'PCs',
|
||||
'printer': 'Printers',
|
||||
'network_device': 'Network Devices',
|
||||
@@ -450,7 +450,7 @@ def build_default_settings():
|
||||
'value': '/ge-aerospace-logo.svg',
|
||||
'valuetype': 'string',
|
||||
'category': 'branding',
|
||||
'description': 'Logo shown on the equipment badge print page'
|
||||
'description': 'Logo shown on the machine badge print page'
|
||||
},
|
||||
{
|
||||
'key': 'site_favicon',
|
||||
|
||||
Reference in New Issue
Block a user