Robust Data Import/Export in Odoo Using connector_flow as a Basis

veröffentlicht in Consulting,Software Engineering am 9. Dez. 2014 Tags: , , ,

This is the English translation of Robuster Daten-Import/Export in Odoo auf Basis von connector_flow.

An ERP system is normally not a self-contained IT system, but instead incorporated into a multitude of processes that require interaction with other systems, for example the import of the product master data of an Excel catalogue, the transfer of orders to a logistics service provider via FTP or enabling a bank to import account movements. Although other programs can indeed access Odoo (for example via RPC or REST-API), a solution that can be directly implemented in Odoo is normally the easiest solution for all tasks that extend past pure data conversion.
This is where our Odoo module connector_flow comes into play.
It provides a structure in Odoo in which recurring tasks can be implemented and which handles standard tasks such as FTP download/upload and CSV processing. Individual procedures can be implemented separately and combined to form one process via defined interfaces. connector_flow is based on the excellent tried and tested Connector Framework, which facilitates a checkable, traceable and parallelisable workflow.
The following screenshots provide an example of a greatly simplified import of product master data from a CSV file. We also used the same technical basis to create an import that reads product data for approximately half a million products from a CSV file for two of our clients.

Our product import can be divided up into two stages: we first import the CSV file and then manage the products in Odoo. The associated technical process (Task Flow) is therefore comprised of these two tasks.

A two-stage Task Flow for the product import from CSV


A two-stage Task Flow for the product import from CSV


When carrying out the first stage, namely handling the CSV file, we are able to use a task that is already provided with the connector_flow module. This csv_import splits a CSV file into its separate lines and stores each of these individual lines as a Python data structure (a list or dictionary), also known as a chunk.
In the second stage, the creation of the product in Odoo, we can then add or update a product from such a chunk (a line of the CSV). For the technical implementation, we need to both program this task and compile the Task Flow.

We first deal with the task required for the creation of the product. The fact that this task receives a chunk from the previous task (reading the CSV file) as an entry means that we inherit the class abstract_chunk_read_task from the connector_flow module. In the chunk-data parameter we receive a dictionary so that we can suitably configure the reading of the CSV. The keys of the dictionary correspond to the column names of the CSV file (example files), meaning that creating the product in Odoo on the basis of the chunk is just a matter of a few clicks:

1
2
3
4
5
6
7
8
9
10
11
12
class product_catalog_import(abstract_chunk_read_task):
    def read_chunk(self, config=None, chunk_data=None, async=True):
        product_data = {
            'name': chunk_data.get('Name'),
            'list_price': float(chunk_data.get('Preis VK')),
            'standard_price': float(chunk_data.get('Preis EK')),
        }
        product_image_url = chunk_data.get('Image URL')
        if product_image_url:
            url_obj = urllib2.urlopen(product_image_url)
            product_data['image'] = b64encode(url_obj.read())
        self.session.create('product.product', product_data)

In order to use our product_catalog_import class in the Task Flow, we still need to register it in the Odoo model impexp.task. This is carried out using the normal Odoo inheritance process. On the one hand, an identifier needs to be added to _get_available_tasks and on the other hand, a method …_class needs to be created and given a name that corresponds to the selected identifier.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
class product_catalog_import_task(orm.Model):
    _inherit = 'impexp.task'
 
    def _get_available_tasks(self, cr, uid, context=None):
        return super(product_catalog_import_task, self) \
            ._get_available_tasks(cr, uid, context=context) \
            + [('product_catalog_import', 'Produkt Catalog Import')]
 
    _columns = {
        'task': fields.selection(_get_available_tasks, string='Task',
                                 required=True),
    }
 
    def product_catalog_import_class(self):
        return product_catalog_import

This enables us to set up our Task Flow. Although this can also be carried out manually via the Odoo interface, a configuration via XML is recommended in the interest of clarity. When completing this configuration, we create records for the Task Flow, the two tasks and a transition between the two tasks. The first task in the Task Flow needs to have the flow_start property. In our case, this is the csv_import, which comes with the connector_flow module. We then configure it using the includes_header property in the config value so that it uses the first line of the CSV file as a title, which it in turn uses to create dictionaries from the remaining lines. The second task is the product_catalog_import in accordance with the indicator that we chose above in the impexp.task model. We then finish off by adding a transition between the tasks.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
        <!-- Task flow -->
 
        <record model="impexp.task.flow" id="task_flow_product_catalog_import">
            <field name="name">Product Catalog Import</field>
        </record>
 
        <!-- Task and task transition -->
 
        <record model="impexp.task" id="task_product_catalog_import_1">
            <field name="name">Product catalog CSV to chunks</field>
            <field name="task">csv_import</field>
            <field name="flow_id" ref="task_flow_product_catalog_import"/>
            <field name="flow_start" eval="True"/>
            <field name="config"><![CDATA[{'includes_header': True}]]></field>
        </record>
 
        <record model="impexp.task" id="task_product_catalog_import_2">
            <field name="name">Product catalog chunks to products</field>
            <field name="task">product_catalog_import</field>
            <field name="flow_id" ref="task_flow_product_catalog_import"/>
        </record>
 
        <record model="impexp.task.transition" id="task_product_catalog_import_1">
            <field name="task_from_id" ref="task_product_catalog_import_1"/>
            <field name="task_to_id" ref="task_product_catalog_import_2"/>
        </record>

To carry out the product import, we use the wizard available under Connector > Import/Export > Run Task. We select “Product Catalog Import” as the Task Flow and our CSV file as the file.

Carrying out the product import

Carrying out the product import


Example files for the product import

Example files for the product import

Shortly (how shortly depends on the configuration of the connector framework) after Run Task has been clicked, three products have been added to Odoo; the fourth product, “orange”, from the input file is missing.

The example products, apple, kiwi and lime, have been imported.

The example products, apple, kiwi and lime, have been imported.

Under Connector > Queue > Procedures we can see that the execution of the second task has failed because the image URL is incorrect. In order to still be able to import the product, we can click on the Related button to edit the raw data and, for example, correct or delete the URL. After this data correction, the procedure can be started again so that the orange is also available in Odoo.

A line in the CSV file contains incorrect data.

A line in the CSV file contains incorrect data.

The source code of the connector_flow module is available on Github, where you will also find the example code presented above under the name of connector_flow_example_product together with an example CSV (contrib/fruit_catalog.csv).

If you are also interested in Odoo/Open ERP or require our expertise for your ERP project, please feel free to contact us. We will be happy to help you further!
You can contact us by calling +49 4105 5615699 or by sending us an e-mail at {This email is obscured. Your must have javascript enabled to see it}.

Über Thomas Rehn

Der promovierte Mathematiker Thomas Rehn ist begeisterter Problemlöser in C++, Java, Perl, PHP und Python. Seine jahrelange Erfahrung als System-Administrator sorgt für den reibungslosen Betrieb unserer Web-, Datenbank und Applicationserver. Nebenbei ist er Autor von und Contributor zu verschiedener mathematischer Open-Source-Software.

Ehrung der initOS GmbH & Co. KG durch die Ottostadt Magdeburg

veröffentlicht in Communication,Consulting,Software Engineering am 21. Nov. 2014 Tags: , ,

Die initOS GmbH & Co. KG wurde am 4. November 2014 von der Ottostadt Magdeburg als eines von 35 Unternehmen ausgezeichnet, welches in besonderem Maße innovative Produkte und Dienstleistungen entwickelt und anbietet und damit für den Forschungs- und Wirtschaftsstandort Magdeburg wirbt.
Die Ehrung fand im Alten Rathaus statt und wurde vom Oberbürgermeister Dr. Lutz Trümper, sowie dem Beigeordneten für Wirtschaft, Tourismus und regionale Zusammenarbeit, Rainer Nitsche und dem Präsidenten der Kammern vollzogen.
Unser Geschäftsführer Frederik Kramer nahm die Auszeichnung persönlich entgegen.

IMG_0350

Die Festrede hielt der Rektor der Otto-von-Guericke-Universität, Prof. Dr. Jens Strackeljan.
Nach einem musikalisch umrahmten Programm wurden die Unternehmen vom Oberbürgermeister persönlich empfangen. Der Abend endete mit einem gemeinsamen Buffet, bei dem sich Möglichkeiten ergaben miteinander ins Gespräch zu kommen.

Glasstehle

Unser Geschäftsführer Frederik Kramer äußerte sich nach der Preisverleihung: “Es ist für uns eine große Ehre und zugleich Wertschätzung unserer Arbeit von der Stadt Magdeburg und Herrn Oberbürgermeister Dr. Lutz Trümper ausgezeichnet worden zu sein. Wir fühlen uns dem Standort und der Stadt verpflichtet und werden weiter intensiv daran arbeiten Magdeburg und Sachsen-Anhalt als IT-Standort bekannt und geschätzt zu machen.” 

Über Kathleen Friedrichs

Frau Friedrichs studierte Geschichte und Biologie für das Lehramt an Sekundarschulen. Seit 15 Jahren verwendet sie ausschließlich Linux und ist eine begeisterte Anhängerin von Open Source. Sie verfügt über Sprachkenntnisse in Polnisch, Russisch und Englisch.

Robuster Daten-Import/Export in Odoo auf Basis von connector_flow

veröffentlicht in Consulting,Software Engineering am 11. Nov. 2014 Tags: , , , ,

An English translation of the following text also is available.

Ein ERP-System ist in der Regel kein abgeschlossenes IT-System, sondern ist in zahlreiche Prozesse eingebunden, die Interaktion mit anderen Systemen voraussetzen. Sei es, dass Produktstammdaten eines Excel-Katalogs importiert werden sollen, dass Bestellungen an einen Logistikdienstleister per FTP übergeben werden sollen, oder, dass Kontobewegungen von einer Bank eingelesen werden sollen. Zwar können auch andere Programme auf Odoo zugreifen (zum Beispiel via RPC oder REST-API), aber eine direkt in Odoo implementierte Lösung ist für alles, was über reine Datenwandlung hinausgeht, meistens die Einfachste.
Hier setzt unser Odoo-Modul connector_flow an.
Es bietet in Odoo eine Struktur, in der wiederkehrende Aufgaben implementiert werden können und die Standardaufgaben wie FTP-Download/-Upload und CSV-Verarbeitung übernimmt. Einzelne Arbeitsschritte können getrennt implementiert und über definierte Schnittstellen zu einem Prozess zusammengefasst werden. connector_flow basiert auf dem erprobten, großartigen Connector-Framework, was einen kontrollierbaren, nachvollziehbaren und parallelisierbaren Ablauf ermöglicht.
Als Beispiel betrachten wir im Folgenden einen stark vereinfachten Import von Produktstammdaten aus einer CSV-Datei. Auf derselben technischen Grundlage haben wir für zwei unserer Kunden einen Import erstellt, der Produktdaten für etwa eine halbe Million Produkte aus einer CSV-Datei einliest.

Unseren Produkt-Import können wir in zwei Schritte aufteilen: erst lesen wir die CSV-Datei ein und pflegen dann die Produkte in Odoo. Der dazugehörige technische Prozess (Task Flow) besteht daher aus diesen zwei Tasks.

Task Flow Example


Zweistufiger Task Flow für den Produkt-Import aus CSV


Für den ersten Schritt, der Handhabung der CSV-Datei, können wir auf einen Task zurückgreifen, der schon mit dem connector_flow-Modul kommt. Dieser csv_import zerlegt eine CSV-Datei in ihre Zeilen und speichert diese jeweils einzeln als Python-Datenstruktur ab (Liste oder Dictionary), auch Chunk genannt.
Im zweiten Schritt, dem Erstellen des Produkts in Odoo, können wir dann aus einer solchem Chunk (Zeile des CSVs) ein Produkt angelegen oder aktualisieren. Zur technischen Umsetzung müssen wir zum einen diesen Task programmieren und zum anderen den Task Flow zusammensetzen.

Als Erstes kümmern wir uns um den Task zum Anlegen des Produkts. Da dieser Task von seinem Vorgänger (Lesen der CSV-Datei) ein Chunk als Eingabe bekommt, erben wir von der Klasse abstract_chunk_read_task aus dem connector_flow-Modul. Im Parameter chunk_data bekommen wir ein Dictionary, da wir das Lesen des CSVs entsprechend konfigurieren werden. Die Keys des Dictionaries entsprechen den Spaltennamen der CSV-Datei (Beispieldaten). Damit ist es nur noch eine Fingerübung das Produkt in Odoo basierend auf dem Chunk anzulegen:

1
2
3
4
5
6
7
8
9
10
11
12
class product_catalog_import(abstract_chunk_read_task):
    def read_chunk(self, config=None, chunk_data=None, async=True):
        product_data = {
            'name': chunk_data.get('Name'),
            'list_price': float(chunk_data.get('Preis VK')),
            'standard_price': float(chunk_data.get('Preis EK')),
        }
        product_image_url = chunk_data.get('Image URL')
        if product_image_url:
            url_obj = urllib2.urlopen(product_image_url)
            product_data['image'] = b64encode(url_obj.read())
        self.session.create('product.product', product_data)

Um unsere Klasse product_catalog_import im Task Flow verwenden zu können, müssen wir sie noch im Odoo-Model impexp.task registrieren. Dies geschieht über gewöhnliche Odoo-Vererbung. Zum einen muss ein Bezeichner zu _get_available_tasks hinzugefügt werden, zum anderen muss eine Methode …_class erstellt werden, deren Namen dem gewählten Bezeichner entspricht.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
class product_catalog_import_task(orm.Model):
    _inherit = 'impexp.task'
 
    def _get_available_tasks(self, cr, uid, context=None):
        return super(product_catalog_import_task, self) \
            ._get_available_tasks(cr, uid, context=context) \
            + [('product_catalog_import', 'Produkt Catalog Import')]
 
    _columns = {
        'task': fields.selection(_get_available_tasks, string='Task',
                                 required=True),
    }
 
    def product_catalog_import_class(self):
        return product_catalog_import

Damit können wir unseren Task Flow aufbauen. Dies ist zwar auch manuell über die Odoo-Oberfläche möglich; es empfiehlt sich aber der Übersichtlichkeit halber eine Konfiguration per XML. Dort legen wir Records jeweils für den Task Flow, die zwei Tasks und einen Übergang zwischen den beiden Tasks an. Der erste Task im Task Flow muss die Eigenschaft flow_start besitzen. Dies ist in unserem Fall der csv_import, der mit dem connector_flow-Modul kommt. Wir konfigurieren ihn noch mittels der includes_header-Eigenschaft im config-Wert so, dass er die erste Zeile der CSV-Datei als Überschrift verwendet und diese verwendet, um aus den übrigen Zeilen Dictionaries zu erstellen. Der zweite Task ist product_catalog_import gemäß dem Bezeichner, den wir oben im impexp.task-Model gewählt haben. Zum Schluss fügen wir noch einen Übergang zwischen den Tasks hinzu.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
        <!-- Task flow -->
 
        <record model="impexp.task.flow" id="task_flow_product_catalog_import">
            <field name="name">Product Catalog Import</field>
        </record>
 
        <!-- Task and task transition -->
 
        <record model="impexp.task" id="task_product_catalog_import_1">
            <field name="name">Product catalog CSV to chunks</field>
            <field name="task">csv_import</field>
            <field name="flow_id" ref="task_flow_product_catalog_import"/>
            <field name="flow_start" eval="True"/>
            <field name="config"><![CDATA[{'includes_header': True}]]></field>
        </record>
 
        <record model="impexp.task" id="task_product_catalog_import_2">
            <field name="name">Product catalog chunks to products</field>
            <field name="task">product_catalog_import</field>
            <field name="flow_id" ref="task_flow_product_catalog_import"/>
        </record>
 
        <record model="impexp.task.transition" id="task_product_catalog_import_1">
            <field name="task_from_id" ref="task_product_catalog_import_1"/>
            <field name="task_to_id" ref="task_product_catalog_import_2"/>
        </record>

Um den Produkt-Import auszuführen, benutzen wir den Wizard unter Connector > Import/Export > Run Task. Als Task Flow wählen wir “Product Catalog Import” aus, als File unsere CSV-Datei.

Produkt-Import ausführen

Produkt-Import ausführen


Beispiel-Daten für Produktimport

Beispiel-Daten für Produktimport


Kurze Zeit (wie kurz hängt von der Konfiguration des Connectors-Frameworks ab) nach Klick auf Run Task sind dann drei Produkte in Odoo angelegt; das vierte Produkt “Orange” aus der Eingabedatei fehlt.
Die Beispiel-Produkte Apfel, Kiwi und Limette sind importiert

Die Beispiel-Produkte Apfel, Kiwi und Limette sind importiert


Unter Connector > Warteschlange > Arbeitsschritte sehen wir, dass eine Ausführung des zweiten Tasks fehlgeschlagen ist, weil die Bild-URL fehlerhaft ist. Um das Produkt dennoch zu importieren, können wir mit einem Klick auf den Related-Button die Rohdaten bearbeiten und beispielsweise die URL korrigieren oder entfernen. Nach dieser Datenkorrektur kann der Arbeitsschritt erneut gestartet werden, so dass schließlich auch die Orange in Odoo verfügbar ist.
Eine Zeile in der CSV-Datei enthält fehlerhafte Daten

Eine Zeile in der CSV-Datei enthält fehlerhafte Daten

Der Quellcode von connector_flow-Modul ist auf Github verfügbar. Dort findet sich auch der hier vorgestellte Beispiel-Code als connector_flow_example_product inklusive Beispiel-CSV (contrib/fruit_catalog.csv).

Falls Sie auch Interesse an Odoo/OpenERP haben oder unsere Expertise für Ihr ERP-Projekt benötigen, dann nehmen Sie mit uns Kontakt auf. Wir helfen Ihnen gerne weiter!

Sie erreichen uns unter der Telefonnummer +49 (0)4105 5615699 oder per Mail an {This email is obscured. Your must have javascript enabled to see it}.

Über Thomas Rehn

Der promovierte Mathematiker Thomas Rehn ist begeisterter Problemlöser in C++, Java, Perl, PHP und Python. Seine jahrelange Erfahrung als System-Administrator sorgt für den reibungslosen Betrieb unserer Web-, Datenbank und Applicationserver. Nebenbei ist er Autor von und Contributor zu verschiedener mathematischer Open-Source-Software.

Nächste Seite »