Skip to content

Instantly share code, notes, and snippets.

@bwrsandman
Last active December 21, 2015 12:48
Show Gist options
  • Save bwrsandman/6307855 to your computer and use it in GitHub Desktop.
Save bwrsandman/6307855 to your computer and use it in GitHub Desktop.
Seed to setup openerp virtualenv for developement

Based on "Creating your first addon for OpenERP" by Naysan Saran (naysan.saran@savoirfairelinux.com)

Table of Contents
# Installation ## Pre-requisites
# apt-get python-pip build-dep python-imaging

...

Arch Linux

# pacman -S bzr python2-pip

...

## Setup 0. Create and cd into the project folder, all commands can be run from in there. 1. Get buildout.cfg for project 2. Set ```option.db_user``` to local username 3. Set ```option.db_name``` to the name of the postgresql table to be created 4. Create the table. (```$ createdb $TABLENAME```) 5. Create the addons folder to avoid an error while building out. (```$ mkdir addons```) ## Buildout

Get bootstrap.py from zope, run the bootstrap script, followed by the buildout that is generated

$ wget http://svn.zope.org/repos/main/zc.buildout/trunk/bootstrap/bootstrap.py
$ python2 bootstrap.py
$ ./bin/buildout

pypy method

Install buildout.boostrap for pypy (requires shell refresh), run bootstrap, followed by buildout

# pip-2.7 install buildout.bootstrap
$ buildout bootstrap
$ buildout
## Result A base install should have only the "Administrator" user and "Base" module installed.

The "addons" folder should be empty, the "bin" folder should have 6 executables.

openerp-project-folder
├── addons
│   └── # empty
├── bin
│   ├── buildout
│   ├── oe
│   ├── paster
│   ├── python_openerp
│   ├── start_openerp
│   └── test_openerp
└── ...
## Running

$ ./bin/start_openerp The web front end should be available at http://localhost:8069

Technical features

Login with username "admin" and password "admin".

Under "Users"/"Users", select "Administrator". Select "Edit" and under "Access Rights", check "Technical Features". Log out and log in again.

# Creating a new module (`simple_note`) *Based on https://doc.openerp.com/trunk/server/03_module_dev_01/*

See also: https://github.com/joaoalf/openerp_bootstrap

For this example, we will create a module called "simple_note", its prefix will be sn

Create a folder in the addons directory called "simple_note"

## Python import file `__init__.py` This file should import all the other python files or submodules ```python import simple_note ``` ## Manifest file `__openerp__.py` This file contains a Python dict literal which declares metadata such as *XML files that will be parsed* and *module dependencies* of this module. ```python { 'name': 'Very Simple Note Addon', 'version': '1.0', 'description': """\ This module is a very simple "notes" addon for OpenERP.""", "category": "Generic Modules/Others", 'author': 'Sandy Carter @ SavoirFaireLinux', 'website': 'http://www.savoirfairelinux.com', 'depends': ['base'], 'data': ['simple_note_view.xml'], 'demo': ['simple_note_demo.xml'], 'test': [], 'installable': True, }

<a name="object"/>
## Object file `simple_note.py`
This file, in part will describe the structure of the database.
We create the "note" data structure with "sn" as a prefix.
It contains the "title"  and "content" fields.

We want access to osv, fields and _
```python
from openerp.osv import fields, osv
from openerp.tools.translate import _


class sn_note(osv.Model):
    """
    A very simple note
    """
    _name = 'sn.note'
    _description = 'A very simple note'
    _columns = {
        'title': fields.char(_('Title'), size=255, require=True),
        'content': fields.text(_('Content'))
    }
## View XML file `simple_note_view.xml` Configures the view of the module within OpenERP ```xml
    <!-- Main view of every note -->
    <record model="ir.ui.view" id="simple_note_tree_view">
        <field name="name">sn.note.tree</field>
        <field name="model">sn.note</field>
        <field name="type">tree</field>
        <field name="arch" type="xml">
            <tree string="SimpleNotesTree">
                <field name="title"/>
                <field name="content"/>
            </tree>
        </field>
    </record>

    <!-- New form note view -->
    <record model="ir.ui.view" id="simple_note_form_view">
        <field name="name">sn.note.form</field>
        <field name="model">sn.note</field>
        <field name="type">form</field>
        <field name="arch" type="xml">
            <form string="SimpleNotesForm">
                <field name="title"/>
                <field name="content"/>
            </form>
        </field>
    </record>

    <!-- Action -->
    <record model="ir.actions.act_window" id="action_simple_note_tree">
        <field name="name">Your notes</field>
        <field name="view_id" ref="simple_note_tree_view"/>
        <field name="res_model">sn.note</field>
        <field name="view_mode">tree,form</field>
    </record>

    <menuitem name="Simple Notes Module" id="simple_note_menu" icon="terp-project" />
    <menuitem name="My Simple Notes" id="simple_note_menu_mainform" parent="simple_note_menu" />
    <menuitem name="All notes" id="simple_note_tree_list" parent="simple_note_menu_mainform" action="action_simple_note_tree"/>

</data>

<a name="demo"/>
## Demo XML file `simple_note_demo.xml`
Fills in the table with example data.
The data we are filling will look like this:

| title               | content                       |
| ------------------- | ----------------------------- |
| Hello               | This is my first simple note. |
| It is raining today | Where is my umbrella?         |
| Where is Bryan?     | Bryan is in the kitchen.      |


Hello This is my first simple note. It is raining today Where is my umbrella? Where is Bryan? Bryan is in the kitchen. ```
## `static/src/img/icon.png` OpenERP will search `static/src/img/icon.png` for an icon.

As Admin from inside OpenERP, go to "Settings"/"Update Modules List".

To find it, go to "Installed Modules" and take off the filter "Installed", then search for "Very Simple Notes Addon".

## Translating the module As Admin from inside OpenERP, go to "Settings"/"Translations"/"Import / Export"/"Export Translation". Select "New Language", "PO File" format and the "Very Simple Notes Addon" Module.

Save the template as "i18n/simple_note.pol", in the simple_note addon folder. (Note the ".pol" extension is not ".po").

Using poedit, translate the words and save the translations under their regional formats.

e.g fr.po should look like this:

# Translation of OpenERP Server.
# This file contains the translation of the following modules:
#   * simple_note
#
msgid ""
msgstr ""
"Project-Id-Version: OpenERP Server 7.0\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2013-08-22 18:11+0000\n"
"PO-Revision-Date: 2013-08-22 14:20-0500\n"
"Last-Translator: Sandy Carter <sandy.carter@savoirfairelinux.com>\n"
"Language-Team: \n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
"Plural-Forms: \n"
"X-Generator: Poedit 1.5.4\n"

#. module: simple_note
#: model:ir.ui.menu,name:simple_note.simple_note_menu
msgid "Simple Notes Module"
msgstr "Module simple de notes"

#. module: simple_note
#: model:ir.actions.act_window,name:simple_note.action_simple_note_tree
msgid "Your notes"
msgstr "Vos notes"

#. module: simple_note
#: field:sn.note,title:0
msgid "Title"
msgstr "Titre"

#. module: simple_note
#: view:sn.note:0
msgid "SimpleNotesForm"
msgstr ""

#. module: simple_note
#: view:sn.note:0
msgid "SimpleNotesTree"
msgstr ""

#. module: simple_note
#: field:sn.note,content:0
msgid "Content"
msgstr "Contenu"

#. module: simple_note
#: model:ir.ui.menu,name:simple_note.simple_note_menu_mainform
msgid "My Simple Notes"
msgstr "Mes notes simples"

#. module: simple_note
#: model:ir.model,name:simple_note.model_sn_note
msgid "A very simple note"
msgstr "Une très simple note"

#. module: simple_note
#: model:ir.ui.menu,name:simple_note.simple_note_tree_list
msgid "All notes"
msgstr "Toutes les notes"

To test, as Admin from inside OpenERP, go to "Settings"/"Translations"/"Load a Translation". Select the language, you will be prompted to change the language in your preferences.

# Reports *In this example, we will add a simple report that will print out a list of notes as a pdf*

Create a report folder which will include __init__.py, note.py and note.rml

report/__init__.py

This file must import each report python script

import note

report/note.py

This file contains a class that will be used to reference the rml file.

from report import report_sxw


class note(report_sxw.rml_parse):
    def __init__(self, cr, uid, name, context):
        super(note, self).__init__(cr, uid, name, context)

report_sxw.report_sxw('report.sn.note', 'sn.note',
      'addons/simple_note/report/note.rml', parser=note, header=True)

##report/note.rml This file defines the structure of the report.

<?xml version="1.0"?>
<!DOCTYPE document SYSTEM "rml.dtd">
<document filename="Notes Report.pdf" >
  <template title="Notes Report" author="Sandy Carter (sandy.carter@savoirfairelinux.com)" allowSplitting="20">
    <!--this section contains elements of the document -->
    <!--which are FIXED into position.                 -->
    <pageTemplate id="main">
      <frame id="first" x1="100.0" y1="400.0" width="150" height="200"/>
    </pageTemplate>
  </template>

  <stylesheet>
    <!--this section contains the STYLE information for             -->
    <!--the document, but there isn't any yet. The tags still       -->
    <!--have to be present, however, or the document won't compile. -->
  </stylesheet>

  <story>
    <!--this section contains the FLOWABLE elements of the -->
    <!--document. These elements will fill up the frames   -->
    <!--defined in the <template> section above.           -->
      <para>
        Welcome to RML, [[ user.name ]]!
      </para>
      <para>
        This is the "story". This is the part of the RML document where your
        text is placed.
      </para>
      <para>
        It should be enclosed in "para" and "/para" tags to turn it into
        paragraphs
      </para>

      <!-- Section to print the tasks in a table -->
      <section>
        <blockTable>
          <tr>
            <td>Title</td>
            <td>Content</td>
          </tr>
          <!-- Loop here for each record -->
          <tr> [[repeatIn(objects,'o')]]
            <td>[[o.title]]</td>
            <td>[[o.content]]</td>
          </tr>
        </blockTable>
      </section>

    </story>
</document>

simple_note_report.xml

This file declares the existing reports

<?xml version="1.0"?>
<openerp>
    <data>
        <report
            id="report_simple_note"
            string="Simple Notes Report"
            model="sn.note"
            name="sn.note"
            rml="simple_note/report/note.rml"
            auto="False"
            multi="True"
            />
    </data>
</openerp>

Import the report folder

The main module has to import the report python files, to do this, open init.py in the module root and add the following line.

import report

Include simple_note_report.xml

Add simple_note_report.xml to the data list in __openerp__.py

{
    ...
    'data': [..., 'simple_note_report.xml'],
    ...
}
# Testing Creating a new postgresql database for the purpose of testing is recommended. ``` $ createdb testing ``` ## YAML *https://doc.openerp.com/v6.0/contribute/15_guidelineds/coding_guidelines_testing*

All yaml tests should be found in a directory called test.

This test file will create one new record and verify (assert) that the record was actually saved properly.

-
    First I create a simple note
-
    !record {model: sn.note, id: note4}:
        title: "This is a test"
        content: "Just testing if the module works."

-
    I check if the simple note, the title and the content were created as expected
-
    !python {model: sn.note}: |
        note_id = ref('simple_note.note4')
        note = self.browse(cr, uid, note_id)
        assert (note.title == "This is a test"), "The title was not saved !"
        assert (note.content == "Just testing if the module works."), "The content was not saved !"

Include test/simple_note_test.yml

Add test/simple_note_test.yml to the test list in __openerp__.py

{
    ...
    'test': [..., 'test/simple_note_test.yml']
    ...
}

Running the test

$ bin/test_openerp --init=simple_note \
                   --database=testing \
                   --test-file=$(pwd)/addons/simple_note/test/simple_note_test.yml

Make sure the --test-file is in absolute path.

Adding the --test-commit flag will make OpenERP commit its tests instead of rolling-back. This can be useful for debugging if something goes wrong.

## unittest2 *https://doc.openerp.com/trunk/server/05_test_framework*

Unit tests tent to be found in a directory name tests. Note: do not include test in the base __init__.py.

Import the tests. Add the test to the checks link.

import test_simple_note

checks = [
    test_portal,
    ]

unittest2 class files

Prefix the python files with test_.

Create the file tests/test_simple_note.py.

from openerp.tests import common


class Test_Simple_Note(common.TransactionCase):

    def setUp(self):
        """*****setUp*****"""
        super(Test_Simple_Note, self).setUp()
        cr, uid = self.cr, self.uid
        self.simple_note = self.registry('sn.note')
        self.note_id = self.simple_note.create(cr, uid, {
            'title': 'This is a test',
            'content': 'Just testing if the module works.',
            }, context=None)

    def test_note(self):
        """
        I check if the simple note, the title and the content were created as
        expected.
        """
        cr, uid = self.cr, self.uid
        note = self.simple_note.browse(cr, uid, self.note_id, context=None)
        self.assertEquals(note.title, 'This is a test')
        self.assertEquals(note.content, 'Just testing if the module works.')

Run tests

$ bin/oe run-tests -d testing -m simple_note
## qUnit .... ## PhantomJS *https://github.com/ariya/phantomjs/wiki*

Using the subprocess module python can call phantomjs from inside a unittest and check the output.

For now, this will only test navigating to the page and checking the page title and the server must be running prior to runing ./bin/oe run-tests ...

Ubuntu

# apt-get install phantomjs

Arch Linux

# pacman -S phantomjs

Javascript

Create a phantomjs script named pagetitle.js and put it in the tests directory`.

var page = require('webpage').create(),
    system = require('system'),
    url;

if (system.args.length === 1) {
    console.log('Usage: pagetitle.js <some URL>');
    phantom.exit();
}

url = system.args[1];

page.open(url, function (status) {
    var title = page.evaluate(function() {
        return document.title;
    });
    console.log(title);
    phantom.exit();
});

This script can be tested for any website with phantomjs.

$ phantomjs pagetitle.js http://www.openerp.com
OpenERP - Beautiful Business Applications

Unit test

Create a unittest2 class file dedicated to phantomjs tests. Name it test_simple_note_phantomjs.

from openerp.tests import common
import os
import subprocess

testsdir = os.getcwd() + "/addons/simple_note/tests"

def execjs(script):
    """
    Runs a subprocess of phantomjs and checks output minus line break.
    requires name of javascript minus .js
    """
    return  subprocess.check_output(["phantomjs",
                                     "/".join([testsdir, script]) + ".js",
                                     "http://localhost:8069"])[:-1]

class Test_Simple_Note_PhantomJS(common.TransactionCase):

    def setUp(self):
        """*****setUp*****"""
        super(Test_Simple_Note_PhantomJS, self).setUp()
        cr, uid = self.cr, self.uid
        self.simple_note = self.registry('sn.note')
        self.note_id = self.simple_note.create(cr, uid, {
            'title': 'This is a test',
            'content': 'Just testing if the module works.',
            }, context=None)

    def test_page_title(self):
        """
        I check if the openerp page has the correct title.
        """
        title = execjs("pagetitle")
        self.assertEquals(title, u"OpenERP")

Import and add the new unittest to the tests/__init__.py script.

## Ghost.py *http://jeanphix.me/Ghost.py/*

Ghost.py offers similar functionality as Phantomjs with the added benefit of being a python module. This allows the javascript code to be declared within the unittest file, rather than in a seperate .js file. Subprocess is not needed to run these tests and it results in cleaner code.

For now, this will only test navigating to the page and checking the page title and the server must be running prior to runing ./bin/oe run-tests ...

Ghost requires PyQt and PySide

Ubuntu

# apt-get install python-qt4 python-pyside

Arch Linux

# ...

Installation

# pip install Ghost.py

Unit test

Create a unit test file and name it test_simple_note_ghost.py

from openerp.tests import common
from ghost import Ghost


class Test_Simple_Note_Ghost(common.TransactionCase):

    def setUp(self):
        """*****setUp*****"""
        super(Test_Simple_Note_Ghost, self).setUp()
        cr, uid = self.cr, self.uid
        self.simple_note = self.registry('sn.note')
        self.note_id = self.simple_note.create(cr, uid, {
            'title': 'This is a test',
            'content': 'Just testing if the module works.',
            }, context=None)
        # Get Ghost to load localhost
        self.ghost = Ghost()
        self.page, self.resources = self.ghost.open("http://localhost:8069")

    def test_page_title(self):
        """
        I check if the openerp page has the correct title.
        """
        title = self.ghost.evaluate("document.title")[0]
        self.assertEquals(title, u"OpenERP")

Import and add the new unittest to the tests/__init__.py script.

# Troubleshooting ## `IOError: decoder jpeg not available` This error might come up when starting the server and when running tests, make sure you have the proper python-imaging dependencies installed.

After installing the dependencies, remove the Pillow egg in the eggs folder ($ rm -r eggs/Pillow*.egg)

# apt-get build-dep python-imaging

Arch Linux

...

UnicodeEncodeError on server startup

On ArchLinux, staring up the server may result in an error such as: UnicodeEncodeError: 'ascii' codec can't encode character u'\xe9' in position 3: ordinal not in range(128) This is actually the psycopg2 python module having trouble with the environment's encoding.

Make sure the PostgreSQL database you created has encoding UTF8, otherwise, drop it and recreate it specifying UTF8.

[buildout]
parts = openerp ropeproject paste
versions = versions
find-links = http://download.gna.org/pychart/
develop = ../openerp_bootstrap
extensions = gp.vcsdevelop
vcs-extend-develop = bzr+https://code.launchpad.net/~openerp/openerp-command/7.0#egg=openerp-command
[openerp]
recipe = anybox.recipe.openerp:server
eggs =
num2words
# replace '6.1' with 'trunk' to get a 7.0 current nightly:
version = bzr lp:openobject-server/7.0 openerp70 last:1
addons = bzr lp:openerp-web/7.0 openerp-web70 last:1 subdir=addons
bzr lp:openobject-addons/7.0 openobjects-addons70 last:1
bzr lp:oerpscenario openscenario last:1
bzr lp:openerp-canada openerp-canada last:1
bzr lp:~savoirfairelinux-openerp/contract-management/isp_contracts contract-management last:1
bzr lp:partner-contact-management partner-contact-management last:1
local addons
options.db_host = False
options.db_name = db_name
options.db_user = db_user
options.db_password = False
options.without_demo = False
with_devtools = True
openerp_command_name = oe
[behave]
recipe = zc.recipe.egg
interpreter = python
extra-paths = ${buildout:directory}/parts/server
eggs = behave
ERPpeek
mock
unittest2
MarkupSafe
Pillow
PyXML
babel
feedparser
gdata
lxml
mako
psycopg2
pychart
pydot
pyparsing
python-dateutil
python-ldap
python-openid
pytz
pywebdav
pyyaml
reportlab
simplejson
vatnumber
vobject
werkzeug
xlwt
docutils
[ropeproject]
recipe = collective.recipe.ropeproject
eggs =
[paste]
recipe = zc.recipe.egg
eggs =
Paste
PasteDeploy
PasteScript
openerp_bootstrap
[versions]
MarkupSafe = 0.15
Pillow = 1.7.7
PyXML = 0.8.4
babel = 0.9.6
feedparser = 5.1.1
gdata = 2.0.16
lxml = 2.3.3
mako = 0.6.2
psycopg2 = 2.4.4
pychart = 1.39
pydot = 1.0.28
pyparsing = 1.5.6
python-dateutil = 1.5
python-ldap = 2.4.9
python-openid = 2.2.5
pytz = 2012b
pywebdav = 0.9.4.1
pyyaml = 3.10
reportlab = 2.5
simplejson = 2.4.0
vatnumber = 1.0
vobject = 0.8.1c
werkzeug = 0.8.3
xlwt = 0.7.3
zc.buildout = 1.5.2
zc.recipe.egg = 1.3.2
zsi = 2.0-rc3
collective.recipe.ropeproject = 1.0b3
rope = 0.9.4
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment