Showing posts with label Odoo. Show all posts
Many times we would have heard or asked our selves this question: how is a free or open source software is interesting to business? The simple answer for it - the cost of possession of such software applications for the customer is in times cheaper that is very interesting to business even at accompanying inconveniences of completion. And this profitability can over come many barriers like organisational, technological or legislative.


It has been a proven that if the company never introduced system of document circulation or an Enterprise Resource Planning(ERP) application, in its corporate culture there are no skills of work and management through informational system. These two processes - development of skills of management and development of informational system as the tool of management - are interdependent. It is often possible to observe that the company management, having absolutely despaired to master management, spends huge money for ERP-system, and the result becomes only comprehension of problems by the personnel and a management, and their correct statement.
EPR-systems has been enabling the companies for great volumes of sales and a gradual reduction in marketing or operational costs. It is necessary for them to analyse the expenses against the margins. For these purposes heavy and expensive systems developed by SAP and Oracle are being used in larger enterprises.


When we are considering enterprises of average and small business, which have opportunities to sell more, but it is already complex to them to cope with their own management. Introduction of system ERP/CRM for them has a big risk of a non-return of investments, there is no direct visible way of a recoupment of system, and therefore the cost of introduction should be in some times less.
It is a known fact that most big organizations select and implement Commercial ERP solutions rather than go with Open Source ERP software. Why so? What are the challenges then that Open Source ERP Vendors face when competing against a Commercial ERP package? Let us investigate and see how Open Source ERP Solutions stack up against the Commercial ERP counterparts.


Time
The time to market, or the time it takes to implement a Commercial ERP package is generally longer (given that you may be subject to software availability timeline restrictions from the software vendor and implementation process methodologies from a large systems integrator). Typically, organizational processes need to conform to the software process itself, and effecting that change can take a long time. Open Source ERP implementations are generally used by smaller companies who are just setting up processes initially, are typically not averse to adjusting to a new process. For this reason, typical Open Source ERP implementations see smaller implementation cycles (mostly due to the simplicity of the project itself)
 

Cost
Open Source ERP solutions are typically less expensive (most being completely free) to license than Commercial software. The only upfront cost associated with using them is the support and maintenance contract fees or some small amount as License fees when using a customized version that is being promoted by a specific company.
 

Flexibility
Open Source, means you can have the code, and can modify it yourself! How can a system provide more flexibility than this? Compare this to a Commercial ERP solution: To get any fix, patch upgrade or new functionality on your system and it will take time!
However in case of an Open Source ERP you might need to keep your own team of developers to not only add the features, but to also maintain it yourself. So a complete IT Team may be required.  This implies that even though the cost of initial software acquisition is low, the cost of keeping the software running can potentially be higher.
 

Training & Ramp-Up
Commercial ERPs typically need specific training for the developers/implementers (examples SAP, Oracle ERP) etc. But for Open Source, you have the entire source-code itself! Open Source ERP providers believe (as do most geeks) that there is no better documentation than the source code itself. Source code may be the best piece of documentation for a Developer, but for an End User it usually means nothing. Now days all the Open Source ERPs come out with excellent documentations including both functional and technical aspects.
 

Business
Now days IT service providers are also becoming more comfortable with subscription revenue as a business model, something common among open-source companies, while larger enterprises are also getting on board. Financial backing from venture capitalists is helping to boost growth.
According to a survey done earlier this year, about 80 percent of companies have installed or will install some form of open-source software by the end of this year. The cost and time for developing a fully matured enterprise class busines intelligence applications like ERP is too large that it may be affordable for larger IT companies. Open Source business model allows an early entry into the market with lowest TCOs which can open up a service based revenue channel at the starting stages of a business life cycle.
Professional services firms have had practices for proprietary software--such as SAP or Oracle software--for many years. But in open source ERPs, most service providers are still relatively young and the market is unsettled, which means that companies that partner today could become competitors in the future. Some of the popular names among Open Source ERP applications currently are OpenERP, Adempiere, OpenBravo, OpenTaps, Tryton, ERP5 etc.

If we consider the arguments above, Open Source ERPs surely has an edge. Keeping in mind about all the advantages there are few points to be investigated before you make a choise of going with Open Source ERPs. The track record of that particular application in providing good documentations when they update the versions, check for the availability and relaibility of support partners apart from the community, explore your ERP's compatibility requirements for your business purpose and make a right choice form the available applications which can realise those requirements, availability of long term support and capabale of taking the committments.

The best brand ambassador for Open Source today are probably Linux (big presence in Enterprise), Android (big presence in mobile, tablets), Firefox web browser, JBoss Application Server to name a few. Open Source ERP solutions are still far from establishing a brand name for themselves (like Linux, JBoss). They probably will need to build communities like those for Linux and JBoss. They might also require angel investors like those that JBoss, RedHat, and Ubuntu have and at least a decade or more of serious concerted development in order to compete with Commercial ERPs!
Adding a sequence for records in OpenERP is very simple. For making a field a sequence type, we need to create new sequence or use existing sequence. For creating a sequence, we need to create two type of objects, one is “ir.sequence.type” and other “ir.sequence”. The example to create these records are given below..


<record forcecreate="1" id="seq_type_id" model="ir.sequence.type">
 
<field name="name">Name</field>

  <field name="code">code</field>
</record>

<record forcecreate="1" id="seq_id" model="ir.sequence">
  <field name="name">Name</field>
  <field name="code">code</field>
  <field name="padding" eval="pading"/>
  <field name="prefix">prefix</field>
  <field name="suffix">suffix</field>
</record>

Eg :

        <record id="sequence_reconcile" model="ir.sequence.type">
            <field name="name">Account Reconcile</field>
            <field name="code">account.reconcile</field>
        </record>

        <record id="sequence_reconcile_seq" model="ir.sequence">
            <field name="name">Account reconcile sequence</field>
            <field name="code">account.reconcile</field>
            <field name="prefix">A</field>
            <field eval="1" name="number_next"/>
            <field eval="1" name="number_increment"/>
        </record>
 
When thsi xml file is executed, the new sequence with name “Name” will be created on OpenERP. You can see this sequence from “under 

Settings>Configuration(Technical)>Sequence&Identifiers>Sequences


All the fields created here is self explanatory. If you have any doubt, you can keep the mouse over the label of each field on openerp and it will display corresponding help text.

Now we need to add this sequence to a record. For that we have to call the get function on ir.sequence class with the correct code. This function call can be done on _default so that the sequence is generated by default when new record is created. This can be done using following code,

_defaults = {

'field_name': lambda self,cr,uid,context={}: self.pool.get('ir.sequence').get(cr, uid, 'code'),

}

Ex:


   _defaults = {
        'name': lambda self,cr,uid,
context={}: self.pool.get('ir.sequence').get(cr, uid, 'account.reconcile', context=context) or '/',
    }

 


Note :

You can create new sequence without any python code. But to link with a field we need to write code. Or on some form view we can make a many2one field to sequence and take the sequence which is selected there. You can see example for this in Journal(Accounting/Configuration/Financial Accounting/Journals/Journals).  There is a field named "Entry Sequence".  This sequence is used for numbering of journal entries created for this journal.
 


OpenERP is an open source comprehensive suite of business applications including Sales, CRM, Project management, Warehouse management, Manufacturing, Accounting, Human Resources etc. OpenERP has separate client and server components. XML-RPC interfaces are available.
Other ERPs include SAP, Oracle, Microsoft etc.

The following points will give an idea of the advantages of OpenERP over other ERPs

Simplicity & Flexibility
The simplicity and flexibility that is offered by OpenERP when considered with other ERP gives users easiness in usage of the system.

Architecture
The architecture of ERPs like SAP, Oracle etc are very complicated to understand and implement where as the architecture of OpenERP is simple and easy to understand.

Cost
The cost to implement an ERP solution like SAP,Oracle etc is very high which small scale or medium scale industries won’t be able to bear where as the cost of implementation of an OpenERP solution is very less compared to the proprietary ERP solutions which will allow small scale and medium scale industries to have an ERP solution for their business.

Training
The training that the users will need in typical proprietary ERP solutions involves lots of time and cost. Employees/users need to be a bit Tech savvy’s in understanding it whereas OpenERP training involves very less time and cost and also the employees/users need to have only a minimum basic knowledge of computer.

Time for Implementation
Time for implementing an in typical proprietary ERP solution needs lots of time whereas the time needed for implementing an ERP solution is very less as more than 500+ modules are readily available in OpenERP.

Addition Of Modules
In OpenERP admin can activate any modules as an when require with no additional cost whereas in other proprietary ERP solution to have an additional module they need to consult the service provider to add new module which also involves additional costs.

Can you describe Odoo Technically?

Odoo is web based application which is built using its own framework 'Open Object'. Odoo is using Python, XML and CSV as the main technologies.


Do you know the main differences between Odoo 7.0 and the later versions?

Odoo (OpenERP) 7.0 was built using the Old APIs which is still supported side-by-side the New APIs in Odoo 8.0 and Odoo 9.0.
The candidate should be familiar with the New APIs as the Old APIs will not be supported anymore in Odoo 10.0 . He also should be able to give some examples (@api.one, @api.multi, @api.depends, @api.onchange, @api.model, .....)


Can you share some details about the Odoo ORM with examples?

Odoo is depending on ORM (Object-Relational Mapping) in almost every single method. So, the candidate should be able to mention a few examples and when to use it and the expected value returned.
The most common Odoo ORM methods should be known. Also, he should know the difference between browse() and create() as both are returing a recordsets.

What are the types of reports that supported in Odoo by default?

Odoo is supporting PDF, HTML and dynamic reports. The candidate should be familiar with QWeb so that he can create and edit reports.

What are the difference between the modules from the technical point of view?

Nothing!!
When it come to coding all modules are the same as there is no real difference between the code written for HR Modules and that written for Accounting Modules regardless the complexity of the accounting modules.

Can you describe the levels of security in Odoo?

Odoo using users and groups to define the permissions for each unit (users & groups). Also there is the security rules which can be defined as the exception that should be happened in some cases. For example, as an accountant you should be able to delete invoices but you will not be able to delete confirmed invoices.
The candidate should be familiar with these levels of security

Can you install Odoo on a remote server using command line?

Your system administrator should be able to install, remove, start, stop and update Odoo on the testing servers and the production servers. But sometimes it may be useful for the developer to access the server by himself to make sure that everything is well built.




What is the version of Python that Odoo uses?

Odoo still using Python 2.7.x. But I can predict that  they will move to Python 3.x for Odoo 11 or may be Odoo 12 as the main distributions of Linux started to move to Python 3.x which means that the support for python 2.7 will be decreased.
Also, new arrivals will consider the new versions of python.

What is your favorite IDE?

It may be a weird but it should help you to know if the candidate can use and understand the advanced features of the IDEs. Many of developers may prefer to use Text Editors!!.
The most recommended IDEs for a healthy development environment is PyCharm CE and Eclipse with PyDev. If you already have your team, then it would be preferred that the candidate is familiar with your tools or at least he/she can start with it.
It will be great if the candidate can use Debugging and Git in his/her favorite IDE.
For a quick development, you can advise him/her with Odoo PyCharm Templates (here).

Can you use Git commands?

It is a priceless skill to know how to use and get benefit of using Git through GitHub or BitBucket. It will help your team to keep the detailed history of the development along the timeline of the project.

Do you understand the developer mode?

Well, He/She should be able to use the developer mode and get more details that he/she needs for the development.
There are many examples for using the developer mode which may need another separate article but this answer may be a good start to understand the developer mode and how to use it efficiently.

Are you familiar with Odoo Training resources on the Internet?

Another weird question that should help you to understand if the candidate can learn and can to be updated as Odoo is growing and changing so fast.
You may find my training useful, Odoo (OpenERP) v8 Technical, there are more than 1,000 developers there who will be happy to find you among them.

Are you familiar with Linux?

Linux Distributions have been used for decades as servers. So, the candidate should have Linux on his local machine. It can even be much better if he used the same distribution as the one on the server which confirm that his code will work smoothly on the servers.



Other Things to Take Care of :

Testing  :  unit testing / integrated testing for modules, the web controllers and web extensions?
web client structure (JS/QWeb/Controllers).
Reports and Wizards
Git

Record Rules For Objects

Record rules determine who can access the objects, depending on the rules set for the particular object. A record rule has some tests to be performed on objects.

You can manage four access modes on objects independently, depending on the test:

        Read access : can read the data in the object,

        Create access : can create a new record in the object,

        Write access : can modify the contents of records in the object,

        Delete access : can delete records from the object.

To configure a rule on an object, use the menu Administration ‣ Security ‣ Record Rules. The fields in the ir.rule object describe:

        Object : Object on which to have the rule

        Name : Name of the rule

        Global : If global is checked, then that rule would be applied for all the groups; and if it is unchecked, then that rule would be applied only for the groups selected for it

        Domain : A list of all the tests for the object. It is specified through a Python expression as a list of tuples.

                If there are multiple tests on same object, then all of them are joined using AND operator, and depending on the result the rule would be satisfied

Access Modes : Read, Write, Create, Delete as described earlier

If only one access mode is checked, then only that mode would be applied

If all of them are checked, then all the access modes would be applied

But at least one access mode has to be checked, all of them cannot be unchecked. If all of them are unchecked, it would raise an exception.

For example : We can have a rule defined on res.partner object, which tests if the user is the dedicated salesman of the partner [('user_id', '=', user.id)]. We check only the create and write access modes and keep other access modes unchecked.

This would mean that a user in the group for which the rule is applied can only create/write records where he himself serves as the dedicated salesman, and cannot create/write records where he is not the dedicated salesman. As other access modes are unchecked, the user cannot read/delete the records of partners where he is not the dedicated salesman .


Each tuple in the search domain needs to have 3 elements, in the form:    

                     ('field_name', 'operator', value),

field_name must be a valid name of field of the object model, possibly following many-to-one relationships using dot-notation, e.g 'street' or 'partner_id.country' are valid values.

operator must be a string with a valid comparison operator from this list: =, !=, >, >=, <, <=, like, ilike, in, not in, child_of, parent_left, parent_right The semantics of most of these operators are obvious. The child_of operator will look for records who are children or grand-children of a given record, according to the semantics of this model (i.e following the relationship field named by self._parent_name, by default parent_id.

Value must be a valid value to compare with the values of field_name, depending on its type .

Note: uid = the id of the curent user

Domain criteria can be combined using 3 logical operators than can be added between tuples: '&' (logical AND, default), '|' (logical OR), '!' (logical NOT). These are prefix operators and the arity of the '&' and '|' operator is 2, while the arity of the '!' is just 1. Be very careful about this when you combine them the first time.

Priority Rule for Logical Operators :
NAO (NOT AND OR)

Here is an example of searching for Partners named ABC from Belgium and Germany whose language is not english ::

[('name','=','ABC'),'!',('language.code','=','en_US'),'|',('country_id.code','=','be'),('country_id.code','=','de')]

The '&' is omitted as it is the default, and of course we could have used '!=' for the language, but what this domain really represents is::

(name is 'ABC' AND (language is NOT english) AND (country is Belgium OR Germany))

Computed fields and default values

So far fields have been stored directly in and retrieved directly from the database. Fields can also be computed. In that case, the field's value is not retrieved from the database but computed on-the-fly by calling a method of the model.

To create a computed field, create a field and set its attribute compute to the name of a method. The computation method should simply set the value of the field to compute on every record in self.

Note :self is a collection

The object self is a recordset, i.e., an ordered collection of records. It supports the standard Python operations on collections, like len(self) and iter(self), plus extra set operations like recs1 + recs2.

Iterating over self gives the records one by one, where each record is itself a collection of size 1. You can access/assign fields on single records by using the dot notation, like record.name.

import random

from openerp import models, fields

class ComputedModel(models.Model):

    _name = 'test.computed'
    name = fields.Char(compute='_compute_name')
    @api.multi
    def _compute_name(self):
        for record in self:
            record.name = str(random.randint(1, 1e6))


Dependencies


The value of a computed field usually depends on the values of other fields on the computed record. The ORM expects the developer to specify those dependencies on the compute method with the decorator depends(). The given dependencies are used by the ORM to trigger the recomputation of the field whenever some of its dependencies have been modified:

from openerp import models, fields, api

class ComputedModel(models.Model):
    _name = 'test.computed'

    name = fields.Char(compute='_compute_name')
    value = fields.Integer()

    @api.depends('value')
    def _compute_name(self):
        for record in self:
            self.name = "Record with value %s"


Default values


Any field can be given a default value. In the field definition, add the option default=X where X is either a Python literal value (boolean, integer, float, string), or a function taking a recordset and returning a value:

name = fields.Char(default="Unknown")
user_id = fields.Many2one('res.users', default=lambda self: self.env.user)

Note

The object self.env gives access to request parameters and other useful things:

    self.env.cr or self._cr is the database cursor object; it is used for querying the database

    self.env.uid or self._uid is the current user's database id

    self.env.user is the current user's record

    self.env.context or self._context is the context dictionary

    self.env.ref(xml_id) returns the record corresponding to an XML id

    self.env[model_name] returns an instance of the given model









Onchange
The "onchange" mechanism provides a way for the client interface to update a form whenever the user has filled in a value in a field, without saving anything to the database.

For instance, suppose a model has three fields amount, unit_price and price, and you want to update the price on the form when any of the other fields is modified. To achieve this, define a method where self represents the record in the form view, and decorate it with onchange() to specify on which field it has to be triggered. Any change you make on self will be reflected on the form.

<!-- content of form view -->
<field name="amount"/>
<field name="unit_price"/>
<field name="price" readonly="1"/>

# onchange handler
@api.onchange('amount', 'unit_price')
def _onchange_price(self):
    # set auto-changing field
    self.price = self.amount * self.unit_price
    # Can optionally return a warning and domains
    return {
        'warning': {
            'title': "Something bad happened",
            'message': "It was very bad indeed",
        }
    }

Model constraints
Odoo provides two ways to set up automatically verified invariants: Python constraints and SQL constraints.

A Python constraint is defined as a method decorated with constrains(), and invoked on a recordset. The decorator specifies which fields are involved in the constraint, so that the constraint is automatically evaluated when one of them is modified. The method is expected to raise an exception if its invariant is not satisfied:

from openerp.exceptions import ValidationError

@api.constrains('age')
def _check_something(self):
    for record in self:
        if record.age > 20:
            raise ValidationError("Your record is too old: %s" % record.age)
    # all records passed the test, don't return anything

Add a constraint that checks that the instructor is not present in the attendees of his/her own session.

openacademy/models.py

# -*- coding: utf-8 -*-

from openerp import models, fields, api, exceptions

class Course(models.Model):
    _name = 'openacademy.course'

                    'message': "Increase seats or remove excess attendees",
                },
            }

    @api.constrains('instructor_id', 'attendee_ids')
    def _check_instructor_not_in_attendees(self):
        for r in self:
            if r.instructor_id and r.instructor_id in r.attendee_ids:
                raise exceptions.ValidationError("A session's instructor can't be an attendee")


SQL constraints are defined through the model attribute _sql_constraints. The latter is assigned to a list of triples of strings (name, sql_definition, message), where name is a valid SQL constraint name, sql_definition is a table_constraint expression, and message is the error message.


    CHECK that the course description and the course title are different

    Make the Course's name UNIQUE

openacademy/models.py

    session_ids = fields.One2many(
        'openacademy.session', 'course_id', string="Sessions")

    _sql_constraints = [
        ('name_description_check',
         'CHECK(name != description)',
         "The title of the course should not be the description"),

        ('name_unique',
         'UNIQUE(name)',
         "The course title must be unique"),
    ]


class Session(models.Model):
    _name = 'openacademy.session'

Add a duplicate option

Since we added a constraint for the Course name uniqueness, it is not possible to use the "duplicate" function anymore (Form ‣ Duplicate).

Re-implement your own "copy" method which allows to duplicate the Course object, changing the original name into "Copy of [original name]".

openacademy/models.py

    session_ids = fields.One2many(
        'openacademy.session', 'course_id', string="Sessions")

    @api.multi
    def copy(self, default=None):
        default = dict(default or {})

        copied_count = self.search_count(
            [('name', '=like', u"Copy of {}%".format(self.name))])
        if not copied_count:
            new_name = u"Copy of {}".format(self.name)
        else:
            new_name = u"Copy of {} ({})".format(self.name, copied_count)

        default['name'] = new_name
        return super(Course, self).copy(default)

    _sql_constraints = [
        ('name_description_check',
         'CHECK(name != description)',



Domains
In Odoo, Domains are values that encode conditions on records. A domain is a list of criteria used to select a subset of a model's records. Each criteria is a triple with a field name, an operator and a value.

For instance, when used on the Product model the following domain selects all services with a unit price over 1000:

[('product_type', '=', 'service'), ('unit_price', '>', 1000)]

By default criteria are combined with an implicit AND. The logical operators & (AND), | (OR) and ! (NOT) can be used to explicitly combine criteria. They are used in prefix position (the operator is inserted before its arguments rather than between). For instance to select products "which are services OR have a unit price which is NOT between 1000 and 2000":

['|',
    ('product_type', '=', 'service'),
    '!', '&',
        ('unit_price', '>=', 1000),
        ('unit_price', '<', 2000)]

A domain parameter can be added to relational fields to limit valid records for the relation when trying to select records in the client interface.

Note :Priority of Logical Operator :
NAO(NOT AND OR)
Inheritance

Model inheritance

Odoo provides two inheritance mechanisms to extend an existing model in a modular way.The first inheritance(Traditional Inheritance) mechanism allows a module to modify the behavior of a model defined in another module:

    add fields to a model,

    override the definition of fields on a model,

    add constraints to a model,

    add methods to a model,

    override existing methods on a model.

The second inheritance mechanism (delegation) allows to link every record of a model to a record in a parent model, and provides transparent access to the fields of the parent record.

In OpenERP, there are 3 (4)ways to inherit from an existing model:

        _inherit = 'model' (without any _name)  ( Need to add fields in the same module)
        _inherit = 'model' (with a specified _name) (Need to add fields in  some other module )
        _inherits = 'model' Polymorphic data using _inherits  _inherits = {'res.partner': 'partner_id'}

   So what about our '_name' property

    If the _name as the same value as the inherited class it will do a basic inheritance.
    If you forget to add the _inherit you will redefine the model
    If your class _inherit one model and you set a _name different it will create a new model in a new database table.
    If your class inherit many model you have to set _name if your override an existing model this way you may have some trouble, it should be avoided. It is better to use this to create new classes that inherit from abstract model.


    What's the difference between them and, therefore, how to use them properly ?


In OpenERP we have many main type of inheritance:

Classical using Python inheritance.

It allows to add specific "generic" behavior to Model by inheriting classes that derive from orm.Model like geoModel that adds goegraphic support.

class Myclass(GeoModel, AUtilsClass):

Standard Using _inherit

The main objective is to add new behaviors/extend existing models. For example you want to add a new field to an invoice and add a new method: `

Multiple inheritance
 _name = must be used
 _inherit = ['model_1', 'model_2']



It is important to notice that _inherit can be a string or a list. You can do _inherit = ['model_1', 'model_2']


Polymorphic data using _inherits

 _inherits = {'res.partner': 'partner_id'}

When using _inherits you will do a kind of polymorphic model in the database way.

This mean we create a model that gets the know how of a Model but adds aditional data/columns in a new database table. So when you create a user, all partner data is stored in res_partner table (and a partner is created) and all user related info is stored in res_users table.

To do this we use a dict:The key corresponds to the base model and the value to the foreign key to the base model.






View inheritance


Instead of modifying existing views in place (by overwriting them), Odoo provides view inheritance where children "extension" views are applied on top of root views, and can add or remove content from their parent.

An extension view references its parent using the inherit_id field, and instead of a single view its arch field is composed of any number of xpath elements selecting and altering the content of their parent view:


_inherit='res.company'

<record id="edit_res_company" model="ir.ui.view">
            <field name="name">custom.module.form</field>
            <field name="model">res.company</field> # Inherited Module
            <field name="inherit_id" ref="base.view_company_form"/>
             <!-- base=name of the module-->
            <field name="arch" type="xml">
              <!-- find field description and add the field
             idea_ids after it -->
                <field name="name" position="after">
                <field name="domain_name"/> -->
                            OR
        <xpath expr="//field[@name='description']" position="after">
          <field name="idea_ids" string="Number of ideas"/>
        </xpath>
       
        </record>


expr
An XPath expression selecting a single element in the parent view. Raises an error if it matches no element or more than one
position

Operation to apply to the matched element:

inside
appends xpath's body at the end of the matched element
replace
replaces the matched element by the xpath's body
before
inserts the xpath's body as a sibling before the matched element
after
inserts the xpaths's body as a sibling after the matched element
attributes
alters the attributes of the matched element using special attribute elements in the xpath's body
Data files (demo.xml)

Module data is declared via data files, XML files with <record> elements. Each <record> element creates or updates a database record.

<openerp>
    <data>

        <record model="{model name}" id="{record_identifier}">
            <field name="{a field name}">{avalue}</field>
        </record>

    </data>
<openerp>

   .model is the name of the Odoo model for the record

    id is an external identifier, it allows referring to the record (without having to know its in-database identifier)

    <field> elements have a name which is the name of the field in the model (e.g. description). Their body is the field's value.

Data files have to be declared in the manifest file to be loaded, they can be declared in the 'data' list (always loaded) or in the 'demo' list (only loaded in demonstration mode).

    # only loaded in demonstration mode
    'demo': [
        'demo.xml',
    ],
Here is the EASIEST way to Install Odoo 8 on Ubuntu 14.04 from GitHub. You are 15 steps away from exploring Next Big Revolution: “Odoo 8″. Open the terminal and execute below commands step-by-step to achieve excellence.

Step 1

Update apt source list 

 sudo apt-get update

Step 2

Install Updates 

sudo apt-get upgrade

Step 3

Install Python Dependencies for Odoo 8 

sudo apt-get install python-dateutil python-docutils python-feedparser python-jinja2 python-ldap python-libxslt1 python-lxml python-mako python-mock python-openid python-psycopg2 python-psutil python-pybabel python-pychart python-pydot python-pyparsing python-reportlab python-simplejson python-tz python-unittest2 python-vatnumber python-vobject python-webdav python-werkzeug python-xlwt python-yaml python-zsi poppler-utils python-pip python-pyPdf python-passlib python-decorator

Step 4

Install supporting packages for Odoo 8 

sudo apt-get install gcc python-dev mc bzr python-setuptools python-babel python-feedparser python-reportlab-accel python-zsi python-openssl python-egenix-mxdatetime python-jinja2 python-unittest2 python-mock python-docutils lptools make python-psutil python-paramiko poppler-utils python-pdftools antiword 

Step 5

Install PostgreSQL and GIT 

sudo apt-get install python-software-properties
sudo apt-get update
sudo apt-get install postgresql-9.3

Step 6

Create Database user for OpenERP Odoo 

sudo su postgres
postgres@openerp-desktop:/$ createuser -s openerp
postgres@openerp-desktop:/$ createuser -s system_name
postgres@openerp-desktop:/$ exit

Step 7

Create Odoo user and group

 sudo adduser --system --home=/opt/openerp --group openerp

Step 8

Download & Install Gdata 

cd /opt/openerp
sudo wget http://gdata-python-client.googlecode.com/files/gdata-2.0.17.tar.gz
sudo tar zxvf gdata-2.0.17.tar.gz
sudo chown -R openerp: gdata-2.0.17
sudo -s
cd gdata-2.0.17/
python setup.py install
exit

Step 9

Get lastest Odoo 8 from github repositoryDownload the Zip file from URL : 

“https://github.com/odoo/odoo/tree/8.0″ . 

Open Folder by command

 sudo su nautilus /opt/openerp
 
Now copy the unzip downloaded folder in opened window OR clone direct to the system from GitHub Repository by command :


git clone https://github.com/odoo/odoo.git --branch 8.0
sudo chown -R openerp: odoo-8.0

Step 10

Create folder for custom and test addons (OPTIONAL) 

sudo mkdir custom-addons test-addons
sudo chown -R openerp: custom-addons
sudo chown -R openerp: test-addons

Step 11

Create Odoo Log File 

sudo mkdir /var/log/openerp
sudo chown -R openerp:root /var/log/openerp

Step 12

Edit Odoo configuration file 

sudo cp /opt/openerp/odoo-8.0/debian/openerp-server.conf /etc/openerp-server.conf
sudo chown openerp: /etc/openerp-server.conf
sudo gedit /etc/openerp-server.conf

 
#Copy and paste below content in config file , write correct addons paths
[options]
; This is the password that allows database operations:
admin_passwd = PASSWORD
db_host = False
db_port = False
db_user = odoo
db_password = False
addons_path = /opt/openerp/odoo-8.0/addons
;Log settings
logfile = /var/log/openerp/openerp-server.log
log_level = error

Step 13

Setup Odoo service file 

sudo cp /opt/openerp/odoo-8.0/openerp-server /etc/init.d/openerp-server
sudo chmod 755 /etc/init.d/openerp-server
sudo chown root: /etc/init.d/openerp-server

Step 14

Now Start Odoo Server

 cd /opt/openerp/odoo
./openerp-server

Step 15

Go to web browser to access Odoo 8 

http://localhost:8069
 
Following are the other extra commands to be used while installing and using OpenERP when unusual error occurred such as:
Error 1. “Address already in use “: Then check the port through


ps aux | grep openerp
locate openerp
kill -9 portno.


Error 2. “Module or dependency not found”: then install that through

sudo apt-get install name_dependency

Error 3. “ImportError No module named ‘requests’ or ‘openerp’” : then follow below commands


cd /opt/openerp/odoo
sudo python setup.py install
Now you can start exploring new OpenERP i.e Odoo 8.
If you have a menu but it hasn't a submenu with a valid action, OpenERP doesn't show it.
You must create a main menu (the main that appears in the top bar menu), a section main menu (the violet menu in the section) and a valid menu (the real clickable menu):

<menuitem id="main_menu" name="Main"/>
    <menuitem id="section_main_menu" parent="main_menu" sequence="450"/>
    <menuitem id="real_menu" parent="section_main_menu" action="your_action"/>



I recommend assigning actions to the lowest menu level only.

The menuitem tag for xml views is used to create all menues on any hierarchy level. The actual hierarchy is controlled by the parent attribute.


The sequence gives the order for your menu items (low to high), 'Settings' menu got sequence 500, 'Reporting' menu got sequence 170
By Default sequence=10

For submenues you can use the parent attribute inside the menu tag: e.g. parent="menu_hr_root". If you leave this attribute your menu item will be at the very top

To manage the visibility for your menu item, you can use the groups attribute inside the menu tag: e.g. groups="base.group_user".
If you leave the groups attribute the menuitem will be visible to everyone .

NOte :Note :Access Rights need to be defined in csv file for that object.

To place a Menu under Other Module menus you need to include the Parent module in your __openerp__.py file and give the parent menu id in your menu item. Let me explain you with an Example. Consider a model called Test. To make that Module visible under Human Resources module do the following steps:

Step-1: in __openerp__.py add the following line "depends" : ["hr"],

Step-2: then in your view xml file add the following lines Line-1: <menuitem id="menu_test" parent="hr.menu_hr_root" name="Test Module Parent"/> Line-2: <menuitem id="menu_test_child" parent="menu_test" name="My Menu" action="action_test"/>

Here **parent="hr.menu_hr_root"** denotes the parent id under which you are going to place your menu.  i.e., hr.menu_hr_root is the id of the Human Resource menu. And **name="Test Module Parent"** denotes your Test menu's parent name and it can be anything.
The Line-2 denotes your clickable menu. Here the **action="action_test"** denotes the action to be carried out when you click on My Menu.

menu entries are stored as records of the 'ir.ui.menu' object type, so it is possible to use 'record' XML tags to update 'ir.ui.menu' records by providing 'model' and 'id' attributes to identify the existing record.


Overriding A menuitem

If we want to change the name of the alraedy existing menu then we can  do this is declaring a new menuitem with the same ID of the menuitem you want to change.

        <record model="ir.ui.menu" id="mod_id.menu_id">
          <field name="name">My New Menu Name</field>

<!-- Use the special many2many value syntax to add a child record, and the `ref()` method to resolve the group XML ID -->

<field name="groups_id" eval="[(4,ref('my_new_group_id'))]"/>

        </record>


Deleting the Menuitem :

<delete id="base.menu_crm_case_job_req_main" model="ir.ui.menu"/>


_rec_name = ‘name’ (by default) 

where ‘name’ is field of the model. If ‘name’ field is not there then you have to assign any other filed name to this ‘_rec_name’ variable from columns.

Note : If ‘name’ field is there in your columns then there is no need to specify '_rec_name'. OpenERP takes name field by default.

You have seen name in any form when you select many2one field. For example in Sale Order when you select Customer, you can see Customer's Name in that many2one field. Now if you want to show Customer's Phone Number in many2one field, you have to define phone field in _rec_name like this: _rec_name = 'phone'
If your columns don't have any name field then you have to define any field in _rec_name.


Let’s try to dig more using an example.


Case I:


If you are having hr.employee module with name field and you are using it as reference in other module, say hr.employee.detail to provide other employee details then you will use it as:

'employee_id' : fields.many2one('hr.employee', 'Employee')

and it will show you the employee name in selection list.

But, In a company, more than one employee can share the same name. So name field can’t be used to differentiate them from each other. But, employee code will be unique for each employee. In such cases, we will take a column field like ‘emp_code’ and will assign it to ‘_rec_name’

_rec_name = ‘emp_code’

So that it can show employee code in dropdown instead of employee name.

Note : In both above cases 'id' will only be store in database. If will affect only the display part.

------------------------------------------------------------------------------------

------------------------------------------------------------------------------------

name_get() 

Case II: 


In same above example if your manager asks you to show combination of both i.e Employee Name, Employee Code what you will do?

In this case we will override name_get() method in hr.employee module

  def name_get(self, cr, uid, ids, context=None):
        if not ids:
            return []
        reads = self.read(cr, uid, ids, ['name','emp_code'], context=context)
        res = []
        for record in reads:
            name = record['name']
            if record['emp_code']:
                name = record['emp_code'][1]+' / '+name
            res.append((record['id'], name))
        return res
and it will display combination of both in drop-down.

The name_get method is used to display value of a record in a many2one field.
Default name_get method return the output value "name" field value in the table (i.e many2one field).
suppose in the table contain no "name" field then we can use _rec_name = field_name
Now in name_get method return _rec_name field value.
 

Returns a textual representation for the records in self. By default this is the value of the display_name field.
Returns:    list of pairs (id, text_repr) for each records
Return Type:    list(tuple)

name_get is a function which return list of pair. The pair is the id of the item and the name the item, example [(1, "SO001"), (4, "SO004")].

 There is no special field to display the result of name_get. If you need to put the result of name_get method in a field of a record, you should create with an attribute 'compute' 

class Shivam(osv.osv):
    def name_get(self, cr, uid, ids, context=None):
        if not ids:
            return []
        reads = self.read(cr, uid, ids, ['name','parent_id'], context=context)
        res = []
        for record in reads:
            name = record['name']
            if record['parent_id']:
                name = record['parent_id'][1]+' / '+name
            res.append((record['id'], name))
        return res

    def _name_get_fnc(self, cr, uid, ids, prop, unknow_none, context=None):
        res = self.name_get(cr, uid, ids, context=context)
        return dict(res)
   
    _name='shivam.mahajan'
    _columns={
    'name': fields.char("Technology", required=True),
    'complete_name': fields.function(_name_get_fnc, type="char", string='Name'), # if you want u can show it in too
    'parent_id': fields.many2one('shivam.mahajan', 'Parent Technology', select=True), # By default
    }
            
All the datetime field in openerp are saved in the databse in the form of string :

record.birthday <type 'str'>

To convert string into datetime

till_datetime = datetime.strptime(record.birthday,"%Y-%m-%d")
1984-09-15 00:00:00 <type 'datetime.datetime'>

To convert datetime into string

till_datetime = till_datetime.strftime("%Y-%m-%d 23:59:59")
984-09-15 23:59:59 <type 'str'>


1.@api.returns
It will return a RecordSet of specified model based on original returned value:

@api.returns('res.partner')
def afun(self):
.
.
.
return x # a RecordSet


2.@api.one
The decorated method automatically loops on records (i.e, for each record in recordset it calls the method), and makes a list with the results.

self is redefined as current record:
@api.one
def afun(self):
self.name='todo'


3.@api.multi
The method typically defines an operation on records. Such a method:
Self will be the current RecordSet without iteration.
@api.multi
def afun(self):
len(self)



4.@api.model
This decorator will convert old API calls to decorated function to new API signature.
@api.model
def afun(self):
pass

5.@api.constrains
This decorator will ensure that decorated function will be called on create, write, unlink operation. If a constraint is met the function should raise a
openerp.exceptions.Warning with appropriate message.

6.@api.depends
This decorator will trigger the call to the decorated function if any of the fields specified in the decorator is altered by ORM or changed in the form:




@api.depends('name', 'an_other_field')
def afun(self):
pass
    1. 7.@api.onchange

This decorator will trigger the call to the decorated function if any of the fields specified in the decorator is changed in the form:
@api.onchange('fieldx')
def do_stuff(self):
if self.fieldx == x:
self.fieldy = 'toto'
In previous sample self corresponds to the record currently edited on the form. When in on_change context all work is done in the cache. So you can alter RecordSet inside your function without being worried about altering database. That’s the main difference with @api.depends
At function return, differences between the cache and the RecordSet will be returned to the form.

    1. 8.@api.noguess

This decorator prevent new API decorators to alter the output of a method


Source : http://zbeanztech.com/blog/method-decorators-odoo-8  
              http://odoo-new-api-guide-line.readthedocs.io/en/latest/decorator.html



In Odoo, a workflow is a technical artefact to manage a set of "things to do" associated to the records of a model. The workflow provides a higher-level way to organize tasks to perform with or on a record.
More specifically, a workflow is a directed graph where the nodes are called "activities" and the arcs are called "transitions".
  • Activities define work that should be done within the Odoo server, such as changing the state of some records, or sending emails.
  • Transitions control how the workflow progresses from activity to activity.
In the definition of a workflow, one can attach conditions, signals, and triggers to transitions, so that the behavior of the workflow depends on user actions (such as clicking on a button), changes to records, or arbitrary Python code.
All in all, Odoo's workflow system provides:
  • a description of the evolution of a record (document) over time
  • automatic actions based on various and flexible conditions
  • management of company roles and validation steps
  • management of interactions between objects
  • a visual representation of document flows through their lifecycle
For instance, a basic order could have the following flow:
Orders start in the Draft state, can be Confirmed by a user, and then either shipped (Closed) or Canceled.




    1. Basics

Defining a workflow with data files is straightforward: a record "workflow" is given together with records for the activities and the transitions. For instance, here is a simple sequence of two activities defined in XML
<record id="test_workflow" model="workflow">
<field name="name">test.workflow</field>
<field name="osv">test.workflow.model</field>
<field name="on_create">True</field>
</record>

<record id="activity_a" model="workflow.activity">
<field name="wkf_id" ref="test_workflow"/>
<field name="flow_start">True</field>
<field name="name">a</field>
<field name="kind">function</field>
<field name="action">print_a()</field>
</record>
<record id="activity_b" model="workflow.activity">
<field name="wkf_id" ref="test_workflow"/>
<field name="flow_stop">True</field>
<field name="name">b</field>
<field name="kind">function</field>
<field name="action">print_b()</field>
</record>

<record id="trans_a_b" model="workflow.transition">
<field name="act_from" ref="activity_a"/>
<field name="act_to" ref="activity_b"/>
</record>
A workflow is always defined with respect to a particular model (the model is given by the attribute osv on the model workflow). Methods specified in the activities or transitions will be called on that model.
In the example code above, a workflow called "test_workflow" is created. It is made up of two activities, named "a" and "b", and one transition, going from "a" to "b".
The first activity has its attribute flow_start set to True so that Odoo knows where to start the workflow traversal after it is instantiated. Because on_create is set to True on the workflow record, the workflow is instantiated for each newly created record. (Otherwise, the workflow should be instantiated by other means, such as from some module Python code.)
When the workflow is instantiated, it begins with activity "a". That activity is of kind function, which means that the action print_a() is a method call on the model test.workflow (the usual cr, uid, ids, context arguments are passed for you).
The transition between "a" and "b" does not specify any condition. This means that the workflow instance immediately goes from "a" to "b" after "a" has been processed, and thus also processes activity "b".
    1. Activities

While the transitions can be seen as the control structures of the work flows, activities are where everything happens, from changing record states to sending email.
Different kinds of activities exist: Dummy, Function, Subflow, and Stop all, each doing different things when the activity is processed. In addition to their kind, activities have other properties, detailed in the next sections.
      1. Flow start and flow stop

The attribute flow_start is a boolean value specifying whether the activity is processed when the workflow is instantiated. Multiple activities can have their attribute flow_start set to True. When instantiated a workflow for a record, Odoo simply processes all of them, and evaluate all their outgoing transitions afterwards.
The attribute flow_stop is a boolean value specifying whether the activity stops the workflow instance. A workflow instance is considered completed when all its activities with the attribute flow_stop set to True are completed.
It is important for Odoo to know when a workflow instance is completed. A workflow can have an activity that is actually another workflow (called a subflow); that activity is completed when the subflow is completed.
      1. Subflow

An activity can embed a complete workflow, called a subflow (the embedding workflow is called the parent workflow). The workflow to instantiate is specified by attribute subflow_id.
Note
In the GUI, that attribute can not be set unless the kind of the activity is Subflow.
The activity is considered completed (and its outgoing transitions ready to be evaluated) when the subflow is completed (see attribute flow_stop above).
      1. Sending a signal from a subflow

When a workflow is embedded in an activity (as a subflow) of a workflow, the sublfow can send a signal from its own activities to the parent workflow by giving a signal name in the attribute signal_send. Odoo processes those activities by sending the value of signal_send prefixed by "subflow." to the parent workflow instance.
In other words, it is possible to react and get transitions in the parent workflow as activities are executed in the subflow.
      1. Server actions

An activity can run a "Server Action" by specifying its ID in the attribute action_id.
      1. Python action

An activity can execute some Python code, given by the attribute action. The evaluation environment is the same as the one explained in the section Conditions.
      1. Split mode

After an activity has been processed, Odoo evaluates its transition to reach the next activity in the flow.
However if an activity has more than one transition, Odoo must decide which activity or activities to follow.
This choice is controlled by the split_mode attribute:
XOR (default)
By default, Odoo will use the first transition (in sequence order) whose condition is satisfied. All other transitions are ignored.
OR
In OR mode, all transitions with a satisfied condition are traversed simultanously. Transitions not yet valid will be ignored, even if they become valid later.
AND
In AND mode, Odoo will wait until all transitions are satisfied, and will traverse all of them (much like the OR mode).
Both OR and AND mode will lead to activities being active in the same workflow.
      1. Join mode

Just like outgoing transition conditions can be combined together to decide whether they can be traversed or not, incoming transitions can be combined together to decide if and when an activity may be processed.
The join_mode attribute controls that behavior:
XOR (default)
Any incoming transition enables the activity and starts its processing.
AND
The activity is enabled and processed only once all incoming transitions have been traversed.
      1. Kinds

An activity's kind defines the type of work an activity can perform.
Dummy (dummy, default)
Do nothing at all, or call a server action. Often used as dispatch or gather "hubs" for transitions.
Function (function)
Run some python code, execute a server action.
Stop all (stopall)
Completely stops the workflow instance and marks it as completed.
Subflow (subflow)
Starts executing an other workflow, once that workflow is completed the activity is done processing.
By default, the subflow is instantiated for the same record as the parent workflow. It is possible to change that behavior by providing Python code that returns a record ID (of the same data model as the subflow). The embedded subflow instance is then the one of the given record.
    1. Transitions

Transitions provide the control structures to orchestrate a workflow. When an activity is completed, the workflow engine tries to get across transitions departing from the completed activity, towards the next activities. In their simplest form (as in the example above), they link activities sequentially: activities are processed as soon as the activities preceding them are completed.
Instead of running all activities in one fell swoop, it is also possible to wait on transitions, going through them only when some criteria are met. The criteria are the conditions, the signals, and the triggers. They are detailed in the following sections.
      1. Conditions

When an activity has been completed, its outgoing transitions are inspected to determine whether it is possible for the workflow instance to proceed through them and reach the next activities. When only a condition is defined (i.e., no signal or trigger is defined), the condition is evaluated by Odoo, and if it evaluates to True, the workflow instance progresses through the transition. If the condition is not met, it will be reevaluated every time the associated record is modified, or by an explicit method call to do it.
By default, the attribute condition (i.e., the expression to be evaluated) is just "True", which trivially evaluates to True. Note that the condition may be several lines long; in that case, the value of the last one determines whether the transition can be taken.
In the condition evaluation environment, several symbols are conveniently defined (in addition to the Odoo safe_eval environment):
  • all the model column names, and
  • all the browse record's attributes.

Signals

In addition to a condition, a transition can specify a signal name. When such a signal name is present, the transition is not taken directly, even if the condition evaluates to True. Instead the transition blocks, waiting to be woken up.
In order to wake up a transition with a defined signal name, the signal must be sent to the workflow instance. A common way to send a signal is to use a button in the user interface, using the element <button/> with the signal name as the attribute name of the button. Once the button is clicked, the signal is sent to the workflow instance of the current record.
Note
The condition is still evaluated when the signal is sent to the workflow instance.
      1. Triggers

With conditions that evaluate to False, transitions are not taken (and thus the activity it leads to is not processed immediately). Still, the workflow instance can get new chances to progress across that transition by providing so-called triggers. The idea is that when the condition is not satisfied, triggers are recorded in database. Later, it is possible to wake up specifically the workflow instances that installed those triggers, offering them to reevaluate their transition conditions. This mechanism makes it cheaper to wake up workflow instances by targetting just a few of them (those that have installed the triggers) instead of all of them.
Triggers are recorded in database as record IDs (together with the model name) and refer to the workflow instance waiting for those records. The transition definition provides a model name (attribute trigger_model) and a Python expression (attribute trigger_expression) that evaluates to a list of record IDs in the given model. Any of those records can wake up the workflow instance they are associated with.
Note
triggers are not re-installed whenever the transition is re-tried.



Move around workflow states back and forth?
-----------------------------------------------
-----------------------------------------------

class Sale_order(osv.Model):

    _inherit = 'sale.order'

    _columns = {
        'state': fields.selection(
            [
            ('draft', 'Draft Quotation'),
             # New State Added
            ('my_new_state', 'My New State'),
            ('sent', 'Quotation Sent'),
            ('cancel', 'Cancelled'),
            ('waiting_date', 'Waiting Schedule'),
            ('progress', 'Sales Order'),
            ('manual', 'Sale to Invoice'),
            ('invoice_except', 'Invoice Exception'),
            ('done', 'Done'),
            ],
            'Status',
            readonly=True,
            track_visibility='onchange',
            help="Gives the status of the quotation or sales order. \nThe exception status is automatically set when a cancel operation occurs in the processing of a document linked to the sales order. \nThe 'Waiting Schedule' status is set when the invoice is confirmed but waiting for the scheduler to run on the order date.",
            select=True
            ),
    }



    <openerp>
    <data>

        # <!-- Inherit the sale order model's form view and customize -->
        <record id="sale_form_view" model="ir.ui.view">
            <field name="name">sale.order.form.inherit</field>
            <field name="model">sale.order</field>
            <field name="inherit_id" ref="sale.view_order_form"/>
            <field name="arch" type="xml">
                <!-- Statusbar widget should also contain the new status -->
                <field name="state" position="replace">
                    <field name="state" widget="statusbar" statusbar_visible="draft,my_new_state,sent,invoiced,done" statusbar_colors='{"invoice_except":"red","waiting_date":"blue"}'/>
                </field>
            </field>
        </record>



    </data>
</openerp>


sale_workflow.xml





<openerp>
    <data>

# That code tells OpenERP that once a user arrives to this activity
# (which is a part of an existing sale.wkf_sale workflow)
# via some transition, a python function called action_my_new_function() should be executed.


        <!-- Create new activity for the new state -->
        <record id="act_my_new_state" model="workflow.activity">
            <field name="wkf_id" ref="sale.wkf_sale"/>
             # <field name="flow_start">True</field>/<field name="flow_stop">True</field>
            <field name="name">my_new_state</field>
            <field name="kind">function</field>
            <field name="action">action_my_new_function()</field>
        </record>

# Let's say we want to be able to progress from
# Draft Quotation to My New State, from
#  My New State to Quotation Sent,
# and also go back from Quotation Sent to My New State. For that we need the following transitions:


        <!-- Create transitions -->

        <!-- From Draft to new state -->
        <record id="trans_draft_my_new_state" model="workflow.transition">
            <field name="act_from" ref="sale.act_draft"/>
            <field name="act_to" ref="act_my_new_state"/>
            <field name="signal">signal_my_new_state_forward</field>
        </record>   

        <!-- From new state to Quotation Sent -->
        <record id="trans_new_state_sent" model="workflow.transition">
            <field name="act_from" ref="act_my_new_state"/>
            <field name="act_to" ref="sale.act_sent"/>
            <field name="signal">signal_quotation_sent</field>
        </record>

        <!-- From Quotation Sent back to new state -->
        <record id="trans_sent_new_state" model="workflow.transition">
            <field name="act_from" ref="sale.act_sent"/>
            <field name="act_to" ref="act_my_new_state"/>
            <field name="signal">signal_my_new_state_backward</field>
        </record> 

    </data>
</openerp>

# The signals tell what user inputs are used to trigger the transition,
# In our example we'll use the press of a button as a trigger.
# Let's add new buttons to the form view definition. Note the states attributes,
# they limit in which states the button will be visible.
# The name attribute of the button matches the signal defined in the workflow and
# thus triggers the transition to a specific activity.



<?xml version="1.0" encoding="UTF-8"?>
<openerp>
    <data>
        <!-- Inherit the sale order model's form view and customize -->
        <record id="sale_form_view" model="ir.ui.view">
            <field name="name">sale.order.form.inherit</field>
            <field name="model">sale.order</field>
            <field name="inherit_id" ref="sale.view_order_form"/>
            <field name="arch" type="xml">

                <!-- Statusbar widget should also contain the new statuses -->
                <field name="state" position="replace">
                    <field name="state" widget="statusbar" statusbar_visible="draft,my_new_state,sent,invoiced,done" statusbar_colors='{"invoice_except":"red","waiting_date":"blue"}'/>
                </field>

                <field name="state" position="before">
                    <!-- From draft to new state -->
                    <button string="Send to new state" type="workflow" name="signal_my_new_state_forward" states="draft" class="oe_highlight"/>

                    <!-- From new state to Quotation Sent -->
                    <button string="Mark as sent" type="workflow" name="signal_quotation_sent" states="my_new_state" class="oe_highlight"/>

                    <!-- From Quotation Sent back to new state -->
                    <button string="Back to new state" type="workflow"  name="signal_my_new_state_backward" states="sent" class="oe_highlight"/>
                </field>
            </field>
        </record>
    </data>
</openerp>

Python code that actually changes the sale order's state on button click.
little code that is put inside our sale order subclass
Note that the function name is the same as in the action of our new activity, i.e. action_my_new_function()

def action_my_new_function(self, cr, uid, ids, context=None):
    res = self.write(cr, uid, ids, {'state': 'my_new_state'}, context=context)   
    return res
 



























Copyright © 2013 SoftKul