PrintedItem (catalog: code, name, image, cached quantityonhand, per-item threshold, bin) and PrintedItemTransaction (the ledger: signed quantity change attributed to a badge-resolved employee). Both registered in PLUGIN_TABLE_OWNERS; 0001 is a post-cutover real baseline. The migration-guard test learns the new expected head. Routes are a placeholder ping until the next stage - the scaffold's list route imported the deleted scaffold model, which surfaces as an empty 'Migration error' because the alembic env imports the models package.
73 lines
2.6 KiB
Python
73 lines
2.6 KiB
Python
"""Printedparts plugin main class.
|
|
|
|
3D-printed parts inventory + kiosk checkout. Quantity-based consumables:
|
|
one row is a KIND of part with a count, not an individually tracked asset,
|
|
so unlike most plugins this one seeds NO AssetType (ADR-001 assets are
|
|
one-row-per-physical-thing). See docs/proposals/printedparts-plugin.md.
|
|
"""
|
|
|
|
import json
|
|
import logging
|
|
from pathlib import Path
|
|
from typing import List, Optional, Type
|
|
|
|
from flask import Flask, Blueprint
|
|
|
|
from shopdb.plugins.base import BasePlugin, PluginMeta
|
|
from shopdb.api import db, Setting
|
|
|
|
from .models import PrintedItem, PrintedItemTransaction
|
|
from .api import printedparts_bp
|
|
|
|
logger = logging.getLogger(__name__)
|
|
|
|
|
|
class PrintedpartsPlugin(BasePlugin):
|
|
"""3D-printed parts inventory + kiosk checkout."""
|
|
|
|
def __init__(self):
|
|
manifest_path = Path(__file__).parent / 'manifest.json'
|
|
with open(manifest_path) as f:
|
|
self._manifest = json.load(f)
|
|
|
|
@property
|
|
def meta(self) -> PluginMeta:
|
|
return PluginMeta(
|
|
name=self._manifest['name'],
|
|
version=self._manifest['version'],
|
|
description=self._manifest['description'],
|
|
author=self._manifest.get('author', ''),
|
|
dependencies=self._manifest.get('dependencies', []),
|
|
core_version=self._manifest.get('core_version', '>=0.1.0'),
|
|
api_prefix=self._manifest.get('api_prefix'),
|
|
)
|
|
|
|
def get_blueprint(self) -> Optional[Blueprint]:
|
|
return printedparts_bp
|
|
|
|
def get_models(self) -> List[Type]:
|
|
return [PrintedItem, PrintedItemTransaction]
|
|
|
|
def init_app(self, app: Flask, db_instance) -> None:
|
|
logger.info(f'Printedparts plugin initialized (v{self.meta.version})')
|
|
|
|
def on_install(self, app: Flask) -> None:
|
|
with app.app_context():
|
|
self._seed_settings()
|
|
logger.info('Printedparts plugin installed')
|
|
|
|
def _seed_settings(self) -> None:
|
|
defaults = [
|
|
('printedparts_code_prefix', '3DP', 'string',
|
|
'Prefix for generated item codes'),
|
|
('printedparts_default_threshold', '5', 'integer',
|
|
'Default low-stock threshold for new items'),
|
|
('printedparts_unknown_badge', 'deny', 'string',
|
|
'Kiosk policy when a badge resolves to no employee: allow or deny'),
|
|
]
|
|
for key, value, valuetype, description in defaults:
|
|
if Setting.get(key) is None:
|
|
Setting.set(key, value, valuetype=valuetype,
|
|
category='printedparts', description=description)
|
|
db.session.commit()
|