Self ERP   Installation und Modulentwicklung mit OpenERP

Inhalt

4 Grundlagen 4.1 Arbeitsumgebung 4.2 Model View Controller 4.3 Ein leeres Modul Skelet 4.3.1 Die __init__.py Datei 4.3.2 Die __terp__.py Datei 4.3.3 Die View Datei 4.3.4 Die Controller Klassen Datei 4.3.5 Testen des Moduls 4.4 Eine einfache Tabelle 4.4.1 Spalten definieren 4.4.2 Menü Einträge definieren 4.4.3 Testen des Moduls 4.5 Ansichten definieren 4.5.1 Definieren einer Listenansicht 4.5.2 Definieren einer Formularansicht 4.6 Festlegen von default-Werten

4 Grundlagen

4.1 Arbeitsumgebung

Es wird vorausgesetzt, das der Leser sich mit dem OpenERP Client vertraut gemacht hat. Und die Paketverwaltungsprozedur kennt.

Der Einfachheit halber werden Entwicklungsschritte direkt auf dem Server vorgeführt. Gearbeitet wird mit dem Midnight Commander -wenn nötig- als root :

cd /usr/lib/python2.5/site-packages/openerp-server/addons
sudo mc
    

Sie werden beim ausprobieren der Beispiele mehrfach dazu aufgefordert, den Quellcode des vorherigen Beispiel-Moduls umzukopieren, bzw. die Bezeichner umzubennen. Dies ist zum durcharbeiten der Beispiele nicht notwendig, sondern geschieht nur, um die Analogie zu den Beispiel-Modulen aus der Download- Rubrik zu waren. Es ist ebenfalls möglich das example_empty-Modul bis hin zu den späteren Kapiteln auszubauen.

Die Änderungen am Quellcode,die von der Namensänderung her rühren, sind blau markiert. Änderungen die neu hinzukommen sind, sind rot markiert.

4.2 Model View Controller

Der Aufbau des OpenERP Systems läst sich als Model View Controller Prinzip verstehen. Wobei die Darstellung der Daten vom Zugriff und der Speicherung getrennt ist.

Die Darstellung erfolgt im OpenERP-Client und wird mit Hilfe von serverseitig abgelegten XML-Dateien festgelegt. Als Modelle dienen die Tabelle der PostgreSQL Datenbank. Die Schnittstelle zwischen dem Modell und der Ansicht ist ein Python-Controller-Objekt.

4.3 Ein leeres Modul Skelet

Erstellen sie ein Verzeichnis im addons-Verzeichnis mit dem Namen example_empty. Legen Sie die folgende Dateien an :

Erzeugen Sie Dateien mit dem Befehl touch :

touch __init__.py
touch example_empty.py
touch __terp__.py
touch example_empty_view.xml
    

4.3.1 Die __init__.py Datei

Die __init__.py Datei wird, wie bei jedem Python modul, bei Programmstart ausgeführt. Hier werden alle Python Dateien angegeben, die geladen werden sollen.

Die erzeugte python Datei example_empty.py wird wie folgt eingebunden :

import example_empty
    

4.3.2 Die __terp__.py Datei

{
    "name" : "Example Empty",
    "version" : "1.1",
    "depends" : ["base"],
    "author" : "SelfERP",
    "description": """Das ist ein leeres Modul zu Entwicklungszwecken""",
    'init_xml': [],
    'update_xml': ["example_empty_view.xml"],
    'demo_xml': [],
    'installable': True,
    'active': False
}
    

Die meisten der Angaben werden Ihnen aus der Modulverwaltung bekannt vorkommen und sind frei definierbar. Depends muß zumindest das base Packet enthalten. Möchten Sie weitere Abhängigkeiten hinzufügen, können sie diese mit einem Komma trennen :

"depends" : ["base","stock"],
    

In update_xml, muß ihre XML-Datei mit den Definitionen der Ansichten angegeben werden.

4.3.3 Die View Datei

Das Modul hat bislang keine Masken definiert. Bei den Angaben handelt es sich lediglich um die Wurzelknoten. Das encoding utf-8 ist Pflicht.

<?xml version="1.0" encoding="utf-8"?>
<openerp>
    <data>
    </data>
</openerp>
    

4.3.4 Die Controller-Klassen Datei

In der Controller-Klassen Datei (example_empty.py) werden Klassen abgelegt, die als Controller zwischen den Anichten und den Datenmodellen (PostgreSQL-Tabellen) dienen. Gleichzeitig wird mit Hilfe der Controller-Klassen die Tabellenstruktur innerhalb der Datenbank beschrieben.

Controller-Klassen erben grundsätzlich ihre Eigenschaften von der Klasse osv. Auch wenn eine Tabelle mehrere Datensätze beinhalten kann, so existiert zu der Tabelle nur eine einzige Controller-Instanz. Innerhalb der View-Datei wird über den Namen der Controller-Klasse auf das Modell verwiesen.

Normalerweise werden die vom Controller definierten Datenbanktabellen bei der Modulinstallation automatisch erzeugt. In diesem Fall wird jedoch ein Controller ohne Datenbanktabelle und Spaltendefinitionen erzeugt.

from osv import osv, fields

class example_empty(osv.osv):
       _name = 'example.empty'
       _auto = False
       _columns = {}
       _defaults = {}
example_empty()
    

4.3.5 Testen des Moduls

Nach dem Sie das Modul Skelet angelegt haben, ist es Zeit eine Testdatenbank zu erstellen Hierzu wählen Sie ein minimales Profil und eine erweiterte Ansicht. Im folgendem wird davon ausgegangen, das die Testdatenbank den Namen test1 erhält.

Öffnen Sie die Modul Liste, sie sollten den Eintrag example_empty.py finden. Alles was Sie jetzt mit dem Modul tun können, ist, es zu installieren und anschließend wieder zu deinstallieren.

4.3 Eine einfache Tabelle

Kopieren Sie das gesamte example_empty Verzeichnis und nennen es example_simple_table. Anschließend empfiehlt es sich, alle Namensbezeichner dem neuen Modulnamen anzupassen. Also :

4.4.1 Spalten definieren

Um Spalten zu definieren, öffnen sie example_simple_table.py und ändern die Datei wie folgt:

from osv import osv, fields

class example_simple_table(osv.osv):
       _name = 'example.simple.table'
       _columns = {'name'            : fields.char('Name', size=64, required=True),
                   'forename'        : fields.char('Vorname', size=32, required=True),
                   'gender'          : fields.selection((('male','Maennlich'),('female','Weiblich')),'Geschlecht'),
                   'birthday'        : fields.date('Geburtstag'),
                   'bestfriend'      : fields.boolean('Bester Freund'),
                   'friendshipyears' : fields.integer('Wieviele Jahre befreundet'),
                   'size'            : fields.float('Groesse (meter)',digits=(12,2) ),
                   'meeting'         : fields.datetime('Naechstes Treffen'),
                   'description'     : fields.text('Kommentar'),

		  }
       _defaults = {}
example_simple_table()
    

4.4.2 Menü Einträge definieren

Um einen Zugriff auf die Tabelle zu ermöglichen, muß ein neuer Menüeintrag erstellt werden, der auf das example_simple_table-Datenmodell verweist. Dies geschieht in example_simple_table_view.xml :

<?xml version="1.0" encoding="utf-8"?>
<openerp>
    <data>
        <menuitem id="example_simple_table_menu" name="Meine Freunde" sequence="0"/>
        <record id="example_simple_table_menu_view_action" model="ir.actions.act_window">
            <field name="name">Zeige alle meine Freunde</field>
            <field name="res_model">example.simple.table</field>
            <field name="view_type">form</field>
        </record>
        <menuitem action="example_simple_table_menu_view_action" 
                     id="example_simple_table_menu_view" 
                     parent="example_simple_table_menu"/>
    </data>
</openerp>
    

Es sind zwei neue Knotentypen hinzugekommen :

Menuitem bewirkt, das ein neuer Eintrag in das Menü hinzugefügt wird. Das erste Menuitem besitzt drei Attribute :

Das zweite menuitem besitzt noch zwei weitere Attibute :

Record ist ein abstrakter Datenknoten, dessen Funktion sich durch das model-Attribut bestimmt.

Field
Ein Record Knoten enthält field-Unterknoten, um das Verhalten zu parametrieren, das über model festgelegt wurde. Ein Feld hat immer ein name-Attribut und einen Wert, der über den Knoteninhalt definiert wird. Der ir.actions.act_window-record-Knoten hat folgende Felder :

4.4.3 Testen des Moduls

Jetzt können Sie erneut eine Testdatenbank anlegen und das Modul installieren. Wenn der neue Menüpunkt nicht gleich nach der Installation erscheint, so liegt das daran, das die Ansicht des OpenERP-Clients zu nächst aktualisiert werden muß. Dies können sie über den Menüpunkt : Form -> Reload / undo

Sie werden sich vielleicht wundern, das sie bereits eine Freundes-Liste mit einem doppel Klick auf "Zeige alle Freunde" öffnen können. Dies liegt nicht etwa daran, das sie keinen weiteren Einfluß auf die Maskengestaltung haben, sondern daran, das sie keine explizite Maskendefinition angegeben haben. Das selbe gilt für die Listenansicht, die nur deshalb funktioniert, weil die example_simple_table Tabelle eine Spalte mit dem Namen "name" besitzt. Ein Spalte mit diesem Namen wird von OpenERP grundsätzlich zur automatischen Generierung einer Ansicht verwendet.

4.5 Ansichten definieren

4.5.1 Definieren einer Listenansicht

Um der Listenansicht weitere Spalten hinzuzufügen, muss lediglich die XML-View Datei angepasst werden. Definieren sie einen record mit dem model ir.ui.view wie folgt :

<?xml version="1.0" encoding="utf-8"?>
<openerp>
    <data>
        <menuitem id="example_simple_table_menu" name="Meine Freunde" sequence="0"/>

        <record model="ir.ui.view" id="example_simple_table_friends_list">
            <field name="name">example.simple.friends.list</field>
            <field name="model">example.simple.table</field>
            <field name="type">tree</field>
            <field name="arch" type="xml">
                <tree string="Alle meine Freunde">
                    <field name="name" select="1"/>
                    <field name="forename" select="1"/>
                    <field name="birthday"/>
                    <field name="bestfriend"/>
                    <field name="friendshipyears"/>
               </tree>
            </field>
        </record>

        <record id="example_simple_table_menu_view_action" model="ir.actions.act_window">
            <field name="name">Zeige alle meine Freunde</field>
            <field name="res_model">example.simple.table</field>
            <field name="view_type">form</field>
        </record>
        <menuitem action="example_simple_table_menu_view_action" 
                     id="example_simple_table_menu_view" 
                     parent="example_simple_table_menu"/>
    </data>
</openerp>		
    

Es ist ein View-Record hinzugekommen, der eine Ansicht mit mehreren Spalten des example.simple.table-Models beschreibt. Von Interesse sind hier die Felder :

4.5.2 Definieren einer Formularansicht

Der OpenERP-Client verwendet ein tabellenbasiertes Formularlayout. Ein Formular unterteilt sich zunächst in mehrere Spalten. Jedes Eingabeelement, das dargestellt werden soll, beansprucht für sich jewals eine Spalte. Wobei Eingabefelder, sofern nicht anders angegeben, auch immer eine Beschriftung besitzen. Diese beansprucht eine weitere Spalte.

Ist ein Formular auf 4 Spalten eingestellt, so können insgesamt zwei Spalten an Eingabeelementen mit ihrer jewaligen Beschriftung nebeneinander dargestellt werden.

Es ist möglich, Eingabeelemente zu gruppieren, um die Formularansicht besser unterteilen zu können. Auf diese Weise kann beispielsweise eine mehrzeiliges Textfeld neben mehreren einzeiligen Textfeldern positioniert werden. Eine Gruppe beansprucht den Bereich einer Spalte.

Um die Zusammenhänge zu veranschaulichen sind in folgendem Bild die einzelnen Gestalungselemente hervorgehoben :

Die XML-View Definition hierfür sieht wie folgt aus :

<?xml version="1.0" encoding="utf-8"?>
<openerp>
    <data>
        <menuitem id="example_simple_table_menu" name="Meine Freunde" sequence="0"/>
        <record id="example_simple_table_menu_view_action" model="ir.actions.act_window">
            <field name="name">Zeige alle meine Freunde</field>
            <field name="res_model">example.simple.table</field>
            <field name="view_type">form</field>
        </record>
        <menuitem action="example_simple_table_menu_view_action" 
                     id="example_simple_table_menu_view" 
                     parent="example_simple_table_menu"/>

        <record model="ir.ui.view" id="example_simple_table_friends_list">
            <field name="name">example.simple.friends.list</field>
            <field name="model">example.simple.table</field>
            <field name="type">tree</field>
            <field name="arch" type="xml">
                <tree string="Alle meine Freunde">
                    <field name="name" select="1"/>
                    <field name="forename" select="1"/>
                    <field name="birthday"/>
                    <field name="bestfriend"/>
                    <field name="friendshipyears"/>
               </tree>
            </field>
        </record>

        <record model="ir.ui.view" id="example_simple_table_friends_form">
            <field name="name">example.simple.friends.form</field>
            <field name="model">example.simple.table</field>
            <field name="type">form</field>
            <field name="arch" type="xml">
                <form string="Freund bearbeiten" col="2">
                    <group col="2">
                        <separator string="Allgemein" colspan="2"/>
                        <field name="name"/>
                        <field name="forename"/>
                        <field name="bestfriend"/>
                        <field name="friendshipyears"/>
                        <separator string="Spezifische Daten" colspan="2"/>
                        <field name="gender"/>
                        <field name="birthday"/>
                        <field name="size"/>
                        <separator string="Verabredung" colspan="2"/>
                        <field name="meeting"/>
                    </group>
                    <group col="1">
                        <separator string="Beschreibung"/>
                        <field name="description" nolabel="1"/>
                    </group>
                </form>
            </field>
        </record>

    </data>
</openerp>
    

Wie sie im Quellcode ersehen können geschieht der Aufbau eine Formulars von links nach rechts und von oben nach unten. Der form Knoten besitzt zwei Spalten, dies wird mit dem Attribut col fesgelegt. Im Bild entspricht das dem roten Rahmen.

Innerhalb des Formulars befinden zwei Gruppen, für jede Spalte eine. Die erste Gruppe - entsprechend dem linken grünen Rahmen - besitzt zwei Spalten, um Eingabefelder samt ihres Titels darstellen zu können (gelber Rahmen). Die Zweite Gruppe - entsprechend dem rechten grünen Rahmen - besitzt nur eine Spalte, da der Titel des mehrzeiligen Eingabefeldes im Separator dargestellt werden soll und nicht links vom Eingabefeld.

Innerhalb der ersten Gruppe befinden sich verschiedene separatoren (blauer Rahmen) die nur der Übersicht dienen. Ein separator nimmt lediglich den Platz einer Spalte in Anspruch. Deshalb muß mit colspan angegeben werden, das der separator den Platz zweier Spalten beanspruchen soll. Mit Hilfe des string Attributs wird der Text festgelegt, den der Separator anzeigen soll.

Innerhalb der zweiten Gruppe ist ein colspan Attribut nicht mehr von nöten da die Gruppe nur eine Spalte beinhaltet. Allerdings muß dem mehrzeiligen Eingabefeld mitgeteilt werden, das es seinen Titel nicht darstellen soll, dies geschieht mit dem Attribut nolabel.

4.6 Festlegen von default-Werten

Jede Controller-Klasse verfügt über eine Membervariable Namens _default. Die Variable verweist auf ein Liste mit Funktionen, die zur Errechnung von Defaultwerten dienen. Die Listenschlüssel entsprechen den Feldnamen. Sobald ein Default-Wert für ein Feld angefordert wird, wird über den Schlüssel die passende Funktion, ausgewählt und ausgeführt.

Um das Nachname Feld mit dem Wert "Ein neuer Freund [eine fortlaufende Nummer]" vorzubesetzen, muß die default-Liste in friends-Controller-Klasse angepasst werden :

counter=0
def _get_default_name(self,cr,uid,context):
  self.counter+=1
  return 'Ein neuer Freund '+str(self.counter)

_defaults = {'name':_get_default_name}