Improve product assignment UX with many2many_tags widget

- Add computed Many2many fields for direct product display in equipment view
- Implement name_get() methods for OwsMachineProduct and OwsMachineTraining
- Apply category filters to product selection (Maschinennutzung, Einweisungen, Kurse)
- Update README.md with comprehensive module documentation
This commit is contained in:
Matthias Lotz 2026-02-07 19:12:15 +01:00
parent 5a27dc6a65
commit 8a414ed3ac
6 changed files with 484 additions and 48 deletions

416
README.md
View File

@ -1 +1,415 @@
Abhängigkeiten von maintenance erklären.
# Open Workshop
**POS-basiertes Maschinenfreigabe- und Werkstattverwaltungssystem für Odoo**
Open Workshop ist eine modulare Erweiterung des Odoo POS-Systems zur Verwaltung von einweisungspflichtigen Maschinen und Geräten in Werkstätten, Makerspaces und FabLabs. Das System ermöglicht die Verwaltung von Maschinenfreigaben, Einweisungen und Equipment-Dokumentation.
---
## Hauptfunktionen
- **Maschinenfreigaben**: Verwaltung von kundenspezifischen Freigaben für einweisungspflichtige Geräte
- **Sicherheitskategorien**: Dreistufiges Kategoriesystem (grün/gelb/rot) für unterschiedliche Einweisungspflichten
- **POS-Integration**: Verkauf von Einweisungen und Anzeige von Maschinenfreigaben direkt am Point of Sale
- **Equipment-Verwaltung**: Vollständige Integration mit dem OCA Maintenance-Modul
- **Dokumentation**: Automatische Synchronisation von Equipment-Daten mit DokuWiki
- **Mitarbeiter-Display**: Dynamische Anzeige von Mitarbeiterdaten im POS Customer Display
---
## Module
### open_workshop_base
**Kernmodul** - Stellt die Basis-Funktionalität für das gesamte System bereit.
**Hauptfunktionen:**
- Maschinenmodelle mit Sicherheitskategorien (grün/gelb/rot)
- Bereiche (z.B. Holz, Metall, FabLab)
- Freigabeverwaltung (wer darf welche Maschine nutzen)
- Produktverknüpfungen für Einweisungen und Nutzung
- Integration mit OCA maintenance.equipment als Single Source of Truth
**Datenmodelle:**
- `ows.user` - Benutzerdaten (RFID, Geburtstag, Sicherheitsunterweisung)
- `ows.machine.area` - Maschinenbereiche mit Farbkodierung
- `ows.machine` - Maschinen (nutzt _inherits Pattern mit maintenance.equipment)
- `ows.machine.access` - Freigaben (Partner ↔ Maschine)
- `ows.machine.product` - Nutzungsprodukte für Maschinen
- `ows.machine.training` - Einweisungsprodukte für Maschinen
**Abhängigkeiten:**
- OCA `maintenance` (Basis-Equipment-Verwaltung)
- OCA `maintenance_equipment_status` (Equipment-Statusverwaltung)
```bash
git clone https://github.com/OCA/maintenance.git
cd maintenance
git reset --hard 5510275e
```
---
### open_workshop_pos
**POS-Integration** - Erweitert den Odoo Point of Sale um Maschinenfreigabe-Funktionalität.
**Hauptfunktionen:**
- **Machine Access List**: Übersichtsanzeige aller verfügbaren Maschinen welche Einweisungspflichtig sind, mit Freigabestatus für den aktuellen Kunden (roter Punkt)
- **Customer Sidebar**: Kundenspezifische Maschinenfreigaben am POS
- **POS Sidebar**: Maschinenauswahl und Statusanzeige
- **Automatische Freigabenvergabe**: Beim Verkauf eines Einweisungsprodukts wird dem Kunden automatisch die Freigabe für das zugehörige Gerät in der Datenbank erteilt
**Verwendungszweck:**
Verkauf von Maschineneinweisungen und Anzeige bestehender Freigaben direkt am Kassensystem. Ermöglicht schnelle Prüfung, welcher Kunde auf welche einweisungspflichtigen Geräte zugreifen darf.
**Abhängigkeiten:**
- `open_workshop_base`
- `point_of_sale`
---
### open_workshop_pos_customer_display
**Mitarbeiter-Display** - Zeigt dynamisches Mitarbeiter-Namensschild im POS Customer Display.
**Hauptfunktionen:**
- Anzeige von Mitarbeiterfoto (generiert durch open_workshop_employee_imagegenerator)
- Darstellung von Name und Schwerpunkten (job_focus)
- Automatische Aktualisierung bei Kassiererwechsel
- Responsive Design mit Fallback auf Company-Logo
**Verwendungszweck:**
Darstellung der Mitarbeitenden am Kundendisplay, verbessert die Kundeninteraktion und Transparenz.
**Abhängigkeiten:**
- `point_of_sale`
- `hr`
---
### open_workshop_dokuwiki
**DokuWiki-Integration** - Synchronisiert Equipment-Daten aus Odoo mit einem DokuWiki-System.
**Hauptfunktionen:**
- Automatische Erstellung von Wiki-Seiten für Equipment
- Übersichtstabelle aller Equipment (DataTables mit Sortierung/Filterung)
- Status-Seiten für Include-Plugin (nur von Odoo generiert, read-only)
- Smart Button "Wiki öffnen" im Equipment-Formular
- Automatische Synchronisation bei Equipment-Änderungen
**Architektur-Prinzip:**
- Odoo generiert **NUR** `odoo-status/` Seiten (maschinengeneriert, read-only)
- Benutzer erstellen eigene Dokumentation in `{bereich}/` Namespaces
- Einbindung der Odoo-Daten via DokuWiki Include-Plugin
**Verwendungszweck:**
Zentrale Dokumentation aller Geräte mit automatisch aktualisiertem Status aus Odoo, kombiniert mit benutzergenerierten Anleitungen und Wartungshinweisen.
**Abhängigkeiten:**
- `open_workshop_base`
- `maintenance`
- `maintenance_equipment_status`
---
### 🎨 open_workshop_employee_imagegenerator
**Mitarbeiterfoto-Generator** - Upload und Zuschnitt von Mitarbeiterfotos direkt in Odoo.
**Hauptfunktionen:**
- Upload und Zuschnitt von Fotos auf festes Format (369x492 Pixel)
- Hinzufügen von Schwerpunktbereichen (job_focus)
- Integrierter Cropper.js für professionelle Bildbearbeitung
- Fester Crop-Frame (nur Bild bewegt sich, nicht der Rahmen)
- Integration im Employee-Formular über Button "Namensschild erstellen"
**Verwendungszweck:**
Erstellung von professionellen Mitarbeiterfotos für das POS Customer Display. Vereinfacht den Workflow durch direkten Upload in Odoo ohne externe Bildbearbeitung.
**Abhängigkeiten:**
- `hr`
- `web`
---
## Architektur & Datenfluss
### Datenmodell-Übersicht
```
┌─────────────────────────────────────────────────────────────┐
│ OCA maintenance.equipment │
│ (Single Source of Truth für name, serial_no, cost, etc.) │
└──────────────────────┬──────────────────────────────────────┘
│ _inherits (Delegation Pattern)
┌────────────────┐
│ ows.machine │ ◄────────┐
│ (OWS-Features) │ │
└────┬───────────┘ │
│ │
┌─────────┼──────────────────────┤
│ │ │
│ ▼ │
│ ┌──────────────────┐ │
│ │ ows.machine.area │ │
│ │ (Bereiche) │ │
│ └──────────────────┘ │
│ │
▼ │
┌──────────────────┐ ┌────────┴─────────┐
│ ows.machine. │ │ ows.machine. │
│ product │ │ training │
│ (Nutzung) │ │ (Einweisung) │
└──────────────────┘ └──────────────────┘
┌──────────────────┐ ┌──────────────────┐
│ res.partner │ │ ows.user │
│ (Kunde) │◄───────────│ (Benutzerdaten) │
└────────┬─────────┘ 1:1 └──────────────────┘
│ M:N
┌──────────────────┐ ┌──────────────────┐
│ ows.machine. │ │ ows.machine │
│ access │───────────►│ │
│ (Freigaben) │ │ │
└──────────────────┘ └──────────────────┘
```
### _inherits Pattern Erklärung
Das Modul nutzt das Odoo **_inherits Pattern** (Delegation/Prototypische Vererbung) für `ows.machine`:
**Wie es funktioniert:**
- `ows.machine` delegiert Basis-Felder an `maintenance.equipment`
- Beim Erstellen einer `ows.machine` wird automatisch ein `maintenance.equipment` erstellt
- Felder wie `name`, `serial_no`, `cost`, `location` werden direkt von equipment übernommen
- **Keine Datenduplizierung!** Ein Equipment-Datensatz, zwei Modell-Sichten
**Vorteile:**
- `maintenance.equipment` bleibt die **Single Source of Truth** für Stammdaten
- Kompatibilität mit allen OCA Maintenance-Modulen
- `ows.machine` fügt nur OWS-spezifische Felder hinzu (category, area_id, etc.)
- Nahtlose Integration in bestehendes Maintenance-System
**Beispiel:**
```python
class OwsMachine(models.Model):
_name = 'ows.machine'
_inherits = {'maintenance.equipment': 'equipment_id'}
equipment_id = fields.Many2one('maintenance.equipment', required=True, ondelete='cascade')
category = fields.Selection(...) # OWS-spezifisch
area_id = fields.Many2one(...) # OWS-spezifisch
```
Beim Zugriff auf `machine.name` wird automatisch `machine.equipment_id.name` zurückgegeben.
---
## 🔄 Modul-Zusammenhänge
### Installation & Abhängigkeiten
```
OCA maintenance ─────┐
OCA maintenance_ │
equipment_status ──┤
open_workshop_base ◄──────────┐
│ │
┌───────────┼─────────────┐ │
│ │ │ │
▼ ▼ ▼ │
open_workshop open_workshop open_workshop
_pos _dokuwiki _employee_
imagegenerator
│ │
▼ │
open_workshop_pos_ │
customer_display ◄───────────────┘
```
**Installation:**
Die OCA Module `maintenance` und `maintenance_equipment_status` müssen im `addons_path` verfügbar sein. Sie werden automatisch mit installiert, wenn `open_workshop_base` installiert wird (siehe `depends` in der `__manifest__.py`).
```bash
git clone https://github.com/OCA/maintenance.git
cd maintenance
git reset --hard 5510275e
```
**Installationsreihenfolge:**
1. `open_workshop_base` (Kernmodul - installiert OCA-Abhängigkeiten automatisch)
2. Optionale Module je nach Bedarf:
- `open_workshop_pos` für POS-Integration
- `open_workshop_dokuwiki` für Wiki-Dokumentation
- `open_workshop_employee_imagegenerator` für Mitarbeiterfotos
- `open_workshop_pos_customer_display` für Customer Display (benötigt imagegenerator)
---
## 🛠️ Entwicklungshinweise
### Wo finde ich was?
#### open_workshop_base
- **Models**: `/models/ows_models.py` - Alle Kern-Datenmodelle
- **Views**: `/views/` - Backend-Ansichten (Form, Tree, Kanban)
- Erweiterte Views:
- `res.partner` (Kontakte): Formular, Listenansicht - Maschinenfreigaben-Tab
- `maintenance.equipment`: Formular, Listenansicht, Suchansicht - OWS-Felder
- **Security**: `/security/ir.model.access.csv` - Zugriffsrechte
- **Data**: `/data/` - Stammdaten (Equipment-Status, Bereiche)
#### open_workshop_pos
- **JavaScript**: `/static/src/js/` - POS Frontend-Logik
- `ows_pos_sidebar.js` - POS Maschinensidebar
- `ows_pos_customer_sidebar.js` - Kundensidebar
- `ows_machine_access_list.js` - Zugriffsliste
- **Templates**: `/static/src/xml/` - OWL Templates für POS UI
- **Models**: `/models/pos_order.py` - POS Order Extensions
#### open_workshop_dokuwiki
- **Models**: `/models/`
- `dokuwiki_client.py` - DokuWiki XML-RPC Client
- `maintenance_equipment.py` - Equipment Extensions
- **Views**: `/views/`
- Erweiterte Views:
- `maintenance.equipment`: Smart Button "Wiki öffnen"
- `maintenance.equipment.status`: DokuWiki-Synchronisation
- **Wizard**: `/wizard/` - Synchronisations-Assistenten
#### open_workshop_employee_imagegenerator
- **JavaScript**: `/static/src/js/employee_image_widget.js` - Cropper Widget
- **Templates**: `/static/src/xml/employee_image_widget.xml` - Widget UI
- **Views**: `/views/`
- Erweiterte Views:
- `hr.employee`: Button "Namensschild erstellen" im Formular
- **CSS**: `/static/src/css/` - Badge-Styling
- **Library**: `/static/lib/cropperjs/` - Cropper.js Integration
#### open_workshop_pos_customer_display
- **JavaScript**: `/static/src/js/customer_display.js` - Display-Logik
- **CSS**: `/static/src/css/employee_badge.css` - Badge-Styling
### Wichtige Methoden & RPC Calls
#### open_workshop_base
- `ows.machine.get_access_list_grouped(partner_id)` - Gruppierte Zugriffsliste für POS
- Liefert alle Bereiche mit Maschinen und Zugriffssstatus für einen Partner
- Wird vom POS Frontend verwendet
#### res.partner Extensions
- `machine_access_ids` - One2many zu allen Freigaben
- `machine_access_html` - Computed HTML-Tabelle für Backend-Ansicht im Res.partner / Kontakt Formular
- `ows_user_id` - One2many zu ows.user (Benutzerdaten)
### Erweiterung des Systems
**Neue Sicherheitskategorie hinzufügen:**
1. In `ows_models.py``OwsMachine.category` Selection erweitern
2. Icon-Mapping in `_compute_category_icon()` aktualisieren
3. CSS in `/static/src/css/category_color.css` ergänzen
**Neuen Maschinenbereich erstellen:**
- Im Backend: **Wartung → Konfiguration → Bereiche**
- Oder via Data-File in `/data/` für Standardbereiche
**POS-UI anpassen:**
- OWL Templates in `/static/src/xml/` bearbeiten
- JavaScript-Komponenten in `/static/src/js/` erweitern
- CSS in `/static/src/css/` anpassen
---
## 📝 Technische Details
### Backend-Konfiguration
**Sicherheitskategorien zuweisen:**
- Menü: **Wartung → Ausrüstung** → [Equipment auswählen]
- Im Formular: Feld "Sicherheitskategorie" (🟢/🟡/🔴)
- Die Kategorie ist direkt am `ows.machine` / `maintenance.equipment` gespeichert
**Einweisungsprodukte zuweisen:**
- Menü: **Wartung → Ausrüstung** → [Equipment auswählen]
- Im Formular: Tab "Einweisungsprodukte"
- Button "Hinzufügen" → Produkt aus `product.product` auswählen
- Speichert Verknüpfung in `ows.machine.training` (Many2many über One2many)
- Beim Verkauf dieses Produkts am POS wird automatisch die Freigabe erteilt
**Nutzungsprodukte zuweisen:**
- Gleicher Weg wie Einweisungsprodukte
- Tab "Nutzungsprodukte" im Equipment-Formular
- Speichert Verknüpfung in `ows.machine.product`
- Diese Produkte können für Nutzungsgebühren verwendet werden
**Maschinenfreigaben manuell vergeben:**
- Menü: **Kontakte** → [Partner auswählen]
- Tab "Maschinenfreigaben" (von open_workshop_base hinzugefügt)
- Übersicht aller Bereiche und Maschinen mit aktuellem Freigabestatus
- Freigaben werden jedoch hauptsächlich automatisch über POS-Verkäufe vergeben
**Konfiguration:**
- **Bereiche**: Wartung → Konfiguration → Bereiche
- **Produkt-Zuordnungen**: Wartung → Konfiguration → Zuordnungen
- Nutzungsprodukte (Übersicht aller Zuordnungen)
- Einweisungsprodukte (Übersicht aller Zuordnungen)
---
### Sicherheitskategorien
- **🟢 Grün (Kategorie 1)**: Keine Einweisungspflicht, freier Zugang
- **🟡 Gelb (Kategorie 2)**: Empfohlene Einweisung, optionale Freigabe
- **🔴 Rot (Kategorie 3)**: Einweisung zwingend erforderlich, POS zeigt nur rote Maschinen
### Freigabenverwaltung
Freigaben werden in `ows.machine.access` gespeichert mit:
- Partner (Kunde)
- Maschine
- Datum der Freigabe
- Optional: Ablaufdatum
- Herkunft (granted_by_pos)
### DokuWiki Namespace-Struktur
```
{equipment_namespace}:
├── overview # Übersichtstabelle (von Odoo)
└── odoo-status/ # Maschinengeneriert (read-only)
├── maschine_1 # Status-Seite für Include
├── maschine_2
└── c_template # Template für Status-Seiten
{bereich}: # Benutzer-Namespaces
├── holz/
│ ├── maschine_1 # Benutzer-Dokumentation
│ └── maschine_2 # (inkludiert odoo-status via {{page>}})
└── metall/
└── ...
```
---
## 📄 Lizenz
AGPL-3 (open_workshop_base)
LGPL-3 (weitere Module)
---
## 👤 Autor
Matthias Lotz / Hobbyhimmel
---
## 🔗 Verwandte Projekte
- [OCA Maintenance](https://github.com/OCA/maintenance) - Basis Equipment-Verwaltung

View File

@ -1,44 +0,0 @@
# ✅ OpenWorkshop Test Checkliste
## 🔹 1. Migration
- [ ] `migrate_existing_partners()` erzeugt zu jedem Partner genau einen `ows.user`.
- [ ] `migrate_existing_partners()` übernimmt korrekt alte `vvow_*`-Felder.
- [ ] `migrate_machine_access_from_old_fields()` erstellt korrekte Einträge in `ows.machine.access`.
- [ ] `migrate_machine_access_from_old_fields()` übernimmt das Änderungsdatum aus `mail.tracking.value`.
## 🔹 2. Kontakte Backend
- [ ] Beim Anlegen eines neuen Partners wird automatisch ein `ows.user` angelegt.
- [ ] Änderungen an Geburtstag, RFID, Haftung in Partner-Formular schreiben korrekt in `ows.user`.
- [ ] Die Werte aus `ows.user` werden korrekt im Partnerformular angezeigt (via `compute`).
- [ ] Das HTML-Widget mit Maschinenfreigaben (`machine_access_html`) wird korrekt dargestellt.
## 🔹 3. POS-Integration
- [ ] Felder aus `ows.user` (Geburtstag, RFID etc.) erscheinen im POS-Kunden-Popup.
- [ ] Maschinenfreigaben erscheinen im POS-Layout korrekt gruppiert nach Bereichen.
- [ ] Farben der Maschinenbereiche werden korrekt aus `color_hex` übernommen.
## 🔹 4. Maschinenverwaltung
- [ ] Maschinen-Formular zeigt Nutzungs- und Einweisungsprodukte korrekt an.
- [ ] Drop-downs in den Produktlisten zeigen nur Produkte der richtigen Kategorie.
- [ ] Neue Zuordnungen können direkt in den Tree-Ansichten editiert werden.
- [ ] Filter greifen korrekt (Maschinennutzung / Einweisungen).
## 🔹 5. Menüstruktur
- [ ] Menüeinträge "Nutzungsprodukte" und "Einweisungsprodukte" erscheinen unter Konfiguration > Maschinen.
- [ ] Klick auf "Alle Maschinen" öffnet die erwartete Listenansicht.
## 🔹 6. CSV/XML Demo-/Initialdaten
- [ ] Maschinenbereiche (`ows.machine.area`) sind korrekt aus `data.xml` geladen.
- [ ] Maschinen und ihre Produkt-Zuordnungen sind vollständig.
- [ ] Kategorien und Produkte sind korrekt verknüpft (`product.category`, `product.product`).
## 🔹 7. Systemweite Konsistenz
- [ ] Es gibt keine doppelten `ows.user`-Einträge.
- [ ] Kein Partner existiert ohne zugehörigen `ows.user`.
- [ ] `res.partner.ows_user_id` ist immer gefüllt.
## 🔹 8. Technische Qualität
- [ ] Kein `@api.depends('id')` mehr vorhanden.
- [ ] Commit wird in Migrationsfunktionen korrekt gesetzt (`self.env.cr.commit()`).
- [ ] Keine toten `vvow_*` Felder mehr im Modell (wenn auf ows.user umgestellt).
- [ ] post-init und pre-load Skripte laufen fehlerfrei bei Neuinstallation.

View File

@ -1,7 +1,7 @@
{
'name': 'Open Workshop Base',
'license': 'AGPL-3',
'version': '18.0.1.0.4', # Migration läuft bei 18.0.1.0.4
'version': '18.0.1.0.5', # Migration läuft bei 18.0.1.0.4
'summary': 'Kern-Modul für Maschinenfreigaben - vereinfachte Equipment-Integration',
'depends': ['base', 'account', 'hr', 'product', 'sale', 'contacts', 'maintenance', 'maintenance_equipment_status'],
'author': 'matthias.lotz',

View File

@ -438,6 +438,54 @@ class MaintenanceEquipment(models.Model):
string='Einweisungsprodukte',
readonly=False
)
ows_usage_product_ids = fields.Many2many(
'product.product',
compute='_compute_usage_products',
inverse='_inverse_usage_products',
string='Nutzungsprodukte',
store=False
)
ows_training_product_ids = fields.Many2many(
'product.product',
compute='_compute_training_products',
inverse='_inverse_training_products',
string='Einweisungsprodukte',
store=False
)
@api.depends('ows_product_ids.product_id')
def _compute_usage_products(self):
for record in self:
record.ows_usage_product_ids = record.ows_product_ids.mapped('product_id')
def _inverse_usage_products(self):
for record in self:
if record.ows_machine_id:
# Entferne alle bisherigen
record.ows_machine_id.product_ids.unlink()
# Füge neue hinzu
for product in record.ows_usage_product_ids:
self.env['ows.machine.product'].create({
'machine_id': record.ows_machine_id.id,
'product_id': product.id
})
@api.depends('ows_training_ids.training_id')
def _compute_training_products(self):
for record in self:
record.ows_training_product_ids = record.ows_training_ids.mapped('training_id')
def _inverse_training_products(self):
for record in self:
if record.ows_machine_id:
# Entferne alle bisherigen
record.ows_machine_id.training_ids.unlink()
# Füge neue hinzu
for product in record.ows_training_product_ids:
self.env['ows.machine.training'].create({
'machine_id': record.ows_machine_id.id,
'training_id': product.id
})
@api.model_create_multi
def create(self, vals_list):
@ -730,6 +778,14 @@ class OwsMachineProduct(models.Model):
product_id = fields.Many2one('product.product', required=True, domain=[('available_in_pos', '=', True)], ondelete='cascade')
machine_id = fields.Many2one('ows.machine', required=True, ondelete='cascade')
def name_get(self):
"""Zeigt den Produktnamen statt ows.machine.product,ID"""
result = []
for record in self:
name = record.product_id.name if record.product_id else 'Unbenannt'
result.append((record.id, name))
return result
class OwsMachineTraining(models.Model):
_name = 'ows.machine.training'
_table = 'ows_machine_training'
@ -737,3 +793,11 @@ class OwsMachineTraining(models.Model):
training_id = fields.Many2one('product.product', required=True, domain=[('available_in_pos', '=', True)], ondelete='cascade')
machine_id = fields.Many2one('ows.machine', required=True, ondelete='cascade')
def name_get(self):
"""Zeigt den Produktnamen statt ows.machine.training,ID"""
result = []
for record in self:
name = record.training_id.name if record.training_id else 'Unbenannt'
result.append((record.id, name))
return result

View File

@ -17,6 +17,7 @@
<field name="model">ows.machine.product</field>
<field name="arch" type="xml">
<list editable="bottom">
<field name="machine_id" column_invisible="1"/>
<field name="product_id" domain="[('categ_id.name', '=', 'Maschinennutzung')]"/>
</list>
</field>
@ -40,6 +41,7 @@
<field name="model">ows.machine.training</field>
<field name="arch" type="xml">
<list editable="bottom">
<field name="machine_id" column_invisible="1"/>
<field name="training_id" domain="[('categ_id.name', 'in', ['Einweisungen', 'Kurse'])]"/>
</list>
</field>

View File

@ -24,10 +24,10 @@
</group>
</group>
<group string="Nutzungsprodukte">
<field name="ows_product_ids" nolabel="1" context="{'tree_view_ref': 'open_workshop_base.view_ows_machine_product_tree_simple'}"/>
<field name="ows_usage_product_ids" nolabel="1" widget="many2many_tags" options="{'no_create': True}" domain="[('categ_id.name', '=', 'Maschinennutzung')]"/>
</group>
<group string="Einweisungsprodukte">
<field name="ows_training_ids" nolabel="1" context="{'tree_view_ref': 'open_workshop_base.view_ows_machine_training_tree_simple'}"/>
<field name="ows_training_product_ids" nolabel="1" widget="many2many_tags" options="{'no_create': True}" domain="[('categ_id.name', 'in', ['Einweisungen', 'Kurse'])]"/>
</group>
</page>
</xpath>