DokuWiki Integration: wiki_doku_id aus Name+Modell, zentraler Namespace konfigurierbar

- wiki_doku_id wird nun aus equipment.name und equipment.model kombiniert generiert
- Systemparameter 'dokuwiki.central_documentation_namespace' für konfigurierbaren Namespace
- Verbessertes Error-Handling in dokuwiki_client mit detaillierteren Fehlermeldungen
- Docker-Netzwerk Unterstützung dokumentiert
- Gelöschte veraltete WordPress-API Dateien
This commit is contained in:
Matthias Lotz 2025-12-23 14:48:44 +01:00
parent 253d289633
commit f79e126c8c
10 changed files with 233 additions and 383 deletions

View File

@ -1,300 +0,0 @@
<?php
/**
* Plugin Name: OpenWorkshop Odoo API
* Plugin URI: https://hobbyhimmel.de/
* Description: Bindet Daten aus Odoo (OpenWorkshop) per REST-API in WordPress ein. Stellt u.a. den Shortcode [openworkshop_machines] bereit.
* Version: 0.1.0
* Author: HobbyHimmel / Matthias Lotz
* License: GPLv3
* License URI: https://www.gnu.org/licenses/gpl-3.0.html
* Text Domain: openworkshop-odoo-api
*/
if ( ! defined( 'ABSPATH' ) ) {
exit;
}
class OpenWorkshop_Odoo_API_Plugin {
const OPTION_GROUP = 'openworkshop_odoo_api_options';
const OPTION_NAME = 'openworkshop_odoo_api_settings';
public function __construct() {
add_action( 'admin_menu', array( $this, 'register_settings_page' ) );
add_action( 'admin_init', array( $this, 'register_settings' ) );
add_shortcode( 'openworkshop_machines', array( $this, 'shortcode_machines' ) );
}
/**
* Register settings page under Settings OpenWorkshop Odoo API
*/
public function register_settings_page() {
add_options_page(
__( 'OpenWorkshop Odoo API', 'openworkshop-odoo-api' ),
__( 'OpenWorkshop Odoo API', 'openworkshop-odoo-api' ),
'manage_options',
'openworkshop-odoo-api',
array( $this, 'render_settings_page' )
);
}
/**
* Register settings (base URL + optional token)
*/
public function register_settings() {
register_setting(
self::OPTION_GROUP,
self::OPTION_NAME,
array(
'type' => 'array',
'sanitize_callback' => array( $this, 'sanitize_settings' ),
'default' => array(
'base_url' => '',
'api_token' => '',
'machines_endpoint' => '/api/v1/machines',
),
)
);
add_settings_section(
'openworkshop_odoo_api_main',
__( 'Odoo API Einstellungen', 'openworkshop-odoo-api' ),
'__return_false',
'openworkshop-odoo-api'
);
add_settings_field(
'base_url',
__( 'Odoo Basis-URL', 'openworkshop-odoo-api' ),
array( $this, 'render_field_base_url' ),
'openworkshop-odoo-api',
'openworkshop_odoo_api_main'
);
add_settings_field(
'machines_endpoint',
__( 'Endpoint für Maschinenliste', 'openworkshop-odoo-api' ),
array( $this, 'render_field_machines_endpoint' ),
'openworkshop-odoo-api',
'openworkshop_odoo_api_main'
);
add_settings_field(
'api_token',
__( 'API Token (optional)', 'openworkshop-odoo-api' ),
array( $this, 'render_field_api_token' ),
'openworkshop-odoo-api',
'openworkshop_odoo_api_main'
);
}
public function sanitize_settings( $input ) {
$output = array();
$output['base_url'] = isset( $input['base_url'] )
? esc_url_raw( rtrim( $input['base_url'], '/' ) )
: '';
$output['machines_endpoint'] = isset( $input['machines_endpoint'] )
? sanitize_text_field( $input['machines_endpoint'] )
: '/api/v1/machines';
$output['api_token'] = isset( $input['api_token'] )
? sanitize_text_field( $input['api_token'] )
: '';
return $output;
}
public function get_settings() {
$settings = get_option( self::OPTION_NAME, array() );
$defaults = array(
'base_url' => '',
'api_token' => '',
'machines_endpoint' => '/api/v1/machines',
);
return wp_parse_args( $settings, $defaults );
}
public function render_settings_page() {
if ( ! current_user_can( 'manage_options' ) ) {
return;
}
$settings = $this->get_settings();
?>
<div class="wrap">
<h1><?php esc_html_e( 'OpenWorkshop Odoo API Einstellungen', 'openworkshop-odoo-api' ); ?></h1>
<form method="post" action="options.php">
<?php
settings_fields( self::OPTION_GROUP );
do_settings_sections( 'openworkshop-odoo-api' );
submit_button();
?>
</form>
<h2><?php esc_html_e( 'Shortcodes', 'openworkshop-odoo-api' ); ?></h2>
<p><?php esc_html_e( 'Maschinenliste anzeigen:', 'openworkshop-odoo-api' ); ?></p>
<code>[openworkshop_machines]</code>
<p><?php esc_html_e( 'Optional können Attribute verwendet werden, um z. B. eine andere Anzahl von Maschinen anzuzeigen.', 'openworkshop-odoo-api' ); ?></p>
</div>
<?php
}
public function render_field_base_url() {
$settings = $this->get_settings();
?>
<input type="text"
name="<?php echo esc_attr( self::OPTION_NAME ); ?>[base_url]"
value="<?php echo esc_attr( $settings['base_url'] ); ?>"
class="regular-text"
placeholder="https://odoo.example.org" />
<p class="description">
<?php esc_html_e( 'Basis-URL deiner Odoo-Instanz (ohne Slash am Ende). Die API-Route wird daran angehängt.', 'openworkshop-odoo-api' ); ?>
</p>
<?php
}
public function render_field_machines_endpoint() {
$settings = $this->get_settings();
?>
<input type="text"
name="<?php echo esc_attr( self::OPTION_NAME ); ?>[machines_endpoint]"
value="<?php echo esc_attr( $settings['machines_endpoint'] ); ?>"
class="regular-text"
placeholder="/api/v1/machines" />
<p class="description">
<?php esc_html_e( 'Relativer Pfad zum Maschinen-Endpoint. Standard: /api/v1/machines', 'openworkshop-odoo-api' ); ?>
</p>
<?php
}
public function render_field_api_token() {
$settings = $this->get_settings();
?>
<input type="text"
name="<?php echo esc_attr( self::OPTION_NAME ); ?>[api_token]"
value="<?php echo esc_attr( $settings['api_token'] ); ?>"
class="regular-text" />
<p class="description">
<?php esc_html_e( 'Optionaler API-Token, der im Authorization-Header gesendet wird (Bearer &lt;token&gt;).', 'openworkshop-odoo-api' ); ?>
</p>
<?php
}
/**
* Shortcode: [openworkshop_machines]
* Attributes:
* limit Anzahl der Einträge (optional)
*/
public function shortcode_machines( $atts ) {
$atts = shortcode_atts(
array(
'limit' => 0,
),
$atts,
'openworkshop_machines'
);
$data = $this->fetch_machines();
if ( is_wp_error( $data ) ) {
return '<p>' . esc_html__( 'Fehler beim Laden der Maschinendaten aus Odoo.', 'openworkshop-odoo-api' ) . '</p>';
}
if ( ! is_array( $data ) || empty( $data ) ) {
return '<p>' . esc_html__( 'Keine Maschinen gefunden.', 'openworkshop-odoo-api' ) . '</p>';
}
$limit = intval( $atts['limit'] );
if ( $limit > 0 ) {
$data = array_slice( $data, 0, $limit );
}
ob_start();
?>
<div class="openworkshop-machines">
<table class="openworkshop-machines-table">
<thead>
<tr>
<th><?php esc_html_e( 'Name', 'openworkshop-odoo-api' ); ?></th>
<th><?php esc_html_e( 'Bereich', 'openworkshop-odoo-api' ); ?></th>
<th><?php esc_html_e( 'Status', 'openworkshop-odoo-api' ); ?></th>
</tr>
</thead>
<tbody>
<?php foreach ( $data as $machine ) : ?>
<tr>
<td>
<?php echo isset( $machine['name'] ) ? esc_html( $machine['name'] ) : ''; ?>
</td>
<td>
<?php echo isset( $machine['area'] ) ? esc_html( $machine['area'] ) : ''; ?>
</td>
<td>
<?php echo isset( $machine['status'] ) ? esc_html( $machine['status'] ) : ''; ?>
</td>
</tr>
<?php endforeach; ?>
</tbody>
</table>
</div>
<?php
return ob_get_clean();
}
/**
* Calls the Odoo API and returns decoded JSON or WP_Error.
*/
protected function fetch_machines() {
$settings = $this->get_settings();
if ( empty( $settings['base_url'] ) ) {
return new WP_Error( 'openworkshop_no_base_url', __( 'Keine Odoo Basis-URL konfiguriert.', 'openworkshop-odoo-api' ) );
}
$url = $settings['base_url'] . $settings['machines_endpoint'];
$args = array(
'timeout' => 10,
'headers' => array(
'Accept' => 'application/json',
),
);
if ( ! empty( $settings['api_token'] ) ) {
$args['headers']['Authorization'] = 'Bearer ' . $settings['api_token'];
}
$response = wp_remote_get( $url, $args );
if ( is_wp_error( $response ) ) {
return $response;
}
$code = wp_remote_retrieve_response_code( $response );
$body = wp_remote_retrieve_body( $response );
if ( $code < 200 || $code >= 300 ) {
return new WP_Error( 'openworkshop_bad_status', sprintf( 'HTTP %d', $code ) );
}
$data = json_decode( $body, true );
if ( json_last_error() !== JSON_ERROR_NONE ) {
return new WP_Error( 'openworkshop_json_error', json_last_error_msg() );
}
return $data;
}
}
function openworkshop_odoo_api_bootstrap() {
static $instance = null;
if ( $instance === null ) {
$instance = new OpenWorkshop_Odoo_API_Plugin();
}
return $instance;
}
openworkshop_odoo_api_bootstrap();

View File

@ -1,30 +0,0 @@
=== OpenWorkshop Odoo API ===
Contributors: hobbyhimmel
Tags: odoo, api, openworkshop, integration
Requires at least: 5.8
Tested up to: 6.6
Stable tag: 0.1.0
License: GPLv3
License URI: https://www.gnu.org/licenses/gpl-3.0.html
Dieses Plugin bindet Maschinendaten aus einer Odoo/OpenWorkshop-Installation via REST-API in WordPress ein.
== Beschreibung ==
Das Plugin stellt u.a. den Shortcode [openworkshop_machines] bereit, der eine einfache Maschinenliste
auf Basis eines JSON-Endpunkts in Odoo rendert.
In den Einstellungen kann die Basis-URL der Odoo-Instanz, der Endpoint (z.B. /api/v1/machines) sowie ein
optionaler API-Token hinterlegt werden.
== Installation ==
1. ZIP in WordPress unter Plugins → Installieren → Plugin hochladen hochladen.
2. Aktivieren.
3. Unter Einstellungen → OpenWorkshop Odoo API die Basis-URL und den Endpoint konfigurieren.
4. Den Shortcode [openworkshop_machines] in einer Seite oder einem Beitrag einfügen.
== Changelog ==
= 0.1.0 =
* Erste Version.

View File

@ -93,6 +93,13 @@ dokuwiki.overview_columns = Name|Hersteller|Modell|Seriennummer|Kosten|Garantie|
dokuwiki.overview_column_data = {name}|{partner_id}|{model}|{serial_no}|{cost}|{warranty_date}|{location}|{ows_machine_id.category_icon}
```
### Beispiel 4: Mit Tags
```python
dokuwiki.overview_columns = Status|Name|Typ|Tags|Standort|Dokumentation
dokuwiki.overview_column_data = {status_smiley}|{name}|{model}|{tags_list}|{location}|{wiki_doku_link}
```
**Wichtig:** Anzahl der Pipes muss übereinstimmen!
- 3 Pipes = 4 Spalten
- Spalten und Daten müssen gleiche Anzahl haben
@ -167,14 +174,45 @@ dokuwiki.overview_column_data = {name}|{partner_id}|{model}|{serial_no}|{cost}|{
## Automatische Updates
Die Übersichtstabelle wird **NICHT** automatisch bei Equipment-Änderungen aktualisiert.
Die Übersichtstabelle kann optional automatisch bei Equipment-Änderungen aktualisiert werden.
**Manueller Sync empfohlen:**
### Manueller Sync (empfohlen)
**Manueller Sync empfohlen für:**
- Nach Massen-Importen
- Einmal täglich/wöchentlich
- Bei strukturellen Änderungen (neue Bereiche, etc.)
**Alternativ: Cronjob einrichten**
**Vorteil:** Performanter, keine zusätzliche Last bei jeder Equipment-Änderung
### Automatischer Sync bei Equipment-Änderungen
**Was passiert beim Speichern eines Equipment?**
1. **Equipment-Detailseite wird immer aktualisiert** wenn:
- Eines der überwachten Felder geändert wurde (siehe unten)
- `wiki_auto_sync` für das Equipment aktiviert ist (Standard: `True`)
- Equipment bereits synchronisiert wurde (`wiki_synced = True`)
2. **Übersichtstabelle wird zusätzlich aktualisiert** wenn:
- Alle Bedingungen von (1) erfüllt sind UND
- Systemparameter `dokuwiki.auto_update_overview_table = True` gesetzt ist
**Überwachte Felder (lösen Sync aus):**
- `name`, `serial_no`, `ows_area_id`, `category_id`, `status_id`
- `model`, `partner_id`, `location`, `note`, `image_1920`, `tag_ids`
**Aktivierung Übersichtstabellen-Sync:**
```python
# In Odoo: Einstellungen → Technisch → Parameter → Systemparameter
dokuwiki.auto_update_overview_table = True
```
**Achtung:**
- Bei vielen gleichzeitigen Equipment-Änderungen kann dies Performance-Probleme verursachen!
- Die komplette Übersichtstabelle wird bei jeder Änderung neu generiert (ca. 156 Zeilen)
### Cronjob (Alternative)
```python
# In Odoo: Einstellungen → Technisch → Automatisierung → Geplante Aktionen
@ -207,6 +245,12 @@ Alle Platzhalter aus dem Detail-Template sind verfügbar!
- `{partner_id}` - Lieferant (Name)
- `{partner_ref}` - Lieferanten-Referenz
## Tags (falls maintenance_equipment_tags installiert)
- `{tags}` - Komma-separierte Liste aller Tags (z.B. "Holz, CNC, Einweisung erforderlich")
- `{tags_list}` - DokuWiki Bullet-Liste mit Zeilenumbrüchen (ideal für Tabellenzellen)
- `{tags_count}` - Anzahl der zugewiesenen Tags
## Spezial-Felder
- `{view_type}` - "Bereich" oder "Einsatzzweck"
@ -216,7 +260,7 @@ Alle Platzhalter aus dem Detail-Template sind verfügbar!
- `{odoo_link}` - Fertiger Link zur Odoo Equipment-Seite
- `{odoo_url}` - URL zur Odoo Equipment-Seite (ohne Link-Markup)
- `{sync_datetime}` - Aktuelles Datum/Zeit
- `{image}` - Equipment-Bild (300px Breite) - Format: `{{:media_id?300}}`
- `{image}` - Equipment-Bild (100px Breite) - Format: `{{:media_id?100}}`
- `{image_large}` - Equipment-Bild (Originalgröße) - Format: `{{:media_id}}`
- `{image_id}` - Media-ID des Bildes (z.B. werkstatt:ausruestung:media:sabako-laser.jpg)
@ -236,7 +280,7 @@ Alle Platzhalter aus dem Detail-Template sind verfügbar!
- **Pipe als Trenner**: Zwischen Spalten `|` verwenden
- **Leere Werte**: Werden automatisch durch `-` ersetzt
- **Links**: `{wiki_doku_link}` und `{odoo_link}` enthalten bereits DokuWiki Link-Syntax
- **Bilder**: `{image}` ist bereits formatiert mit `{{:...?300}}`
- **Bilder**: `{image}` ist bereits formatiert mit `{{:...?100}}`
- **ows.machine**: Immer mit Präfix `ows_machine_id.` (z.B. `{ows_machine_id.location}`)
## Beispiel-Konfigurationen
@ -258,3 +302,9 @@ Daten: {status_smiley}|{ows_machine_id.category_icon}|{partner_id}|{note}|{mod
Spalten: Name|Status|Kategorie|Hersteller|Modell|S/N|Kosten|Garantie|Standort|Bereich|Bild|Doku
Daten: {name}|{status_smiley}|{ows_machine_id.category_icon}|{partner_id}|{model}|{serial_no}|{cost}|{warranty_date}|{location}|{ows_area}|{image}|{wiki_doku_link}
```
### Mit Tags
```
Spalten: Name|Status|Tags|Hersteller|Standort|Doku
Daten: {name}|{status_smiley}|{tags_list}|{partner_id}|{location}|{wiki_doku_link}
```

View File

@ -31,7 +31,7 @@ ACL:
* Phase 1: Nur @odoo Gruppe hat Zugriff
* Phase 2: @werkstatt bekommt Lesezugriff auf Hauptseiten, Edit-Zugriff auf Doku-Seiten
""",
'author': 'Open Workshop',
'author': 'Hobbyhimmel',
'website': 'https://hobbyhimmel.de',
'license': 'LGPL-3',
'depends': [

View File

@ -1,24 +0,0 @@
====== Geräte & Maschinen - Übersicht ======
Diese Seite wird automatisch von Odoo aktualisiert und bietet eine sortier- und filterbare Übersicht aller Geräte und Maschinen.
**Letztes Update:** {{SYNC_DATETIME}}
<datatable Equipment-Übersicht>
^ Name ^ Status ^ Sicherheits-Kategorie ^ Bereich ^ Standort ^ Bild ^
{{EQUIPMENT_TABLE_ROWS}}
</datatable>
----
**Hinweis:** Diese Tabelle ist interaktiv:
* Klicken Sie auf die Spaltenköpfe zum Sortieren
* Nutzen Sie das Suchfeld zum Filtern
* Klicken Sie auf den Namen für die Detail-Dokumentation
**Symbole:**
* 🟢 Grün = Keine Einweisung erforderlich
* 🟡 Gelb = Einweisung empfohlen
* 🔴 Rot = Einweisung zwingend erforderlich
//Automatisch generiert aus Odoo - Änderungen werden beim nächsten Sync überschrieben//

View File

@ -3,11 +3,11 @@
<!-- DokuWiki Smiley-Werte für Status aus open_workshop_base -->
<record id="open_workshop_base.equipment_status_inbetrieb" model="maintenance.equipment.status">
<field name="smiley">:-)</field>
<field name="smiley">😊</field>
</record>
<record id="open_workshop_base.equipment_status_defekt" model="maintenance.equipment.status">
<field name="smiley">:-(</field>
<field name="smiley">☹️</field>
</record>
<record id="open_workshop_base.equipment_status_wartung" model="maintenance.equipment.status">

View File

@ -42,15 +42,34 @@ class DokuWikiClient(models.AbstractModel):
"- dokuwiki.password"
)
# URL validieren/normalisieren
if not wiki_url.startswith(('http://', 'https://')):
raise UserError(f"DokuWiki URL muss mit http:// oder https:// beginnen: {wiki_url}")
try:
_logger.info(f"Verbinde zu DokuWiki: {wiki_url} (User: {wiki_user})")
wiki = DokuWiki(wiki_url, wiki_user, wiki_password)
# Test-Verbindung
version = wiki.version
_logger.info(f"DokuWiki-Verbindung erfolgreich: {version}")
_logger.info(f"DokuWiki-Verbindung erfolgreich: Version {version}")
return wiki
except DokuWikiError as e:
_logger.error(f"DokuWiki-Verbindung fehlgeschlagen: {e}")
raise UserError(f"Verbindung zu DokuWiki fehlgeschlagen: {e}")
error_details = f"URL: {wiki_url}, User: {wiki_user}, Fehler: {e}"
_logger.error(f"DokuWiki-Verbindung fehlgeschlagen: {error_details}")
raise UserError(
f"Verbindung zu DokuWiki fehlgeschlagen.\n\n"
f"Details:\n"
f"- URL: {wiki_url}\n"
f"- User: {wiki_user}\n"
f"- Fehler: {e}\n\n"
f"Hinweise:\n"
f"- Prüfen Sie ob die URL korrekt ist (sollte auf /lib/exe/xmlrpc.php zeigen)\n"
f"- Stellen Sie sicher, dass XML-RPC in DokuWiki aktiviert ist\n"
f"- Prüfen Sie die Benutzer-Zugangsdaten"
)
except Exception as e:
_logger.error(f"Unerwarteter Fehler bei DokuWiki-Verbindung: {e}", exc_info=True)
raise UserError(f"Unerwarteter Fehler: {e}")
@api.model
def create_page(self, page_id, content, summary="Erstellt von Odoo"):
@ -71,12 +90,24 @@ class DokuWikiClient(models.AbstractModel):
wiki = self._get_wiki_connection()
try:
_logger.info(f"Erstelle/Aktualisiere Wiki-Seite: {page_id} ({len(content)} Zeichen)")
wiki.pages.set(page_id, content, summary=summary)
_logger.info(f"Wiki-Seite erstellt/aktualisiert: {page_id}")
_logger.info(f"Wiki-Seite erfolgreich erstellt/aktualisiert: {page_id}")
return True
except DokuWikiError as e:
_logger.error(f"Fehler beim Erstellen der Wiki-Seite {page_id}: {e}")
raise UserError(f"Fehler beim Erstellen der Wiki-Seite: {e}")
_logger.error(f"DokuWiki API Fehler bei Seite {page_id}: {e}", exc_info=True)
raise UserError(
f"Fehler beim Erstellen der Wiki-Seite '{page_id}':\n\n"
f"{e}\n\n"
f"Mögliche Ursachen:\n"
f"- XML-RPC nicht aktiviert in DokuWiki\n"
f"- Keine Schreibrechte für den Namespace\n"
f"- Ungültige Zeichen in der Page-ID\n"
f"- DokuWiki gibt Fehler/Warnings aus (prüfen Sie die DokuWiki-Logs)"
)
except Exception as e:
_logger.error(f"Unerwarteter Fehler beim Erstellen der Seite {page_id}: {e}", exc_info=True)
raise UserError(f"Unerwarteter Fehler: {e}")
@api.model
def get_page(self, page_id):

View File

@ -9,6 +9,63 @@ _logger = logging.getLogger(__name__)
class MaintenanceEquipment(models.Model):
"""
Erweitert das Maintenance Equipment Modell um DokuWiki-Integration.
Diese Klasse ermöglicht die Synchronisation von Werkstatt-Equipment-Daten mit einem
DokuWiki-System. Jedes Equipment erhält eine zentrale Dokumentationsseite sowie
verschiedene Ansichtsseiten (nach Bereich, später auch nach Einsatzzweck).
Hauptfunktionen:
- Automatische Generierung von Wiki-Seiten aus Equipment-Daten
- Template-basierte Wiki-Seiten-Erstellung (verwendet c_template aus DokuWiki)
- Bild-Upload und Einbindung in Wiki-Seiten
- Zentrale Dokumentationsseite pro Equipment (manuell erweiterbar)
- Bereichs-spezifische Übersichtsseiten (automatisch aktualisiert)
- Automatische Übersichtstabelle aller Equipment (DataTable mit Sortierung/Filterung)
- Automatische Synchronisation bei Feldänderungen (optional)
Wiki-Seitenstruktur:
- werkstatt:ausruestung:doku:{wiki_doku_id} - Zentrale Dokumentation
- werkstatt:ausruestung:{area}:{wiki_doku_id} - Bereichsansicht
- werkstatt:ausruestung:uebersicht - Equipment-Übersichtstabelle
Neue Felder:
- image_1920: Bild des Equipment (wird automatisch ins Wiki hochgeladen)
- wiki_doku_id: Eindeutige ID für Wiki-Dokumentation (aus Equipment-Name generiert)
- wiki_page_url: Berechnete URL zur Wiki-Seite
- wiki_synced: Status der Synchronisation
- wiki_last_sync: Zeitstempel der letzten Synchronisation
- wiki_auto_sync: Schalter für automatische Synchronisation bei Änderungen
Template-System:
Die Wiki-Seiten werden aus einem Template (werkstatt:ausruestung:c_template)
generiert, das folgende Platzhalter unterstützt:
- {feldname} - Direkte Equipment-Felder (z.B. {name}, {serial_no})
- {ows_machine_id.feldname} - Maschinenfelder (z.B. {ows_machine_id.power})
- {wiki_doku_page} - ID der zentralen Dokumentationsseite
- {wiki_doku_link} - DokuWiki-Link zur Dokumentationsseite
- {image} - Bild-Einbindung (wird automatisch hochgeladen)
- {tags} - Komma-separierte Tag-Liste
- {sync_datetime} - Zeitstempel der Synchronisation
Übersichtstabelle:
Generiert eine zentrale Übersichtstabelle aller Equipment mit konfigurierbaren
Spalten. Die Tabelle verwendet das DokuWiki DataTable-Plugin für interaktive
Sortierung und Filterung. Konfiguration über Systemparameter:
- dokuwiki.overview_page_id - Seiten-ID der Übersicht
- dokuwiki.overview_title - Titel der Seite
- dokuwiki.overview_columns - Spaltenüberschriften (pipe-separiert)
- dokuwiki.overview_column_data - Spaltendaten mit Platzhaltern
Verwendung:
1. Equipment anlegen/bearbeiten in Odoo
2. Bereich (ows_area_id) setzen (erforderlich für Wiki-Sync)
3. Optional: Bild hochladen (wird automatisch ins Wiki übertragen)
4. "Zum Wiki synchronisieren" klicken (oder automatisch bei Änderungen)
5. Wiki-Seiten werden erstellt/aktualisiert
6. "Wiki-Seite öffnen" für direkten Browser-Zugriff
Notes:
- Zentrale Dokumentationsseite wird nur beim ersten Sync erstellt
- Bereichsansichten werden bei jedem Sync aktualisiert
- Bilder werden als werkstatt:ausruestung:media:{wiki_doku_id}.jpg gespeichert
- Wiki-Namen werden normalisiert (Umlaute, Sonderzeichen, Kleinschreibung)
- Bei fehlendem Template wird Fallback-Inhalt verwendet
- Übersichtstabelle kann manuell oder automatisch aktualisiert werden
"""
_inherit = 'maintenance.equipment'
# Bild-Feld
@ -23,7 +80,7 @@ class MaintenanceEquipment(models.Model):
wiki_doku_id = fields.Char(
string='Wiki Dokumentations-ID',
readonly=True,
help='Eindeutige ID für die zentrale Wiki-Dokumentation (generiert aus Equipment-Namen, z.B. "formatkreissaege")'
help='Eindeutige ID für die zentrale Wiki-Dokumentation (generiert aus Equipment-Namen, z.B. "formatkreissaege", und Modell "bosch-abc" -> "formatkreissaege-bosch-abc")'
)
wiki_page_url = fields.Char(
string='Wiki-Seiten URL',
@ -63,8 +120,8 @@ class MaintenanceEquipment(models.Model):
def _get_wiki_doku_id(self):
"""
Generiert die Wiki-Dokumentations-ID aus dem Equipment-Namen.
Format: normalisierter_name (z.B. "formatkreissaege", "tischkreissaege")
Generiert die Wiki-Dokumentations-ID aus dem Equipment-Namen und Modell.
Format: normalisierter_name-normalisiertes_modell (z.B. "formatkreissaege-bosch-abc")
Returns:
str: Wiki-Doku-ID oder False wenn keine ID vorhanden
@ -75,9 +132,13 @@ class MaintenanceEquipment(models.Model):
if self.wiki_doku_id:
return self.wiki_doku_id
# Generiere aus Equipment-Namen
# Generiere aus Equipment-Namen und Modell
if self.name:
return self._normalize_wiki_name(self.name)
normalized_name = self._normalize_wiki_name(self.name)
if self.model:
normalized_model = self._normalize_wiki_name(self.model)
return f"{normalized_name}-{normalized_model}"
return normalized_name
return False
@ -103,14 +164,23 @@ class MaintenanceEquipment(models.Model):
def _get_wiki_doku_page_id(self):
"""
Generiert die Wiki-Page-ID für die zentrale Dokumentation.
Format: werkstatt:ausruestung:doku:{wiki_doku_id}
Format: {central_namespace}:{wiki_doku_id}
Der Namespace ist über Systemparameter 'dokuwiki.central_documentation_namespace' konfigurierbar.
Returns:
str: Page-ID der zentralen Doku
"""
self.ensure_one()
# Namespace aus Systemparameter laden
IrConfigParameter = self.env['ir.config_parameter'].sudo()
central_namespace = IrConfigParameter.get_param(
'dokuwiki.central_documentation_namespace',
default='werkstatt:ausruestung:doku'
)
wiki_doku_id = self._get_wiki_doku_id()
return f"werkstatt:ausruestung:doku:{wiki_doku_id}"
return f"{central_namespace}:{wiki_doku_id}"
def _normalize_wiki_name(self, name):
"""
@ -250,6 +320,19 @@ class MaintenanceEquipment(models.Model):
'partner_ref': self.partner_ref or '',
}
# Tags hinzufügen (falls vorhanden)
if hasattr(self, 'tag_ids') and self.tag_ids:
# Komma-separierte Liste für Template
values['tags'] = ', '.join(self.tag_ids.mapped('name'))
# DokuWiki Bullet-Liste für Tabellenzellen
values['tags_list'] = ' * ' + '\\\\ * '.join(self.tag_ids.mapped('name')) if self.tag_ids else ''
# Anzahl der Tags
values['tags_count'] = str(len(self.tag_ids))
else:
values['tags'] = ''
values['tags_list'] = ''
values['tags_count'] = '0'
# ows.machine Felder hinzufügen (falls verknüpft)
if self.ows_machine_id:
machine = self.ows_machine_id
@ -377,12 +460,12 @@ class MaintenanceEquipment(models.Model):
str: Wiki-Markup-Inhalt
"""
self.ensure_one()
if self.model:
model_part = f" - Modell: {self.model}"
else:
model_part = ""
content = f"""====== {self.name} - Dokumentation ======
**Bereich:** {self.ows_area_id.name if self.ows_area_id else 'Nicht zugeordnet'}
**Kategorie:** {self.category_id.name if self.category_id else 'Keine'}
**Seriennummer:** {self.serial_no or 'Keine'}
content = f"""====== {self.name}{model_part} ======
===== Beschreibung =====
@ -421,9 +504,11 @@ Hier kann die Dokumentation für {self.name} geschrieben werden.
if not self.name:
raise UserError("Equipment-Name muss gesetzt sein für Wiki-Synchronisation!")
# wiki_doku_id beim ersten Sync setzen (aus Equipment-Namen generieren)
# wiki_doku_id beim ersten Sync setzen (aus Equipment-Namen und Modell generieren)
if not self.wiki_doku_id:
generated_id = self._normalize_wiki_name(self.name)
generated_id = self._get_wiki_doku_id()
if not generated_id:
generated_id = self._normalize_wiki_name(self.name)
self.write({'wiki_doku_id': generated_id})
_logger.info(f"Wiki-Doku-ID für {self.name} gesetzt: {generated_id}")
@ -521,7 +606,11 @@ Hier kann die Dokumentation für {self.name} geschrieben werden.
result = super().write(vals)
# Felder die eine Synchronisation auslösen
sync_fields = {'name', 'serial_no', 'area_id', 'category_id'}
sync_fields = {'name', 'serial_no', 'ows_area_id', 'category_id', 'status_id',
'model', 'partner_id', 'location', 'note', 'image_1920', 'tag_ids'}
# Flag ob Übersichtstabelle aktualisiert werden soll
should_update_overview = False
if sync_fields & set(vals.keys()):
# Nur synchronisieren wenn auto_sync aktiviert und bereits synced
@ -529,6 +618,7 @@ Hier kann die Dokumentation für {self.name} geschrieben werden.
if record.wiki_auto_sync and record.wiki_synced:
try:
record.sync_to_dokuwiki()
should_update_overview = True
except Exception as e:
_logger.warning(f"Automatische Wiki-Sync fehlgeschlagen: {e}")
# Nicht abbrechen, nur loggen
@ -536,6 +626,21 @@ Hier kann die Dokumentation für {self.name} geschrieben werden.
# Sync-Status zurücksetzen
record.write({'wiki_synced': False})
# Optional: Übersichtstabelle aktualisieren
if should_update_overview:
IrConfigParameter = self.env['ir.config_parameter'].sudo()
auto_update_overview = IrConfigParameter.get_param(
'dokuwiki.auto_update_overview_table',
default='False'
)
if auto_update_overview.lower() == 'true':
try:
self.env['maintenance.equipment'].action_sync_overview_table()
_logger.info("Übersichtstabelle automatisch aktualisiert")
except Exception as e:
_logger.warning(f"Automatische Übersichtstabellen-Sync fehlgeschlagen: {e}")
return result
# ==========================================

View File

@ -3,6 +3,22 @@ from odoo import fields, models
class MaintenanceEquipmentStatus(models.Model):
"""
Extension of the maintenance.equipment.status model to add DokuWiki smiley support.
This class extends the standard Odoo maintenance equipment status model to include
a smiley field that can be used for DokuWiki integration. The smiley field allows
administrators to associate DokuWiki emoticon syntax with each maintenance equipment
status, enabling visual representation of equipment status in DokuWiki documentation.
The smiley field accepts DokuWiki emoticon syntax (e.g., ':-)', ':-(', ':-D', etc.)
which can be used to display appropriate emoticons when exporting or displaying
equipment status information in DokuWiki format.
This extension is particularly useful for workshops or maintenance departments that
use DokuWiki as their documentation platform and want to have a visual representation
of equipment status alongside textual information.
"""
_inherit = 'maintenance.equipment.status'
smiley = fields.Char(

View File

@ -30,10 +30,12 @@ class ResConfigSettings(models.TransientModel):
'dokuwiki.url': 'https://wiki.hobbyhimmel.de',
'dokuwiki.user': 'odoo.odoo',
'dokuwiki.password': 'CHANGE_ME',
'dokuwiki.central_documentation_namespace': 'werkstatt:ausruestung:doku',
'dokuwiki.overview_page_id': 'werkstatt:ausruestung:start',
'dokuwiki.overview_title': 'Geräte & Maschinen - Übersicht',
'dokuwiki.overview_columns': 'Name|Zustand|Sicherheit|Bereich|Standort|Bild',
'dokuwiki.overview_column_data': '[[{wiki_page_id}|{name}]]|{status_smiley}|{ows_machine_id.category_icon}|{ows_area}|{location}|{image}',
'dokuwiki.auto_update_overview_table': 'False', # Automatische Übersichtstabellen-Aktualisierung bei Equipment-Änderungen
}
# Nur fehlende Parameter anlegen