Asset-side printer save, status CRUD, real printer types, network fix
- Printers save through the asset blueprint (PUT /printers) instead of the legacy machines API; restrict supply-model picker to printer models. - Asset statuses get full CRUD (PUT/DELETE with in-use guard); canonical set. - Printer types reseeded to a real classification set + list filter. - Equipment accepts gauge/maintenance references. - Fix network list emitting network_device instead of networkdevice (View 404). Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
@@ -42,7 +42,7 @@ def seed_cli():
|
||||
def seed_reference_data():
|
||||
"""Seed reference data (machine types, statuses, etc.)."""
|
||||
from shopdb.extensions import db
|
||||
from shopdb.core.models import MachineType, MachineStatus, OperatingSystem
|
||||
from shopdb.core.models import MachineType, MachineStatus, OperatingSystem, AssetStatus
|
||||
from shopdb.core.models.relationship import RelationshipType
|
||||
|
||||
# Machine types
|
||||
@@ -82,6 +82,24 @@ def seed_reference_data():
|
||||
s = MachineStatus(**s_data)
|
||||
db.session.add(s)
|
||||
|
||||
# Asset statuses (canonical set - the asset model is the contract)
|
||||
asset_statuses = [
|
||||
{'status': 'In Use', 'description': 'Currently in use', 'color': '#28a745'},
|
||||
{'status': 'Inventory', 'description': 'In inventory', 'color': '#17a2b8'},
|
||||
{'status': 'In Repair', 'description': 'Being repaired', 'color': '#ffc107'},
|
||||
{'status': 'Retired', 'description': 'No longer in use', 'color': '#6c757d'},
|
||||
{'status': 'Returned', 'description': 'Returned to vendor or owner', 'color': '#fd7e14'},
|
||||
{'status': 'Warrantied', 'description': 'Under warranty service', 'color': '#20c997'},
|
||||
{'status': 'Lost', 'description': 'Lost or missing', 'color': '#dc3545'},
|
||||
]
|
||||
|
||||
for s_data in asset_statuses:
|
||||
existing = AssetStatus.query.filter_by(status=s_data['status']).first()
|
||||
if not existing:
|
||||
db.session.add(AssetStatus(isactive=True, **s_data))
|
||||
elif existing.isactive is not True:
|
||||
existing.isactive = True
|
||||
|
||||
# Operating systems
|
||||
os_list = [
|
||||
{'osname': 'Windows 10', 'osversion': '10.0'},
|
||||
|
||||
@@ -53,6 +53,7 @@ class Config:
|
||||
|
||||
LOG_LEVEL = os.environ.get('LOG_LEVEL', 'INFO')
|
||||
|
||||
ZABBIX_ENABLED = os.environ.get('ZABBIX_ENABLED', 'false').lower() == 'true'
|
||||
ZABBIX_URL = os.environ.get('ZABBIX_URL', '')
|
||||
ZABBIX_TOKEN = os.environ.get('ZABBIX_TOKEN', '')
|
||||
|
||||
|
||||
@@ -153,6 +153,56 @@ def create_asset_status():
|
||||
return success_response(s.to_dict(), message='Asset status created', http_code=201)
|
||||
|
||||
|
||||
@assets_bp.route('/statuses/<int:status_id>', methods=['PUT'])
|
||||
@jwt_required()
|
||||
def update_asset_status(status_id: int):
|
||||
"""Update an asset status."""
|
||||
s = AssetStatus.query.get(status_id)
|
||||
if not s:
|
||||
return error_response(ErrorCodes.NOT_FOUND, 'Asset status not found',
|
||||
http_code=404)
|
||||
|
||||
data = request.get_json() or {}
|
||||
|
||||
# Conflict check on rename
|
||||
if 'status' in data and data['status'] != s.status:
|
||||
if AssetStatus.query.filter_by(status=data['status']).first():
|
||||
return error_response(
|
||||
ErrorCodes.CONFLICT,
|
||||
f"Asset status '{data['status']}' already exists",
|
||||
http_code=409
|
||||
)
|
||||
|
||||
for key in ('status', 'description', 'color', 'isactive'):
|
||||
if key in data:
|
||||
setattr(s, key, data[key])
|
||||
|
||||
db.session.commit()
|
||||
return success_response(s.to_dict(), message='Asset status updated')
|
||||
|
||||
|
||||
@assets_bp.route('/statuses/<int:status_id>', methods=['DELETE'])
|
||||
@jwt_required()
|
||||
def delete_asset_status(status_id: int):
|
||||
"""Delete an asset status. Refused if any asset still uses it."""
|
||||
s = AssetStatus.query.get(status_id)
|
||||
if not s:
|
||||
return error_response(ErrorCodes.NOT_FOUND, 'Asset status not found',
|
||||
http_code=404)
|
||||
|
||||
inuse = Asset.query.filter_by(statusid=status_id).count()
|
||||
if inuse:
|
||||
return error_response(
|
||||
ErrorCodes.CONFLICT,
|
||||
f"Cannot delete: {inuse} asset(s) still use this status",
|
||||
http_code=409
|
||||
)
|
||||
|
||||
db.session.delete(s)
|
||||
db.session.commit()
|
||||
return success_response(message='Asset status deleted')
|
||||
|
||||
|
||||
# =============================================================================
|
||||
# Assets
|
||||
# =============================================================================
|
||||
|
||||
@@ -35,7 +35,7 @@ PLUGIN_TABLE_OWNERS: dict[str, Iterable[str]] = {
|
||||
'equipment': ('equipmenttypes', 'equipment'),
|
||||
'network': ('networkdevicetypes', 'networkdevices', 'vlans', 'subnets'),
|
||||
'notifications': ('notificationtypes', 'notifications'),
|
||||
'printers': ('printertypes', 'printers', 'printerdata'),
|
||||
'printers': ('printertypes', 'printers', 'printerdata', 'modelsupplies'),
|
||||
'usb': ('usbdevicetypes', 'usbdevices', 'usbcheckouts'),
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user