Upgrade von OpenERP 6.1 auf 7.0

OpenERP ist ohne Zweifel ein interessantes und vermutlich das am weitesten entwickelte Open-Source ERP-System. Der Knackpunkt bei dem System ist allerdings das Upgrade von einer Major-Version auf die nächste, also z. B. von 6.1 auf 7.0 oder 8.0. Dieser wird im Rahmen des Geschäftsmodells von OpenERP s.a. ausschließlich Kunden mit Supportvertrag ermöglicht.

Es gibt allerdings einige Ansätze, dieses Upgrade auch ohne Vertrag mit OpenERP s.a. durchzuführen. Nachdem ich bei der Firma FENECON im Jahr 2012 im Rahmen meiner Bachelorarbeit OpenERP 6.1 eingeführt hatte, habe ich nun etwas Zeit gefunden, mich gemeinsam mit Herrn Farkas vom IT-Dienstleister Coreser mit diesen Ansätzen zu beschäftigen.

Das bekannteste freie Projekt zum Thema ist „OpenUpgrade„. Es nutzt eine in OpenERP vorhandene Upgrade-Schnittstelle, die vermutlich auch von OpenERP s.a. intern genutzt wird, und migriert damit eine komplette Datenbank 1-zu-1. Dazu werden für jedes Addon Migrationsskripte geschrieben, die dann in einem eigens angepassten OpenERP-Server ausgeführt werden. OpenUpgrade ist in seinem Vorgehen sehr umfangreich und komplex – und das ist wohl auch sein größter Nachteil. Ich habe die Migration meiner Datenbank jedenfalls nach vielen Versuchen aufgegeben.

Bei der Recherche bin ich dann aber auf das Projekt „anybox.migration.openerp“ gestoßen, von dem ich vorher noch nie etwas gehört hatte, obwohl ich die OpenERP-Welt seit mehreren Jahren mitverfolge. Um es etwas bekannter zu machen gibt es diesen Blog-Eintrag.

Anybox.migration verwendet CSV-Dateien, die aus der alten OpenERP-Datenbank exportiert, weiter verarbeitet und schließlich in eine leere OpenERP-Datenbank importiert werden. Das schöne an dem Tool ist, dass zu jeder Zeit sehr transparent ist, was gerade geschieht. Die gesamte Konfiguration beschränkt sich auf eine einzige Konfigurationsdatei die beschreibt, welche Tabellenspalten wohin migriert werden. Beispiel:

base:
    # Partners
    res_partner.*:
    res_partner.name:
        res_partner.name:
        res_partner.display_name:
    res_partner.__discriminator__:
        - name

Die mitgelieferte Datei „openerp6.1-openerp7.0.yml“ ist schon sehr umfangreich und deckt viele Bereiche ab. Für meine Zwecke musste ich sie nur leicht ergänzen bzw. abändern.

anybox.migration.openerp

Installation

Die Installation ist auf bitbucket wie folgt beschrieben:

virtualenv sandbox
sandbox/bin/pip install anybox.migration.openerp
sandbox/bin/migrate -h

Migration

Danach kann mit einem Befehl wie diesem, die Migration durchgeführt werden:

sandbox/bin/migrate -s source_dbname \
        -t target_dbname \
        -r tabelle1 \
           tabelle2 \
        -p openerp6.1-openerp7.0.yml \
           custom.yml
  • -s source_dbname ist der Name der OpenERP-6.1-Datenbank
  • -t target_dbname ist eine OpenERP-7.0-Datenbank, in der die nötigen Module installiert wurden, die aber noch keine Daten enthält
  • -r tabelle1 tabelle2 sind die Datenbanktabellen der OpenERP-6.1-Datenbank, die migriert werden sollen
  • -p openerp6.1-openerp7.0.yml custom.yml sind die Konfigurationsdateien

Beispiel: Migration der Partner (res_partner)

In diesem Beispiel sollen von einer OpenERP 6.1 Demo-Datenbank die Partner nach OpenERP 7.0 übernommen werden.

1. Quell-Datenbank vorbereiten

Erstellen einer OpenERP 6.1-Datenbank „demo_v61“ mit Demo-Daten:

OpenERP6.1-Demo-Datenbank

2. Ziel-Datenbank vorbereiten

Erstellen einer OpenERP 7.0-Datenbank „demo_v7“ ohne Demo-Daten:

OpenERP7.0-Datenbank

3. Migration von res_partner

Als erstes kopieren wir die Konfigurationsdatei in das aktuelle Verzeichnis, da wir daran sicher noch ein paar Änderungen vornehmen werden müssen.

cp ./sandbox/lib/python2.7/site-packages/anygrate/mappings/openerp6.1-openerp7.0.yml .

Anschließend starten wir den ersten Versuch:

sandbox/bin/migrate \
 -s demo_v61 \
 -t demo_v7 \
 -p openerp6.1-openerp7.0.yml \
 -r res_partner

Das Ergebnis sieht so aus:

Computing the real list of tables to export...
WARNING:depending.pyc:Dependency LOOP: res_company has a m2o to res_partner which is one of its ancestors (path=('res_partner',))
WARNING:depending.pyc:Dependency LOOP: res_currency has a m2o to res_company which is one of its ancestors (path=('res_partner', 'res_company'))
[1] The real list of tables to export is: res_users, res_partner_title, res_currency, res_company, res_partner
Exporting tables as CSV files...
WARNING:mapping.pyc:Mapping is not complete: module "web_view_editor" is missing!
WARNING:mapping.pyc:Mapping is not complete: module "web_kanban" is missing!
WARNING:mapping.pyc:Mapping is not complete: module "web_graph" is missing!
WARNING:mapping.pyc:Mapping is not complete: module "web_gantt" is missing!
WARNING:mapping.pyc:Mapping is not complete: module "web_diagram" is missing!
WARNING:mapping.pyc:Mapping is not complete: module "web_calendar" is missing!
WARNING:mapping.pyc:Mapping is not complete: module "process" is missing!
WARNING:mapping.pyc:Mapping is not complete: module "base_import" is missing!
WARNING:mapping.pyc:Mapping is not complete: module "web_tests" is missing!
WARNING:mapping.pyc:Mapping is not complete: module "web" is missing!
The real list of tables to import is: res_users, res_partner_title, res_currency, res_company, res_partner
Computing the list of Foreign Keys to update in the target csv files...
Migrating CSV files...
INFO:processing.pyc:Processing CSV files...
INFO:processing.pyc:Postprocessing CSV files...
Trying to import data in the target database...
INFO:importing.pyc:BRUTE FORCE LOOP
WARNING:importing.pyc:Error importing file res_users.target2.csv:
insert or update on table "res_users" violates foreign key constraint "res_users_partner_id_fkey"
DETAIL:  Key (partner_id)=(1937) is not present in table "res_partner".

INFO:importing.pyc:Succesfully imported res_partner_title.target2.csv
INFO:importing.pyc:Succesfully imported res_currency.target2.csv
INFO:importing.pyc:Succesfully imported res_company.target2.csv
WARNING:importing.pyc:Error importing file res_partner.target2.csv:
column "display_name" of relation "res_partner" does not exist

INFO:importing.pyc:BRUTE FORCE LOOP
WARNING:importing.pyc:Error importing file res_users.target2.csv:
insert or update on table "res_users" violates foreign key constraint "res_users_partner_id_fkey"
[2] DETAIL:  Key (partner_id)=(1937) is not present in table "res_partner".

WARNING:importing.pyc:Error importing file res_partner.target2.csv:
[3] column "display_name" of relation "res_partner" does not exist

ERROR:importing.pyc:

***
* Could not import remaining tables : res_users, res_partner :-(
***

Zur Erklärung:

[1]: Aufgrund von internen Abhängigkeiten (Datenbank-Constraints) wird nicht nur die Tabelle res_partner migriert, sondern auch die Tabellen res_users, res_partner_title, res_currency, res_company

[2]: Beim Migrieren der Tabelle res_users taucht ein Fehler auf, weil der entsprechende Eintrag in Tabelle res_partner nicht vorhanden ist. Die Erklärung dafür folgt in [3].

[3]: Die Tabelle res_partner konnte nicht migriert werden, weil die Spalte „display_name“ dort nicht existiert.

Ein kurzer Blick in die Datenbank mittels pgAdmin verrät, dass es diese Spalte tatsächlich nicht gibt, dort gibt es nur eine Spalte „name“. Die Konfigurationsdatei ist also fehlerhaft und muss abgeändert werden:

nano openerp6.1-openerp7.0.yml

Mit „Strg“ + „W“ suchen wir nach „display_name“ und finden folgende Zeilen:

base:
    [...]
    res_users.name:
        res_partner.name:
        res_partner.display_name:
    [...]
    res_partner.name:
        res_partner.name:
        res_partner.display_name:

Wenn das Modul „base“ installiert ist (was immer der Fall ist), wird bei der Migration der „name“ von „res_users“ und von „res_partner“ also nach „res_partner.name“ und „res_partner.display_name“ kopiert. Da es „res_partner.display_name“ nicht gibt, können wir die beiden Zeilen mit einem „#“ auskommentieren:

#        res_partner.display_name:

Mit „Strg“ + „X“ und „J“ speichern wir die Datei.

Nächster Versuch:

sandbox/bin/migrate \
  -s demo_v61 \
  -t demo_v7 \
  -p openerp6.1-openerp7.0.yml \
  -r res_partner

Mit dem Ergebnis:

[...]
[1] INFO:importing.pyc:Succesfully imported res_partner_title.target2.csv
[1] INFO:importing.pyc:Succesfully imported res_currency.target2.csv
[1] INFO:importing.pyc:Succesfully imported res_company.target2.csv
[1] INFO:importing.pyc:Succesfully imported res_partner.target2.csv
INFO:importing.pyc:BRUTE FORCE LOOP
[1] INFO:importing.pyc:Succesfully imported res_users.target2.csv
INFO:importing.pyc:

***
[2] * Successfully imported all csv files!! :-)
***

[...]
[3] Finished \o/ Use --write to really write to the target database

Erklärung:

[1] Die gelisteten Tabellen wurden erfolgreich migriert

[2] Darauf haben wir gehofft: die gesamte Migration war erfolgreich

[3] Wir können nun mit dem Paramter „-w“ oder „–write“ die Migration mit Schreibzugriff durchführen

sandbox/bin/migrate \
  -w \
  -s demo_v61 \
  -t demo_v7 \
  -p openerp6.1-openerp7.0.yml \
  -r res_partner

Der Webclient zeigt nun unter der Adresse http://localhost:8069/?db=demo_v7#view_type=kanban&model=res.partner&action=62 die migrierten Partner an. (Der Partner-View muss manuell aufgerufen werden, weil noch keine entsprechenden Module installiert wurden)

Vollständige Migration

Zur vollständigen Migration der Datenbank waren bei mir damit folgende Schritte notwendig:

1. Aufräumen der Quelldatenbank

Nachdem ich in der Vergangenheit doppelt angelegte Produkte, Partner, Adressen usw. gelöscht hatte, zu denen aber bereits ein Angebot, Lagerbestand usw. existierte – Warum die OpenERP Datenbank-Constraints das nicht verhinderten weiß ich nicht – ist meine Quelldatenbank etwas inkonsistent. Da bei einem Einfügen in eine neue Datenbank solche Inkonsistenzen überprüft und nicht zugelassen werden, muss die Quelldatenbank vorher aufgeräumt werden. Ich verwende dazu dieses Skript: (das so auf keinen Fall zusammen mit einer zweiten Datenbank funktionieren wird! Es ist nur zu Anschauungszwecken hier aufgeführt)

Download: openerp-6.1-sql

2. Migration mit anybox.migration.openerp

Danach folgt der aufwändigste Teil – die eigentliche Migration der Datenbank. Die Schwierigkeit ist herauszufinden, welche Tabellen man benötigt und welche Migrationsanweisungen für die eigene Datenbank abgewandelt werden müssen, z. B. weil weitere Addons installiert waren.

Den eigentlichen Befehl zur Ausführung von migrate habe ich in der Datei cmd.sh abgelegt. Der Befehl verweist außerdem auf meine Konfigurationsdatei fenecon.yml

Download: cmd-sh

Download: fenecon-yml

Der Befehl:

./cmd.sh

führt dann die Migration aus. Mit etwas Glück (und nach einigen Stunden Arbeit) beendet sich das Skript dann hoffentlich mit

***
* Successfully imported all csv files!! :-)
***

Wenn es soweit ist, kann in dem Migrationsbefehl in der cmd.sh der Parameter „-w“ ergänzt werden. Dieser sorgt dafür, dass die Daten tatsächlich in die neue Datenbank geschrieben werden.

sandbox/bin/migrate -w -s FENECON -t F_14 -p fenecon.yml \ ...

Leider war es bei mir notwendig, anybox.migration.openerp mit folgendem Patch etwas zu verändern, weil sonst eine falsche SQL-Anweisung generiert wurde:

+++ sandbox/local/lib/python2.7/site-packages/anygrate/exporting.py
@@ -34,6 +34,8 @@
                 continue
             columns = discriminators[table]
             id_column = ['id'] if table not in m2m_tables else []
+            if table in ('res_company_users_rel','product_supplier_taxes_rel','account_account_tax_default_rel','product_taxes_rel'):
+                id_column = []
             cursor.execute('select %s from %s' % (', '.join(columns + id_column), table))
             result[table] = cursor.fetchall()
     return result

(Die beiden mit „+“ markierten Zeilen mussten also an der angegebenen Stelle in der Datei exporting.py eingefügt werden)

Zu beachten ist weiterhin, dass keine Bilder mit dieser Methode übertragen werden. Theoretisch wäre es zwar möglich, für den vorliegenden Fall war der Aufwand aber zu groß.

3. Überprüfung der Zieldatenbank

Nun kann man sich bei OpenERP 7.0 in der neuen Datenbank anmelden. Es ist wichtig, diese ausführlich darauf zu testen, ob alle relevanten Daten übernommen wurden. Falls dem nicht so ist, muss die Konfigurationsdatei überarbeitet und die Migration mit einer neuen Zieldatenbank neu gestartet werden.

4. Konfiguration der Zieldatenbank

Wenn dann alle Daten korrekt im neuen System auftauchen, muss dieses abschließend konfiguriert werden und z. B. die fehlenden Bilder (Unternehmenslogo, Mitarbeiter, Produkte) ergänzt werden.

Fertig.

 

PS: Ich hoffe, dieser Blog-Eintrag ist nützlich für alle, die ein Upgrade von 6.1 auf 7.0 oder später auch auf 8.0 durchführen wollen. Hinter der Migration steckt natürlich noch mehr, als in dieser kurzen Beschreibung gezeigt wurde, aber sie sollte einen guten Start darstellen. Bei Fragen zu bestimmten Teilen der Migration gehe ich gerne noch etwas mehr in die Tiefe.

7 thoughts on “Upgrade von OpenERP 6.1 auf 7.0

  1. Stefan says:

    Danke für deine Arbeit
    aber was mir noch nicht klar ist, du schreibst den meisten Aufwand macht die Unterschiede zu finden ? Wie finde ich die Unterschiede ?

    Gruss Stefan

    • Die mitgelieferte Konfigurationsdatei ist bereits recht vollständig und sollte für eine einfache Installation passen. Unterschiede ergeben sich dann, wenn Addons installiert wurden, die noch nicht abgedeckt sind. Dann bricht das Migrationsprogramm irgendwann ab, weil Quell- und Zieldatenbank nicht zusammenpassen. In diesem Fall muss dann die Konfigurationsdatei manuell angepasst und ergänzt werden.

      Also: Am besten einfach einmal mit der Standarddatei und wenigen Tabellen (Paramter -r) starten. Wenn das geklappt hat, kann man sich Schritt für Schritt steigern.

  2. Herbert says:

    Wow, wie hast du das bloss geschafft?

    Mit diesem Migrationsscript schaffe ich es noch nicht mal die Tabelle „res_partner“ zu migrieren. Das knallt bei mir an allen Enden.

    Selbst mit einer 61er Datenbank mit Demodaten knallt es schon bei der Tabelle sale_order:

    „ValueError: invalid literal for int() with base 10: ‚False'“ ??

    Nach einem Tag gebe ich nun auf, vielleicht komme ich mit OpenUpgrade einen Schritt weiter 😉

    • Hallo Herbert,

      ich habe mir die Mühe gemacht und die Migration von res_partner mit einer Demo-Datenbank durchgespielt und im Beitrag ergänzt: siehe oben „Beispiel: Migration der Partner (res_partner)“.

      Funktioniert es so bei dir?

  3. Thomas Mohr says:

    Bei mir kommen beim Versuch die Tabellen zu migrieren Dinge wie z.B. module base_setup is missing etc. Was genau heißt das und was macht man dagegen ?

  4. Alois Schneider says:

    Danke für deine Anleitung, nach der ich versuche eine 6.1 Datenbank nach 7.0 zu migrieren. Auf einem Debian Wheezy System habe ich sowohl Openerp 6.1 als auch Openerp 7 installiert (die zwei Instanzen laufen unter zwei verschiedenen Benutzern: openerp61 und openerp7.
    1. Unter 6.1 habe ich meine Produktivdatenbank gesichert und als „migrate_v61“ importiert. Zugriff unter 6.1 auf diese Datenbank klappt.
    2. Unter 7 eine neue Datenbank „migrate_v7“ angelegt.
    3. Als Benutzer postgres den OWNER der Datenbank „migrate_v61 auf „openerp7 geändert.
    4. Als Benutzer openerp7 (su – openerp7 -s /bin/bash) das Migrate-Skript wie folgt aufgerufen:
    /sandbox/bin/migrate -s migrate_v61 -t migrate_v7 -p openerp6.1-openerp7.0.yml -r res_partner
    ergibt folgende Fehler:
    openerp7@debian:~$ sandbox/bin/migrate -s migrate_v61 -t migrate_v7 -r res_partner
    Computing the real list of tables to export…
    WARNING:depending.pyc:Dependency LOOP: res_company has a m2o to res_partner which is one of its ancestors (path=(‚res_partner‘,))
    WARNING:depending.pyc:Dependency LOOP: account_account has a m2o to res_company which is one of its ancestors (path=(‚res_partner‘, ‚res_company‘))
    WARNING:depending.pyc:Dependency LOOP: res_currency has a m2o to res_company which is one of its ancestors (path=(‚res_partner‘, ‚res_company‘, ‚account_account‘))
    The real list of tables to export is: res_users, res_partner_title, res_currency, account_account_type, account_account, res_company, res_partner
    Exporting tables as CSV files…
    Traceback (most recent call last):
    File „sandbox/bin/migrate“, line 9, in
    load_entry_point(‚anybox.migration.openerp==0.9‘, ‚console_scripts‘, ‚migrate‘)()
    File „/opt/openerp7/sandbox/local/lib/python2.7/site-packages/anygrate/migrating.py“, line 79, in main
    excluded, target_dir=tempdir, write=args.write)
    File „/opt/openerp7/sandbox/local/lib/python2.7/site-packages/anygrate/migrating.py“, line 107, in migrate
    filepaths = export_to_csv(source_tables, target_dir, source_connection)
    File „/opt/openerp7/sandbox/local/lib/python2.7/site-packages/anygrate/exporting.py“, line 16, in export_to_csv
    cursor.copy_expert(„COPY %s TO STDOUT WITH CSV HEADER NULL ““ % table, f)
    psycopg2.ProgrammingError: FEHLER: keine Berechtigung für Relation res_users

    openerp7@debian:~$ sandbox/bin/migrate -s migrate_v61 -t migrate_v7 -r res_partner -p openerp6.2-openerp7.0.yml
    Computing the real list of tables to export…
    WARNING:depending.pyc:Dependency LOOP: res_company has a m2o to res_partner which is one of its ancestors (path=(‚res_partner‘,))
    WARNING:depending.pyc:Dependency LOOP: account_account has a m2o to res_company which is one of its ancestors (path=(‚res_partner‘, ‚res_company‘))
    WARNING:depending.pyc:Dependency LOOP: res_currency has a m2o to res_company which is one of its ancestors (path=(‚res_partner‘, ‚res_company‘, ‚account_account‘))
    The real list of tables to export is: res_users, res_partner_title, res_currency, account_account_type, account_account, res_company, res_partner
    Exporting tables as CSV files…
    Traceback (most recent call last):
    File „sandbox/bin/migrate“, line 9, in
    load_entry_point(‚anybox.migration.openerp==0.9‘, ‚console_scripts‘, ‚migrate‘)()
    File „/opt/openerp7/sandbox/local/lib/python2.7/site-packages/anygrate/migrating.py“, line 79, in main
    excluded, target_dir=tempdir, write=args.write)
    File „/opt/openerp7/sandbox/local/lib/python2.7/site-packages/anygrate/migrating.py“, line 107, in migrate
    filepaths = export_to_csv(source_tables, target_dir, source_connection)
    File „/opt/openerp7/sandbox/local/lib/python2.7/site-packages/anygrate/exporting.py“, line 16, in export_to_csv
    cursor.copy_expert(„COPY %s TO STDOUT WITH CSV HEADER NULL ““ % table, f)
    psycopg2.ProgrammingError: FEHLER: keine Berechtigung für Relation res_users

    Scheint an den Berechtigungen zu liegen. Kannst du mir sagen, was ich falsch gemacht habe?

    Gruss Alois

    • Hallo,

      es liegt wohl in der Tat an den Berechtigungen. Ich habe mir das mal im Quellcode angeschaut. Der Fehler stammt aus der Funktion export_to_csv in der Datei exporting.py, Zeile 16. Diese wird aufgerufen in der Datei migrating.py, Zeile 107. Das Problem liegt an der source_connection, die vorher in Zeile 89 erstellt wird:
      source_connection = psycopg2.connect("dbname=%s" % source_db)
      Diese Methode stellt augenscheinlich eine Verbindung ohne Benutzername und Passwort zur Datenbank her. Ich konnte auf die Schnelle aber herausfinden, als welcher Benutzer die Verbindung erstellt wird, wenn keiner angegeben ist. Vermutlich der aktuell angemeldete – bei dir also openerp7.

      Die Lösung für dein Problem könnte also sein, das Postgres-Passwort für openerp7 vorübergehend zu entfernen um eine Anmeldung ohne Passwort zu erlauben. Alternativ könntest du die Zeile im Quellcode (und auch die target_connection darunter) gemäß der Doku zum verwendeten Psycopg2-Modul abändern.

      Gruß,
      Stefan

Schreibe einen Kommentar

Deine E-Mail-Adresse wird nicht veröffentlicht. Erforderliche Felder sind mit * markiert