Migrate frontend to plugin-based asset architecture

- Add equipmentApi and computersApi to replace legacy machinesApi
- Add controller vendor/model fields to Equipment model and forms
- Fix map marker navigation to use plugin-specific IDs (equipmentid,
  computerid, printerid, networkdeviceid) instead of assetid
- Fix search to use unified Asset table with correct plugin IDs
- Remove legacy printer search that used non-existent field names
- Enable optional JWT auth for detail endpoints (public read access)
- Clean up USB plugin models (remove unused checkout model)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
cproudlock
2026-01-29 16:07:41 -05:00
parent 9c220a4194
commit c3ce69da12
28 changed files with 4123 additions and 3454 deletions

View File

@@ -393,7 +393,7 @@ def lookup_asset_by_number(assetnumber: str):
# =============================================================================
@assets_bp.route('/<int:asset_id>/relationships', methods=['GET'])
@jwt_required()
@jwt_required(optional=True)
def get_asset_relationships(asset_id: int):
"""
Get all relationships for an asset.
@@ -521,7 +521,7 @@ def delete_asset_relationship(rel_id: int):
# =============================================================================
@assets_bp.route('/map', methods=['GET'])
@jwt_required()
@jwt_required(optional=True)
def get_assets_map():
"""
Get all assets with map positions for unified floor map display.
@@ -529,13 +529,14 @@ def get_assets_map():
Returns assets with mapleft/maptop coordinates, joined with type-specific data.
Query parameters:
- assettype: Filter by asset type name (equipment, computer, network, printer)
- 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)
- businessunitid: Filter by business unit ID
- statusid: Filter by status ID
- locationid: Filter by location ID
- search: Search by assetnumber, name, or serialnumber
"""
from shopdb.core.models import Location, BusinessUnit
from shopdb.core.models import Location, BusinessUnit, MachineType, Machine
query = Asset.query.filter(
Asset.isactive == True,
@@ -543,10 +544,52 @@ def get_assets_map():
Asset.maptop.isnot(None)
)
selected_assettype = request.args.get('assettype')
# Filter by asset type name
if assettype := request.args.get('assettype'):
types = assettype.split(',')
query = query.join(AssetType).filter(AssetType.assettype.in_(types))
if selected_assettype:
query = query.join(AssetType).filter(AssetType.assettype == selected_assettype)
# Filter by subtype (depends on asset type) - case-insensitive matching
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':
# Filter by equipment type
try:
from plugins.equipment.models import Equipment
query = query.join(Equipment, Equipment.assetid == Asset.assetid).filter(
Equipment.equipmenttypeid == subtype_id
)
except ImportError:
pass
elif asset_type_lower == 'computer':
# Filter by computer type
try:
from plugins.computers.models import Computer
query = query.join(Computer, Computer.assetid == Asset.assetid).filter(
Computer.computertypeid == subtype_id
)
except ImportError:
pass
elif asset_type_lower == 'network device':
# Filter by network device type
try:
from plugins.network.models import NetworkDevice
query = query.join(NetworkDevice, NetworkDevice.assetid == Asset.assetid).filter(
NetworkDevice.networkdevicetypeid == subtype_id
)
except ImportError:
pass
elif asset_type_lower == 'printer':
# Filter by printer type
try:
from plugins.printers.models import Printer
query = query.join(Printer, Printer.assetid == Asset.assetid).filter(
Printer.printertypeid == subtype_id
)
except ImportError:
pass
# Filter by business unit
if bu_id := request.args.get('businessunitid'):
@@ -618,6 +661,41 @@ def get_assets_map():
locations = Location.query.filter(Location.isactive == True).all()
loc_data = [{'locationid': loc.locationid, 'locationname': loc.locationname} for loc in locations]
# Get subtypes based on asset type categories (keys match database asset type values)
subtypes = {}
# Equipment types from equipment plugin
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} for et in equipment_types]
except ImportError:
subtypes['Equipment'] = []
# Computer types from computers plugin
try:
from plugins.computers.models import ComputerType
computer_types = ComputerType.query.filter(ComputerType.isactive == True).order_by(ComputerType.computertype).all()
subtypes['Computer'] = [{'id': ct.computertypeid, 'name': ct.computertype} for ct in computer_types]
except ImportError:
subtypes['Computer'] = []
# Network device types
try:
from plugins.network.models import NetworkDeviceType
net_types = NetworkDeviceType.query.filter(NetworkDeviceType.isactive == True).order_by(NetworkDeviceType.networkdevicetype).all()
subtypes['Network Device'] = [{'id': nt.networkdevicetypeid, 'name': nt.networkdevicetype} for nt in net_types]
except ImportError:
subtypes['Network Device'] = []
# Printer types
try:
from plugins.printers.models import PrinterType
printer_types = PrinterType.query.filter(PrinterType.isactive == True).order_by(PrinterType.printertype).all()
subtypes['Printer'] = [{'id': pt.printertypeid, 'name': pt.printertype} for pt in printer_types]
except ImportError:
subtypes['Printer'] = []
return success_response({
'assets': data,
'total': len(data),
@@ -625,7 +703,8 @@ def get_assets_map():
'assettypes': types_data,
'statuses': status_data,
'businessunits': bu_data,
'locations': loc_data
'locations': loc_data,
'subtypes': subtypes
}
})