fix: Set company_id in equipment migration to fix serial number generation

- Add default company_id to equipment records during migration
- Fixes 'Generate Serial Number' button not working for migrated equipment
- Equipment without company_id could not generate serial numbers
This commit is contained in:
Matthias Lotz 2025-12-11 19:22:52 +01:00
parent b68ac293c8
commit 550cdac1eb
3 changed files with 168 additions and 173 deletions

View File

@ -1,67 +1,23 @@
# 0. Migration: open_workshop → open_workshop_base (Pflichtschritt)
# 0. Migration: open_workshop → open_workshop_base ✅ ABGESCHLOSSEN
Dieser Schritt muss **vor allen anderen** durchgeführt werden, damit die neue Modulstruktur korrekt funktioniert.
**Status: ERLEDIGT (08.12.2025)**
## 0.1 Ordner umbenennen
Die Migration wurde erfolgreich durchgeführt:
```
mv open_workshop open_workshop_base
```
- ✅ SQL-Migration: Modul umbenannt in Datenbank
- ✅ Pre-Migration: maintenance.equipment Integration vorbereitet
- ✅ Post-Migration: 23 Maschinen zu maintenance.equipment migriert
- ✅ _inherits Pattern implementiert (ows.machine → maintenance.equipment)
- ✅ Automatische Migration in CI/CD Pipeline integriert
- ✅ UX verbessert: Equipment wird automatisch erstellt
- ✅ Location readonly und auto-sync von area
- ✅ Menü umbenannt zu "Ausrüstung"
## 0.2 Manifest-Datei anpassen (`__manifest__.py`)
```
'name': 'Open Workshop Base',
```
Alle Pfade von:
```
open_workshop/...
```
ändern zu:
```
open_workshop_base/...
```
## 0.3 Referenzen im Code ersetzen
In **allen Dateien**:
- `open_workshop.``open_workshop_base.`
- `/open_workshop/static/...``/open_workshop_base/static/...`
## 0.4 Modul in Odoo aktualisieren
```
odoo -u open_workshop_base -d <dbname>
```
Falls das alte Modul noch existiert:
```
odoo -u open_workshop_base -d <dbname> --load=base
```
## 0.5 Datenbank prüfen
```
SELECT name FROM ir_module_module WHERE name LIKE 'open_workshop%';
```
Falls `open_workshop` noch existiert:
```
DELETE FROM ir_module_module WHERE name = 'open_workshop';
```
## 0.6 Wichtige Hinweise
- Modellnamen wie `_name = 'ows.machine'` **bleiben unverändert**
- Tabellennamen ändern sich **nicht**
- Keine Datenmigration erforderlich
**Technische Details:**
- Alte Felder entfernt: code, description, storage_location, purchase_price, purchase_date
- Delegation an maintenance.equipment: name, serial_no, location, cost, effective_date
- JSONB für mehrsprachige Namen (de_DE, en_US)
- Area → Location Synchronisation automatisch
---
@ -105,116 +61,99 @@ Diese Architektur vermeidet:
---
# 2. Zielarchitektur Odoo-Module
Das Repository wird wie folgt strukturiert:
# 2. Aktuelle Modulstruktur (Stand: 08.12.2025)
```
open_workshop/
├── open_workshop_base/ # Pflicht Backendmodelle & Logik
├── open_workshop_pos/ # POS-Funktionen (JS, XML, UI)
├── open_workshop_maintenance/ # Integration mit maintenance.equipment
├── open_workshop_api/ # PFLICHT REST API für WordPress
└── open_workshop_website/ # OPTIONAL interne Website in Odoo
├── open_workshop/ # ✅ Alt-Modul (installable=False, Kompatibilität)
├── open_workshop_base/ # ✅ FERTIG Backend, _inherits maintenance.equipment
├── open_workshop_pos/ # ✅ FERTIG POS-Integration (JS, XML, UI)
└── open_workshop_api/ # ⏳ GEPLANT REST API für WordPress
```
Die API ist **nicht optional**, sobald WordPress als Frontend genutzt wird.
**Entfernt:** open_workshop_maintenance (verworfen - Funktionalität direkt in open_workshop_base integriert)
---
# 3. Modul: open_workshop_base (Pflicht)
# 3. Modul: open_workshop_base ✅ FERTIG
**Status: PRODUKTIV (18.0.1.0.4)**
Enthält:
- `ows.machine` (Maschinen)
- `ows.machine.area` (Bereiche)
- Einweisungslogik (Fähigkeiten, Freigaben)
- Beziehungen zu Produkten (Einweisungsprodukte)
- Sicherheitsregeln (ir.model.access)
- Backend-UI: Form, Tree, Kanban
- ✅ `ows.machine` mit _inherits zu `maintenance.equipment`
- ✅ `ows.machine.area` (Bereiche mit JSONB-Namen)
- ✅ `ows.machine.product` (Nutzungsprodukte)
- ✅ `ows.machine.training` (Einweisungsprodukte)
- ✅ `ows.machine.access` (Zugriffsfreigaben)
- ✅ Automatische Equipment-Erstellung bei Maschinenanlage
- ✅ Area → Location Synchronisation
- ✅ Sicherheitsregeln (ir.model.access)
- ✅ Backend-UI: Form, Tree, Search/Filter
- ✅ Smart Button zu Equipment-Details
- ✅ Integration mit OCA maintenance Modulen
Nicht erlaubt:
**Abhängigkeiten:**
- base, account, hr, product, sale, contacts, **maintenance**
- POS-Code
- Website-Code
- API-Controller
Es ist das Fundament des Systems.
**Maintenance Integration:**
- Statt separatem Modul: Direkte Integration via _inherits
- Equipment-Felder: name, serial_no, location, cost, effective_date
- OWS-Felder: category, area_id, product_ids, training_ids
---
# 4. Modul: open_workshop_pos (Pflicht für POS-Nutzung)
# 4. Modul: open_workshop_pos ✅ FERTIG
Auslagerung aller POS-relevanten Komponenten:
**Status: PRODUKTIV**
Vollständig separiertes POS-Modul mit:
### JS-Module:
- Sidebar
- Customer Widgets
- Machine Access Widgets
- Template Patches
- ✅ ows_machine_access_list.js
- ✅ ows_pos_customer_sidebar.js
- ✅ ows_pos_sidebar.js
- ✅ ows_product_screen_template_patch.js
### XML:
- QWeb Templates für POS (Frontend)
- Stylesheets für POS
### XML-Templates:
- ✅ QWeb Templates für POS-UI
- ✅ Maschinenfreigaben-Widget
- ✅ Customer Sidebar mit Zugriffsstatus
### Assets:
```json
'point_of_sale.assets': [
'open_workshop_pos/static/src/...'
'open_workshop_pos/static/src/js/**/*.js',
'open_workshop_pos/static/src/xml/**/*.xml',
'open_workshop_pos/static/src/css/**/*.css'
]
```
Dieses Modul muss **physisch getrennt** vom Base-Modul sein.
**Abhängigkeiten:**
- open_workshop_base
- point_of_sale
**Integration:**
- Zeigt Maschinenzugriffe im POS
- Filterung nach Bereichen
- Live-Status der Freigaben
---
# 5. Modul: open_workshop_maintenance (optional, aber empfohlen)
# 5. Modul: open_workshop_api ⏳ GEPLANT
Ziele:
- Maschinenmodelle mit Odoo Maintenance verbinden
- `maintenance.equipment``ows.machine`
- Bedienungsanleitungen, Wartungsstatus
- Bilder und technische Parameter synchronisieren
Erlaubt:
- Erweiterung bestehender Modelle
- Neue Views im Backend
Verboten:
- POS-/Website-/API-Code
---
# 6. Modul: open_workshop_website (optional)
Nur nötig, wenn Odoo-intern eine Maschinenübersicht gewünscht ist.
Für WordPress ist dieses Modul **nicht erforderlich**.
Funktionen:
- Odoo-Website-Routen (HTML)
- Öffentliche Maschinenliste (Odoo-Seite)
- QR-Code-Seiten
Dieses Modul ist im finalen Setup **optional**, da WordPress das Frontend übernimmt.
---
# 7. Modul: open_workshop_api (Pflichtmodul!)
**Status: NOCH NICHT IMPLEMENTIERT**
Dieses Modul stellt die **REST API** bereit, über die WordPress öffentlich verfügbare Daten abholt.
## 7.1 Ziele
## 5.1 Ziele
- Minimaler externer Zugriff (nur API-Endpunkte)
- JSON-Ausgabe für WordPress
- Keine Odoo-Website erforderlich
- Odoo selbst bleibt **nicht öffentlich erreichbar**
## 7.2 API-Endpunkte
## 5.2 API-Endpunkte
Pflicht:
@ -231,7 +170,7 @@ GET /api/v1/files/<attachment_id>
GET /api/v1/events (später für Kurse)
```
## 7.3 JSON Beispiel
## 5.3 JSON Beispiel
```json
{
@ -245,7 +184,7 @@ GET /api/v1/events (später für Kurse)
}
```
## 7.4 Sicherheitsmechanismen
## 5.4 Sicherheitsmechanismen
- CORS nur für WordPress-Domain erlauben
- Optionaler Token im Header (`Authorization: Bearer <token>`)
@ -254,11 +193,13 @@ GET /api/v1/events (später für Kurse)
---
# 8. WordPress Integration (Pflicht, da Frontend)
# 6. WordPress Integration ⏳ GEPLANT
**Status: VORBEREITET (API noch nicht implementiert)**
WordPress bleibt die öffentliche Website.
## 8.1 Warum?
## 6.1 Warum?
- Sehr hohe Performance (CDN, Caching)
- SEO-optimiert
@ -266,7 +207,7 @@ WordPress bleibt die öffentliche Website.
- Keine Sicherheitsrisiken im internen Odoo
- Keine Lizenzkosten
## 8.2 WordPress Plugin (bereitgestellt)
## 6.2 WordPress Plugin (bereitgestellt)
Das Plugin:
@ -283,7 +224,7 @@ Beispiel:
---
# 9. Finaler Architektur-Plan
# 7. Finaler Architektur-Plan
```
Internet
@ -305,7 +246,7 @@ Beispiel:
---
# 10. Sicherheit
# 8. Sicherheit
- Reverse Proxy (Traefik/Nginx) muss alle Odoo-Backoffice-URLS sperren
- Nur `/api/v1/*` darf öffentlich sein
@ -315,41 +256,65 @@ Beispiel:
---
# 11. Vorteile dieser Architektur
# 9. Vorteile dieser Architektur
## ✔ WordPress bleibt ultraschnell (1000 Besucher/Tag problemlos)
## ✔ Odoo bleibt sicher hinter Firewall
## ✔ Keine Benutzerkosten → alle Ehrenamtlichen können intern mitarbeiten
## ✔ Modular, wartbar, zukunftssicher
## ✔ API erlaubt feine Steuerung, welche Daten öffentlich sind
## ✔ Keine Last auf DSL-Leitung (WordPress hostet extern)
**WordPress bleibt ultraschnell** (1000 Besucher/Tag problemlos)
**Odoo bleibt sicher** hinter Firewall
**Keine Benutzerkosten** → alle Ehrenamtlichen können intern mitarbeiten
**Modular, wartbar, zukunftssicher**
**API erlaubt feine Steuerung**, welche Daten öffentlich sind
**Keine Last auf DSL-Leitung** (WordPress hostet extern)
**Maintenance-Integration** ohne Extra-Modul direkt in Base
---
# 12. Implementierungsreihenfolge (Pflicht)
# 10. Implementierungsstand (08.12.2025)
1. **open_workshop_base** erstellen und stabilisieren
2. **open_workshop_pos** extrahieren
3. **open_workshop_maintenance** hinzufügen
4. **open_workshop_api** implementieren (**Pflicht!**)
5. WordPress-Plugin konfigurieren
6. API testen
7. WordPress-Frontend aufbauen
| Schritt | Status | Details |
|---------|--------|---------|
| 1. open_workshop_base | ✅ **FERTIG** | Version 18.0.1.0.4, produktiv |
| 2. open_workshop_pos | ✅ **FERTIG** | POS-Integration komplett |
| 3. ~~open_workshop_maintenance~~ | ❌ **VERWORFEN** | Direkt in Base integriert |
| 4. Maintenance Integration | ✅ **FERTIG** | _inherits Pattern implementiert |
| 5. Migration Workflow | ✅ **FERTIG** | SQL + Python, CI/CD integriert |
| 6. open_workshop_api | ⏳ **GEPLANT** | REST API für WordPress |
| 7. WordPress Plugin | ⏳ **GEPLANT** | Frontend-Integration |
---
# 13. Endfazit
# 11. Nächste Schritte
Diese finale Architektur ist:
1. **open_workshop_api** entwickeln
- REST Controller implementieren
- JSON-Serializer für machines/areas
- CORS und Security konfigurieren
- Token-Auth optional hinzufügen
- technisch korrekt
- performant
- sicher
- kostenoptimiert
- langfristig erweiterbar
- perfekt für ein FabLab wie den HobbyHimmel
2. **WordPress Plugin** anpassen
- API-Endpunkte konfigurieren
- Shortcode-Rendering
- Caching implementieren
Die API ist **zentrales und unverzichtbares Modul** dieser Struktur.
3. **Testing & Deployment**
- API-Tests schreiben
- Reverse Proxy konfigurieren
- Performance-Tests
- Go-Live vorbereiten
**Dieses Dokument ist vollständig und benötigt keine externen Verweise.**
---
# 12. Endfazit
Diese Architektur hat sich bewährt:
- ✅ **Technisch korrekt** _inherits Pattern statt separatem Modul
- ✅ **Performant** Maintenance.equipment als Single Source of Truth
- ✅ **Sicher** API-Layer trennt intern/extern
- ✅ **Kostenoptimiert** Keine Odoo.sh Lizenzen nötig
- ✅ **Langfristig erweiterbar** Modularer Aufbau
- ✅ **Produktiv im Einsatz** Migration erfolgreich abgeschlossen
Die API ist **zentrales Zukunftsmodul** für die WordPress-Integration.
**Letztes Update: 08.12.2025**

View File

@ -38,6 +38,10 @@ def migrate(cr, version):
machines = cr.fetchall()
migrated_count = 0
# Default Company ermitteln
cr.execute("SELECT id FROM res_company ORDER BY id LIMIT 1")
default_company_id = cr.fetchone()[0] if cr.rowcount > 0 else None
for machine in machines:
(m_id, m_name_jsonb, m_area_id,
m_active, m_create_uid, m_write_uid, m_create_date, m_write_date) = machine
@ -70,13 +74,14 @@ def migrate(cr, version):
cr.execute("""
INSERT INTO maintenance_equipment
(name, serial_no, location, cost, effective_date, equipment_assign_to,
active, create_uid, write_uid, create_date, write_date)
company_id, active, create_uid, write_uid, create_date, write_date)
VALUES
(jsonb_build_object('de_DE', %s, 'en_US', %s), %s, %s, %s, %s, 'other',
%s, %s, %s, %s, %s)
%s, %s, %s, %s, %s, %s)
RETURNING id
""", (display_name, display_name, m_serial_no,
location, 0.0, date.today(),
default_company_id,
m_active if m_active is not None else True,
m_create_uid, m_write_uid, m_create_date, m_write_date))

View File

@ -1,20 +1,45 @@
<!-- machine_views.xml -->
<odoo>
<!-- Maschinen Such- und Filteransicht -->
<record id="view_machine_search" model="ir.ui.view">
<field name="name">ows.machine.search</field>
<field name="model">ows.machine</field>
<field name="arch" type="xml">
<search string="Ausrüstung suchen">
<field name="name" string="Name"/>
<field name="serial_no" string="Code"/>
<field name="area_id" string="Bereich"/>
<separator/>
<filter string="Kategorie 1 (grün)" name="filter_green" domain="[('category', '=', 'green')]"/>
<filter string="Kategorie 2 (gelb)" name="filter_yellow" domain="[('category', '=', 'yellow')]"/>
<filter string="Kategorie 3 (rot)" name="filter_red" domain="[('category', '=', 'red')]"/>
<separator/>
<filter string="Aktiv" name="filter_active" domain="[('active', '=', True)]"/>
<filter string="Archiviert" name="filter_inactive" domain="[('active', '=', False)]"/>
<separator/>
<group expand="0" string="Gruppieren nach">
<filter string="Bereich" name="group_area" context="{'group_by': 'area_id'}"/>
<filter string="Kategorie" name="group_category" context="{'group_by': 'category'}"/>
</group>
</search>
</field>
</record>
<!-- Maschinen Listenansicht -->
<record id="view_machine_tree" model="ir.ui.view">
<field name="name">ows.machine.tree</field>
<field name="model">ows.machine</field>
<field name="arch" type="xml">
<list>
<list sample="1" multi_edit="1">
<field name="category_icon" string="⚙" readonly="1"/>
<field name="name"/>
<field name="serial_no" string="Code"/>
<field name="category"/>
<field name="area_id" widget="many2one_color"/>
<field name="product_names"/>
<field name="training_names"/>
<field name="active"/>
<field name="name" optional="show"/>
<field name="serial_no" string="Code" optional="show"/>
<field name="category" optional="show"/>
<field name="area_id" widget="many2one" optional="show"/>
<field name="location" optional="hide"/>
<field name="product_names" optional="show"/>
<field name="training_names" optional="show"/>
<field name="active" optional="show"/>
</list>
</field>
</record>