Files
leocrm/app/plugins/builtins/contacts/plugin.py
T

167 lines
6.5 KiB
Python
Raw Normal View History

"""Contacts plugin — Core CRM entity lifecycle management.
Registers Contact entity models, permissions, restore config, and history hooks
via the same plugin lifecycle as all other business plugins. No Core special case.
"""
from __future__ import annotations
import logging
from app.plugins.base import BasePlugin
from app.plugins.manifest import (
FrontendDashboardWidget,
FrontendMenuItem,
FrontendPageRoute,
MiniAppContribution,
PluginManifest,
PluginRouteDef,
)
logger = logging.getLogger(__name__)
class ContactsPlugin(BasePlugin):
"""Contacts plugin — owns the full Contact domain (Block B1).
Routes (contacts, companies, contact folders, folder permissions) live in
this plugin and are mounted via manifest.routes with
require_active_plugin("contacts") protection. Entity lifecycle
(permissions, entity models, restore, history) is managed through
on_activate/on_deactivate like every other business plugin.
"""
manifest = PluginManifest(
name="contacts",
version="1.1.0",
display_name="Contacts",
description="Core CRM contacts — persons and companies.",
dependencies=[],
routes=[
PluginRouteDef(
path="/api/v1/contacts",
module="app.plugins.builtins.contacts.routes",
router_attr="router",
),
PluginRouteDef(
path="/api/v1/companies",
module="app.plugins.builtins.contacts.company_routes",
router_attr="router",
),
PluginRouteDef(
path="/api/v1/contact-folders",
module="app.plugins.builtins.contacts.folder_routes",
router_attr="router",
),
PluginRouteDef(
path="/api/v1/contact-folders",
module="app.plugins.builtins.contacts.folder_permission_routes",
router_attr="router",
),
],
events=[],
migrations=[],
miniapps=[
MiniAppContribution(
app_id="contacts_stats",
name="Kontakt-Zähler",
icon="Building2",
description="Firmen- und Kontakt-Zähler (persönliche StatCards).",
permission="contacts:read",
settings_schema={
"fields": [
{"name": "show_companies", "label": "Firmen anzeigen", "type": "boolean", "default": True},
{"name": "show_persons", "label": "Personen anzeigen", "type": "boolean", "default": True},
]
},
col_span=2,
row_span=1,
hosts=["chat", "dashboard", "window"],
component="@/components/dashboard/ContactsStatsWidget",
order=5,
),
],
dashboard_widgets=[
FrontendDashboardWidget(
id="recent_contacts",
label_key="dashboard.recentContacts",
label="Recent Contacts",
component="@/components/dashboard/RecentContactsWidget",
icon="Users",
order=10,
col_span=2,
permission="contacts:read",
),
],
menu_items=[
FrontendMenuItem(label_key='nav.contacts', label='Kontakte', path='/contacts', icon='Users', order=10, permission='contacts:read'),
],
page_routes=[
FrontendPageRoute(path='/contacts', component='@/pages/ContactsList', protected=True, permission='contacts:read'),
FrontendPageRoute(path='/contacts/:id', component='@/pages/ContactDetailPage', protected=True, permission='contacts:read'),
FrontendPageRoute(path='/contacts/dedup', component='@/pages/DedupMergePage', protected=True, permission='contacts:read'),
],
permissions=[
"contacts:read",
"contacts:write",
"contacts:delete",
],
is_core=True,
author="LeoCRM Team",
min_app_version="1.0.0",
contract_version="1.0.0",
)
def get_entity_models(self) -> dict[str, type]:
from app.models.contact import Contact
return {
"contact": Contact,
"contacts": Contact,
"company": Contact,
}
async def on_activate(self, db, service_container, event_bus) -> None:
"""Activate: register restore config + history hooks for Contact."""
await super().on_activate(db, service_container, event_bus)
# Register restore config for Contact entities
from app.core.restore_registry import RestoreConfig, get_restore_registry
from app.models.contact import Contact
get_restore_registry().register(RestoreConfig(
entity_type="contact",
model_class=Contact,
restore_permission="contacts:write",
excluded_fields=frozenset({
"search_tsv", "embedding", "default_person_id", "admin_contactperson_id",
}),
))
# Register history hooks for Contact entities
from app.core.history_hooks import register_history_hooks
from app.core.hooks import get_hook_registry
register_history_hooks(
get_hook_registry(), "contact",
"contact.after_create", "contact.after_update", "contact.after_delete",
owner_tag="contacts",
)
logger.info("Contacts plugin activated: restore + history registered")
async def on_deactivate(self, db, service_container, event_bus) -> None:
"""Deactivate: unregister restore config + history hooks for Contact."""
from app.core.restore_registry import get_restore_registry
get_restore_registry().unregister("contact")
# Unregister history hooks — use owner_tag to remove only this plugin's hooks
from app.core.hooks import get_hook_registry
hook_reg = get_hook_registry()
hook_reg.unregister_actions_by_owner("contact.after_create", "contacts")
hook_reg.unregister_actions_by_owner("contact.after_update", "contacts")
hook_reg.unregister_actions_by_owner("contact.after_delete", "contacts")
from app.plugins.builtins.contracts import get_contract_registry
get_contract_registry().unregister(self.manifest.name)
await super().on_deactivate(db, service_container, event_bus)
logger.info("Contacts plugin deactivated: restore + history unregistered")