feat(dokuwiki): Add partner fields, editable smileys, category icon, and mass sync wizard

- Add partner_id and partner_ref template placeholders for supplier information
- Implement editable status smiley field in maintenance.equipment.status
- Add UI views for editing smileys in Odoo (Wartung → Konfiguration → Equipment Status)
- Fix category field bug: use ows_category instead of category_id for security category
- Add category_icon placeholder displaying emoji indicators (🔴/🟡/🟢)
- Implement mass synchronization wizard (Wartung → Konfiguration → Wiki-Synchronisation)
  - Sync all equipment or filter by area
  - Sync only unsynchronized equipment
  - Force-sync option to overwrite existing documentation pages
  - Statistics display with error reporting
- Add security access rules for wizard
- Update README with new placeholders
This commit is contained in:
Matthias Lotz 2025-12-14 20:55:21 +01:00
parent 3fa050f153
commit a5f8fd32c3
11 changed files with 241 additions and 4 deletions

View File

@ -7,12 +7,15 @@ Basis-Felder (maintenance.equipment):
{model} - Modell
{category} - Kategoriename
{status} - Status (aus status_id)
{status_smiley} - Status als Smiley (aus status_id.smiley Feld, z.B. :-) oder :-()
{location} - Standort
{ows_area} - Bereichsname
{assign_date} - Zuweisungsdatum (formatiert)
{cost} - Kosten
{warranty_date} - Garantiedatum (formatiert)
{note} - Notizen
{partner_id} - Lieferant (Name)
{partner_ref} - Lieferanten-Referenz
Spezial-Felder:
{view_type} - "Bereich" oder "Einsatzzweck"
@ -32,4 +35,5 @@ ows.machine Felder (falls verknüpft):
{ows_machine_id.serial_no} - Seriennummer
{ows_machine_id.location} - Standort
{ows_machine_id.note} - Notizen
{ows_machine_id.category} - Kategorie
{ows_machine_id.category} - Sicherheitskategorie (red/yellow/green)
{ows_machine_id.category_icon} - Kategorie-Icon als Emoji (🔴/🟡/🟢)

View File

@ -1,3 +1,4 @@
# -*- coding: utf-8 -*-
from . import models
from . import wizard
from .hooks import post_init_hook

View File

@ -37,13 +37,17 @@ ACL:
'depends': [
'open_workshop_base',
'maintenance',
'maintenance_equipment_status',
],
'external_dependencies': {
'python': ['dokuwiki'],
},
'data': [
'security/ir.model.access.csv',
'data/ir_config_parameter.xml',
'views/maintenance_equipment_views.xml',
'views/maintenance_equipment_status_views.xml',
'wizard/equipment_wiki_sync_wizard_views.xml',
],
'post_init_hook': 'post_init_hook',
'installable': True,

View File

@ -1,4 +1,5 @@
# -*- coding: utf-8 -*-
from . import dokuwiki_client
from . import maintenance_equipment
from . import maintenance_equipment_status
from . import res_config_settings

View File

@ -222,7 +222,7 @@ class MaintenanceEquipment(models.Model):
values = {
# Spezielle Werte
'wiki_doku_page': doku_page_id,
'wiki_doku_link': f"[[{doku_page_id}|✏️ Zentrale Dokumentation bearbeiten]]",
'wiki_doku_link': f"[[{doku_page_id}| Wiki Dokumentation]]",
'odoo_link': f"[[{odoo_equipment_url}|🔗 In Odoo öffnen]]",
'odoo_url': odoo_equipment_url,
'sync_datetime': datetime.now().strftime('%d.%m.%Y %H:%M'),
@ -234,12 +234,15 @@ class MaintenanceEquipment(models.Model):
'ows_area': self.ows_area_id.name if self.ows_area_id else '',
'category': self.category_id.name if self.category_id else '',
'status': self.status_id.name if self.status_id else '',
'status_smiley': self.status_id.smiley if self.status_id and self.status_id.smiley else '',
'location': self.location or '',
'assign_date': self.assign_date.strftime('%d.%m.%Y') if self.assign_date else '',
'cost': str(self.cost) if self.cost else '',
'warranty_date': self.warranty_date.strftime('%d.%m.%Y') if self.warranty_date else '',
'color': str(self.color) if self.color else '',
'note': self.note or '',
'partner_id': self.partner_id.name if self.partner_id else '',
'partner_ref': self.partner_ref or '',
}
# ows.machine Felder hinzufügen (falls verknüpft)
@ -259,8 +262,10 @@ class MaintenanceEquipment(models.Model):
ows_machine_fields['ows_machine_id.location'] = machine.location
if hasattr(machine, 'note'):
ows_machine_fields['ows_machine_id.note'] = machine.note or ''
if hasattr(machine, 'category_id') and machine.category_id:
ows_machine_fields['ows_machine_id.category'] = machine.category_id.name
if hasattr(machine, 'ows_category') and machine.ows_category:
ows_machine_fields['ows_machine_id.category'] = machine.ows_category
if hasattr(machine, 'ows_category_icon') and machine.ows_category_icon:
ows_machine_fields['ows_machine_id.category_icon'] = machine.ows_category_icon
values.update(ows_machine_fields)

View File

@ -0,0 +1,11 @@
# -*- coding: utf-8 -*-
from odoo import fields, models
class MaintenanceEquipmentStatus(models.Model):
_inherit = 'maintenance.equipment.status'
smiley = fields.Char(
string='Smiley',
help='DokuWiki Smiley für diesen Status (z.B. :-) oder :-()'
)

View File

@ -0,0 +1,2 @@
id,name,model_id:id,group_id:id,perm_read,perm_write,perm_create,perm_unlink
access_equipment_wiki_sync_wizard,equipment.wiki.sync.wizard,model_equipment_wiki_sync_wizard,base.group_user,1,1,1,1
1 id name model_id:id group_id:id perm_read perm_write perm_create perm_unlink
2 access_equipment_wiki_sync_wizard equipment.wiki.sync.wizard model_equipment_wiki_sync_wizard base.group_user 1 1 1 1

View File

@ -0,0 +1,24 @@
<?xml version="1.0" encoding="utf-8"?>
<odoo>
<record id="maintenance_equipment_status_form_view" model="ir.ui.view">
<field name="name">maintenance.equipment.status.form.dokuwiki</field>
<field name="model">maintenance.equipment.status</field>
<field name="inherit_id" ref="maintenance_equipment_status.maintenance_equipment_status_view_form"/>
<field name="arch" type="xml">
<field name="name" position="after">
<field name="smiley" placeholder="z.B. :-) oder :-("/>
</field>
</field>
</record>
<record id="maintenance_equipment_status_tree_view" model="ir.ui.view">
<field name="name">maintenance.equipment.status.tree.dokuwiki</field>
<field name="model">maintenance.equipment.status</field>
<field name="inherit_id" ref="maintenance_equipment_status.maintenance_equipment_status_view_tree"/>
<field name="arch" type="xml">
<field name="name" position="after">
<field name="smiley" optional="show"/>
</field>
</field>
</record>
</odoo>

View File

@ -0,0 +1,2 @@
# -*- coding: utf-8 -*-
from . import equipment_wiki_sync_wizard

View File

@ -0,0 +1,123 @@
# -*- coding: utf-8 -*-
import logging
from odoo import api, fields, models, _
from odoo.exceptions import UserError
_logger = logging.getLogger(__name__)
class EquipmentWikiSyncWizard(models.TransientModel):
_name = 'equipment.wiki.sync.wizard'
_description = 'Massen-Synchronisation von Equipment zu DokuWiki'
sync_mode = fields.Selection([
('all', 'Alle Equipment'),
('area', 'Nach Bereich filtern'),
('unsynced', 'Nur nicht synchronisierte'),
], string='Synchronisations-Modus', default='all', required=True)
ows_area_id = fields.Many2one(
'ows.machine.area',
string='Bereich',
help='Nur Equipment aus diesem Bereich synchronisieren'
)
force_sync = fields.Boolean(
string='Zentrale Doku-Seiten überschreiben',
default=False,
help='Auch bereits existierende zentrale Dokumentationsseiten neu generieren (VORSICHT: Überschreibt manuell erstellten Inhalt!)'
)
# Statistik-Felder (nach Sync)
total_count = fields.Integer(string='Gefunden', readonly=True)
success_count = fields.Integer(string='Erfolgreich', readonly=True)
error_count = fields.Integer(string='Fehler', readonly=True)
error_messages = fields.Text(string='Fehlermeldungen', readonly=True)
@api.onchange('sync_mode')
def _onchange_sync_mode(self):
"""Bereich-Feld nur bei 'area' Modus anzeigen"""
if self.sync_mode != 'area':
self.ows_area_id = False
def action_sync_equipment(self):
"""
Hauptaktion: Equipment synchronisieren basierend auf gewähltem Modus
"""
self.ensure_one()
# Equipment-Liste basierend auf Modus ermitteln
domain = []
if self.sync_mode == 'area':
if not self.ows_area_id:
raise UserError("Bitte einen Bereich auswählen!")
domain.append(('ows_area_id', '=', self.ows_area_id.id))
elif self.sync_mode == 'unsynced':
domain.append(('wiki_synced', '=', False))
# Equipment finden
equipment_records = self.env['maintenance.equipment'].search(domain)
if not equipment_records:
raise UserError("Keine Equipment-Einträge gefunden, die synchronisiert werden können!")
total = len(equipment_records)
success = 0
errors = 0
error_list = []
_logger.info(f"Starte Wiki-Synchronisation für {total} Equipment-Einträge")
# Jeden Equipment-Eintrag synchronisieren
for equipment in equipment_records:
try:
# Prüfen ob Bereich gesetzt ist
if not equipment.ows_area_id:
error_msg = f"{equipment.name}: Kein Bereich gesetzt"
error_list.append(error_msg)
errors += 1
_logger.warning(error_msg)
continue
# Force-Sync: Zentrale Doku-Seite überschreiben
if self.force_sync and equipment.wiki_doku_id:
doku_page_id = equipment._get_wiki_doku_page_id()
doku_content = equipment._generate_wiki_doku_page_content()
dokuwiki_client = self.env['dokuwiki.client']
dokuwiki_client.create_page(
doku_page_id,
doku_content,
f"Überschrieben von Massen-Sync: {equipment.name}"
)
_logger.info(f"Zentrale Doku-Seite überschrieben: {doku_page_id}")
# Standard-Synchronisation
equipment.sync_to_dokuwiki()
success += 1
_logger.info(f"{equipment.name} synchronisiert")
except Exception as e:
error_msg = f"{equipment.name}: {str(e)}"
error_list.append(error_msg)
errors += 1
_logger.error(f"{error_msg}")
# Statistik speichern
self.write({
'total_count': total,
'success_count': success,
'error_count': errors,
'error_messages': '\n'.join(error_list) if error_list else 'Keine Fehler',
})
# Ergebnis-View anzeigen
return {
'type': 'ir.actions.act_window',
'name': 'Wiki-Synchronisation Ergebnis',
'res_model': 'equipment.wiki.sync.wizard',
'view_mode': 'form',
'res_id': self.id,
'target': 'new',
'context': {'show_result': True},
}

View File

@ -0,0 +1,60 @@
<?xml version="1.0" encoding="utf-8"?>
<odoo>
<!-- Wizard Form View -->
<record id="equipment_wiki_sync_wizard_form" model="ir.ui.view">
<field name="name">equipment.wiki.sync.wizard.form</field>
<field name="model">equipment.wiki.sync.wizard</field>
<field name="arch" type="xml">
<form string="Wiki-Synchronisation">
<group invisible="context.get('show_result', False)">
<group>
<field name="sync_mode" widget="radio"/>
<field name="ows_area_id" invisible="sync_mode != 'area'" required="sync_mode == 'area'"/>
</group>
<group>
<field name="force_sync"/>
</group>
</group>
<!-- Ergebnis-Anzeige -->
<group invisible="not context.get('show_result', False)">
<group>
<field name="total_count"/>
<field name="success_count"/>
<field name="error_count"/>
</group>
<group>
<field name="error_messages" widget="text" colspan="2" invisible="error_count == 0"/>
</group>
</group>
<footer>
<button string="Synchronisieren"
name="action_sync_equipment"
type="object"
class="btn-primary"
invisible="context.get('show_result', False)"/>
<button string="Schließen"
special="cancel"
class="btn-secondary"/>
</footer>
</form>
</field>
</record>
<!-- Menu-Action -->
<record id="action_equipment_wiki_sync_wizard" model="ir.actions.act_window">
<field name="name">Wiki-Synchronisation</field>
<field name="res_model">equipment.wiki.sync.wizard</field>
<field name="view_mode">form</field>
<field name="target">new</field>
<field name="context">{}</field>
</record>
<!-- Menu-Eintrag unter Wartung → Konfiguration -->
<menuitem id="menu_equipment_wiki_sync"
name="Wiki-Synchronisation"
parent="maintenance.menu_maintenance_configuration"
action="action_equipment_wiki_sync_wizard"
sequence="99"/>
</odoo>