Files
shopdb-flask/plugins/warranty/plugin.py
Cam Proudlock a9d0dfff4a Initial import: shopdb-flask multi-site asset platform
Flask + Vue rewrite of the classic-ASP shopdb, built as a plugin
framework so sister GE Aerospace sites can adopt it. Product version
0.7.0-pre-0.8.0, plugin contract 0.11.0, 966 backend tests.

Key entry points:
- docs/INSTALL-WINDOWS-IIS.md - tested Windows + IIS install runbook
  (own-site and subpath deployment methods)
- docs/PILOT-DEPLOY.md - production pilot: stand up + import + verify
- docs/IMPORT-API.md - schema-agnostic legacy data import contract
- scripts/site_imports/wjf/ - West Jefferson reference import loader
- docs/adr/ - twelve accepted architecture decision records
2026-07-14 07:58:51 -04:00

102 lines
3.3 KiB
Python

"""Warranty plugin main class.
Asset-general plugin: owns its warranties + warrantyassets tables, an API
surface, and a sidebar entry. Not tied to any one asset type - a warranty can
cover a PC, printer, network device, or machine.
"""
import json
import logging
from pathlib import Path
from typing import List, Dict, Optional, Type
from flask import Flask, Blueprint
from shopdb.plugins.base import BasePlugin, PluginMeta
from .api import warranty_bp
from .models import Warranty, WarrantyAsset
logger = logging.getLogger(__name__)
class WarrantyPlugin(BasePlugin):
"""Warranty tracking plugin."""
def __init__(self):
self._manifest = self._load_manifest()
def _load_manifest(self) -> Dict:
manifest_path = Path(__file__).parent / 'manifest.json'
if manifest_path.exists():
with open(manifest_path, 'r') as f:
return json.load(f)
return {}
@property
def meta(self) -> PluginMeta:
return PluginMeta(
name=self._manifest.get('name', 'warranty'),
version=self._manifest.get('version', '1.0.0'),
description=self._manifest.get('description', 'Asset warranty tracking'),
author=self._manifest.get('author', 'ShopDB Team'),
dependencies=self._manifest.get('dependencies', []),
core_version=self._manifest.get('core_version', '>=0.2.0,<1.0.0'),
api_prefix=self._manifest.get('api_prefix', '/api/warranty'),
)
def get_blueprint(self) -> Optional[Blueprint]:
return warranty_bp
def get_models(self) -> List[Type]:
return [Warranty, WarrantyAsset]
def get_navigation_items(self) -> List[Dict]:
return [
{
'name': 'Warranties',
'icon': 'shield',
'route': '/warranties',
'position': 8,
},
]
def get_reports(self) -> List[Dict]:
return [
{
'id': 'warranty',
'name': 'Warranty Report',
'description': 'Assets bucketed by coverage: expired, expiring soon, active',
'category': 'warranty',
'route': '/reports/warranty',
},
]
def get_asset_panels(self) -> List[Dict]:
# ADR-010 pilot. Warranty is asset-general so it shows on every detail
# page (['*']). Declares the existing per-asset warranty endpoint the
# WarrantyPanel already reads; the generic core panel renderer consumes
# this instead of each detail view hand-composing the panel.
return [
{
'id': 'warranty',
'title': 'Warranty',
'assettypes': ['*'],
'endpoint': '/api/warranty/asset/{assetid}',
'render': 'table',
'position': 30,
},
]
def get_permissions(self) -> List:
"""Return the RBAC permissions this plugin owns."""
return [
('warranty.view', 'View warranties', 'warranty'),
('warranty.create', 'Create warranties', 'warranty'),
('warranty.edit', 'Edit warranties', 'warranty'),
('warranty.delete', 'Delete warranties', 'warranty'),
]
def init_app(self, app: Flask, db_instance) -> None:
logger.info(f"Warranty plugin initialized (v{self.meta.version})")