← Back to team overview

openerp-community-reviewer team mailing list archive

[Merge] lp:~ajite/multi-company/multi-company into lp:multi-company

 

Augustin Cisterne-Kaas - www.elico-corp.com has proposed merging lp:~ajite/multi-company/multi-company into lp:multi-company.

Commit message:
[ADD]  base_intercompany and base_intercompany_sale modules.

Requested reviews:
  Multi Company Core Editors (multi-company-core-editors)

For more details, see:
https://code.launchpad.net/~ajite/multi-company/multi-company/+merge/203454

Added base_intercompany and base_intercompany_sale modules.
-- 
https://code.launchpad.net/~ajite/multi-company/multi-company/+merge/203454
Your team Multi Company Core Editors is requested to review the proposed merge of lp:~ajite/multi-company/multi-company into lp:multi-company.
=== added directory 'base_intercompany'
=== added file 'base_intercompany/__init__.py'
--- base_intercompany/__init__.py	1970-01-01 00:00:00 +0000
+++ base_intercompany/__init__.py	2014-01-28 03:02:10 +0000
@@ -0,0 +1,27 @@
+# -*- coding: utf-8 -*-
+##############################################################################
+#
+#    OpenERP, Open Source Management Solution
+#    Copyright (c) 2010-2014 Elico Corp. All Rights Reserved.
+#    Augustin Cisterne-Kaas <augustin.cisterne-kaas@xxxxxxxxxxxxxx>
+#    Eric Caudal <eric.caudal@xxxxxxxxxxxxxx>
+
+#    This program is free software: you can redistribute it and/or modify
+#    it under the terms of the GNU Affero General Public License as
+#    published by the Free Software Foundation, either version 3 of the
+#    License, or (at your option) any later version.
+#
+#    This program is distributed in the hope that it will be useful,
+#    but WITHOUT ANY WARRANTY; without even the implied warranty of
+#    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+#    GNU Affero General Public License for more details.
+#
+#    You should have received a copy of the GNU Affero General Public License
+#    along with this program.  If not, see <http://www.gnu.org/licenses/>.
+#
+##############################################################################
+import connector
+import consumer
+import backend
+import company
+import icops_model

=== added file 'base_intercompany/__openerp__.py'
--- base_intercompany/__openerp__.py	1970-01-01 00:00:00 +0000
+++ base_intercompany/__openerp__.py	2014-01-28 03:02:10 +0000
@@ -0,0 +1,49 @@
+# -*- coding: utf-8 -*-
+##############################################################################
+#
+#    OpenERP, Open Source Management Solution
+#    Copyright (c) 2010-2014 Elico Corp. All Rights Reserved.
+#    Augustin Cisterne-Kaas <augustin.cisterne-kaas@xxxxxxxxxxxxxx>
+#    Eric Caudal <eric.caudal@xxxxxxxxxxxxxx>
+
+#    This program is free software: you can redistribute it and/or modify
+#    it under the terms of the GNU Affero General Public License as
+#    published by the Free Software Foundation, either version 3 of the
+#    License, or (at your option) any later version.
+#
+#    This program is distributed in the hope that it will be useful,
+#    but WITHOUT ANY WARRANTY; without even the implied warranty of
+#    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+#    GNU Affero General Public License for more details.
+#
+#    You should have received a copy of the GNU Affero General Public License
+#    along with this program.  If not, see <http://www.gnu.org/licenses/>.
+#
+##############################################################################
+
+{'name': 'Base Intercompany',
+ 'version': '0.3',
+ 'category': 'Generic Modules',
+ 'depends': ['connector'],
+ 'author': 'Elico Corp',
+ 'license': 'AGPL-3',
+ 'website': 'https://www.elico-corp.com',
+ 'description': """
+This module is the structure designed to manage Inter-company Process (ICOPS)
+and allows 2 companies to create objects in each other.
+This module needs to be installed with one of the following modules:
+     - Base Intercompany Sale
+     - Base Intercompany Stock (in development)
+     - Any module which extends the Base Intercompany module
+
+TODO: demo data to be improved.\n
+Blueprint: https://blueprints.launchpad.net/multi-company/+spec/icops
+""",
+ 'images': [],
+ 'demo': ['base_intercompany_demo.xml'],
+ 'data': ['security/ir.model.access.csv',
+          'icops_model_view.xml',
+          'base_intercompany_menu.xml'],
+ 'installable': True,
+ 'application': False,
+ }

=== added file 'base_intercompany/backend.py'
--- base_intercompany/backend.py	1970-01-01 00:00:00 +0000
+++ base_intercompany/backend.py	2014-01-28 03:02:10 +0000
@@ -0,0 +1,30 @@
+# -*- coding: utf-8 -*-
+##############################################################################
+#
+#    OpenERP, Open Source Management Solution
+#    Copyright (c) 2010-2014 Elico Corp. All Rights Reserved.
+#    Augustin Cisterne-Kaas <augustin.cisterne-kaas@xxxxxxxxxxxxxx>
+#    Eric Caudal <eric.caudal@xxxxxxxxxxxxxx>
+
+#    This program is free software: you can redistribute it and/or modify
+#    it under the terms of the GNU Affero General Public License as
+#    published by the Free Software Foundation, either version 3 of the
+#    License, or (at your option) any later version.
+#
+#    This program is distributed in the hope that it will be useful,
+#    but WITHOUT ANY WARRANTY; without even the implied warranty of
+#    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+#    GNU Affero General Public License for more details.
+#
+#    You should have received a copy of the GNU Affero General Public License
+#    along with this program.  If not, see <http://www.gnu.org/licenses/>.
+#
+##############################################################################
+import openerp.addons.connector.backend as backend
+
+
+icops = backend.Backend('icops')
+""" Generic ICOPS Backend """
+
+icops7 = backend.Backend(parent=icops, version='7.0')
+""" ICOPS Backend for OpenERP 7 """

=== added file 'base_intercompany/base_intercompany_demo.xml'
--- base_intercompany/base_intercompany_demo.xml	1970-01-01 00:00:00 +0000
+++ base_intercompany/base_intercompany_demo.xml	2014-01-28 03:02:10 +0000
@@ -0,0 +1,76 @@
+<?xml version="1.0" encoding="utf-8"?>
+<openerp>
+    <data noupdate="1">
+        <record id="customer_origin" model="res.partner">
+            <field name="name">Company Origin</field>
+        </record>
+
+        <record id="supplier_destination" model="res.partner">
+            <field name="name">Company Destination</field>
+            <field name="supplier" eval="True" />
+        </record>
+
+        <record id="company_origin" model="res.company">
+            <field name="name">Company Origin</field>
+            <field name="parent_id" ref="base.main_company"/>
+            <field name="partner_id" ref="customer_origin" />
+        </record>
+
+        <record id="company_destination" model="res.company">
+            <field name="name">Company Destination</field>
+            <field name="parent_id" ref="base.main_company"/>
+            <field name="partner_id" ref="supplier_destination" />
+        </record>
+
+        <record id="customer_origin" model="res.partner">
+            <field name="company_id"></field>
+        </record>
+
+        <record id="supplier_destination" model="res.partner">
+            <field name="company_id"></field>
+        </record>
+
+        <record id="partner_origin" model="res.partner">
+            <field name="name">User Origin</field>
+            <field name="company_id" ref="company_origin"/>
+            <field name="customer" eval="False"/>
+        </record>
+
+        <record id="user_origin" model="res.users">
+            <field name="partner_id" ref="partner_origin"/>
+            <field name="login">user.origin</field>
+            <field name="password">user.origin</field>
+            <field name="company_id" ref="company_origin"/>
+            <field name="company_ids" eval="[(6,0,[ref('company_origin')])]" />
+            <field name="groups_id" eval="[(6,0,[ref('base.group_user'), ref('base.group_partner_manager')])]"/>
+        </record>
+
+        <record id="partner_destination" model="res.partner">
+            <field name="name">User Destination</field>
+            <field name="company_id" ref="company_destination"/>
+            <field name="customer" eval="False"/>
+        </record>
+
+        <record id="user_destination" model="res.users">
+            <field name="partner_id" ref="partner_destination"/>
+            <field name="login">user.destination</field>
+            <field name="password">user.destination</field>
+            <field name="company_id" ref="company_destination"/>
+            <field name="company_ids" eval="[(6,0,[ref('company_destination')])]" />
+            <field name="groups_id" eval="[(6,0,[ref('base.group_user'), ref('base.group_partner_manager')])]"/>
+        </record>
+        
+        <!-- ICOPS Setup -->
+        <record id="backend_origin" model="icops.backend">
+            <field name="name">Backend Origin</field>
+            <field name="company_id" ref="company_origin" />
+            <field name="icops_uid" ref="user_origin" />
+        </record>
+
+        <record id="backend_destination" model="icops.backend">
+            <field name="name">Backend Destination</field>
+            <field name="company_id" ref="company_destination" />
+            <field name="icops_uid" ref="user_destination" />
+        </record>
+    </data>
+</openerp>
\ No newline at end of file

=== added file 'base_intercompany/base_intercompany_menu.xml'
--- base_intercompany/base_intercompany_menu.xml	1970-01-01 00:00:00 +0000
+++ base_intercompany/base_intercompany_menu.xml	2014-01-28 03:02:10 +0000
@@ -0,0 +1,22 @@
+<?xml version="1.0" encoding="utf-8"?>
+<openerp>
+  <data>
+    <menuitem id="menu_icops_root"
+        parent="connector.menu_connector_root"
+        name="ICOPS"
+        sequence="10"
+        groups="connector.group_connector_manager"/>
+
+    <menuitem id="menu_icops_backend"
+        name="Backends"
+        parent="menu_icops_root"
+        action="action_icops_backend"/>
+
+    <menuitem id="menu_server_root"
+        parent="connector.menu_connector_root"
+        name="Server"
+        sequence="20"
+        groups="connector.group_connector_manager"/>
+
+  </data>
+</openerp>

=== added file 'base_intercompany/company.py'
--- base_intercompany/company.py	1970-01-01 00:00:00 +0000
+++ base_intercompany/company.py	2014-01-28 03:02:10 +0000
@@ -0,0 +1,142 @@
+# -*- coding: utf-8 -*-
+##############################################################################
+#
+#    OpenERP, Open Source Management Solution
+#    Copyright (c) 2010-2014 Elico Corp. All Rights Reserved.
+#    Augustin Cisterne-Kaas <augustin.cisterne-kaas@xxxxxxxxxxxxxx>
+#    Eric Caudal <eric.caudal@xxxxxxxxxxxxxx>
+
+#    This program is free software: you can redistribute it and/or modify
+#    it under the terms of the GNU Affero General Public License as
+#    published by the Free Software Foundation, either version 3 of the
+#    License, or (at your option) any later version.
+#
+#    This program is distributed in the hope that it will be useful,
+#    but WITHOUT ANY WARRANTY; without even the implied warranty of
+#    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+#    GNU Affero General Public License for more details.
+#
+#    You should have received a copy of the GNU Affero General Public License
+#    along with this program.  If not, see <http://www.gnu.org/licenses/>.
+#
+##############################################################################
+
+from openerp.osv import orm, fields
+
+
+class res_intercompany(orm.Model):
+    _name = 'res.intercompany'
+    _description = 'Inter-Company'
+
+    def _select_concepts(self, cr, uid, context=None):
+        """ Available concepts
+
+        Can be inherited to add custom versions.
+        """
+        return []
+
+    def _select_models(self, cr, uid, context=None):
+        """ Available Object names
+
+        Can be inherited to add custom versions.
+        """
+        return {}
+
+    def _get_model(self, cr, uid, ids, name, arg, context=None):
+        """ Available versions
+
+        Can be inherited to add custom versions.
+        """
+        res = {}
+        for intercompany in self.browse(cr, uid, ids, context):
+            model = None
+            if intercompany.concept:
+                model = self._select_models(
+                    cr, uid)[intercompany.concept]
+            res[intercompany.id] = model
+        return res
+
+    def get_intercompany(self, cr, uid, obj_id,
+                         obj_class_name, obj_name, context=None):
+        """
+        get company from and to
+        """
+        if isinstance(obj_id, list):
+            obj_id = obj_id[0]
+        assert isinstance(obj_id, int) or isinstance(obj_id, long)
+        obj = self.pool.get(obj_class_name).browse(
+            cr,
+            uid,
+            obj_id,
+            context=None)
+        company = obj.company_id
+        ic_uid_ids = None
+        company_to_ids = []
+        for ic in company.intercompany_ids:
+            if ic.concept == obj_name:
+                company_to_ids.append(ic.company_to.id)
+                ic_uid_ids = ic.icops_uid.id
+        if company.intercompany_ids:
+            ic_uid_ids = company.intercompany_ids[0].icops_uid.id
+        return obj.company_id.id, company_to_ids, ic_uid_ids
+
+    def _check_intercompany_user(self, cr, uid, ids, context=None):
+        for ic in self.browse(cr, uid, ids, context=context):
+            if not ic.icops_uid:
+                return False
+        return True
+
+    _columns = {
+        'backend_id': fields.many2one('icops.backend', 'Original Backend',
+                                      required=True, ondelete='cascade'),
+        'backend_to': fields.many2one('icops.backend', 'Destination Backend',
+                                      required=True, ondelete='cascade'),
+        'concept': fields.selection(_select_concepts, string="Concept",
+                                    required=True),
+        'model': fields.function(_get_model, type='char',
+                                 string='Object', store=False),
+        'icops_uid': fields.related(
+            'backend_to', 'icops_uid', type='many2one',
+            relation='res.users', readonly=True, string='IC User'),
+        'on_create': fields.boolean('Create'),
+        'on_write': fields.boolean('Update'),
+        'on_unlink': fields.boolean('Delete'),
+        'on_confirm': fields.boolean('Confirm'),
+        'on_cancel': fields.boolean('Cancel')
+    }
+
+    _constraints = [
+        (_check_intercompany_user, 'Please set IC user for the Company first',
+            ['icops_uid'])
+    ]
+
+    _defaults = {
+        'backend_id': lambda self, cr, uid, c: c.get('active_id', False),
+    }
+    # _sql_constraints = [(
+    #     'company_from_company_to_unique', 'unique(company_from, company_to)',
+    #     'A setup for that company already exists')]
+
+    def check_need_create_intercompany_object(
+            self, cr, uid, company_from, company_to, concept,
+            event, return_list_type=False, regular=False):
+        """ @company_from
+            @company_to
+            @o2o_field_name  file name.Example, so2po po2so ,,,,
+            @node  value of  o2o_field_name ,  draft, confirm
+            @return_list_type,  the return type is list or boolean
+        """
+
+        if type(company_to) != list:
+            company_to = [company_to]
+        request = [('company_from', '=', company_from),
+                   ('concept', '=', concept),
+                   (event, '=', True)]
+        if not regular:
+            request.append(('company_to', 'in', company_to))
+        intercompany_ids = self.search(
+            cr, uid, request)
+        if return_list_type:
+            return intercompany_ids
+        else:
+            return intercompany_ids and True or False

=== added file 'base_intercompany/connector.py'
--- base_intercompany/connector.py	1970-01-01 00:00:00 +0000
+++ base_intercompany/connector.py	2014-01-28 03:02:10 +0000
@@ -0,0 +1,136 @@
+# -*- coding: utf-8 -*-
+##############################################################################
+#
+#    OpenERP, Open Source Management Solution
+#    Copyright (c) 2010-2014 Elico Corp. All Rights Reserved.
+#    Augustin Cisterne-Kaas <augustin.cisterne-kaas@xxxxxxxxxxxxxx>
+#    Eric Caudal <eric.caudal@xxxxxxxxxxxxxx>
+
+#    This program is free software: you can redistribute it and/or modify
+#    it under the terms of the GNU Affero General Public License as
+#    published by the Free Software Foundation, either version 3 of the
+#    License, or (at your option) any later version.
+#
+#    This program is distributed in the hope that it will be useful,
+#    but WITHOUT ANY WARRANTY; without even the implied warranty of
+#    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+#    GNU Affero General Public License for more details.
+#
+#    You should have received a copy of the GNU Affero General Public License
+#    along with this program.  If not, see <http://www.gnu.org/licenses/>.
+#
+##############################################################################
+
+from openerp.osv import orm, osv, fields
+from openerp.addons.connector.connector import Environment
+from openerp.addons.connector.checkpoint import checkpoint
+
+
+class base_intercompany_installed(orm.AbstractModel):
+    """Empty model used to know if the module is installed on the
+    database.
+
+    If the model is in the registry, the module is installed.
+    """
+    _name = 'base_intercompany.installed'
+
+
+def get_environment(session, model_name, backend_id):
+    """ Create an environment to work with.  """
+    backend_record = session.browse('icops.backend', backend_id)
+    env = Environment(backend_record, session, model_name)
+    lang_code = 'en_US'
+    env.set_lang(code=lang_code)
+    return env
+
+
+class icops_binding(orm.AbstractModel):
+    _name = 'icops.binding'
+    _inherit = 'external.binding'
+    _description = 'Coswin Binding (abstract)'
+
+    _columns = {
+        'backend_id': fields.many2one(
+            'icops.backend',
+            'ICOPS Backend',
+            required=True,
+            ondelete='restrict'),
+        'icops_ids': fields.one2many(
+            'icops.record', 'binding_id',
+            string="ICOPS Record"),
+    }
+
+
+class icops_record(orm.Model):
+    _name = 'icops.record'
+
+    _columns = {
+        'binding_id': fields.integer('ICOPS Binding'),
+        'record_id': fields.integer('ICOPS ID'),
+        'model': fields.char('Model'),
+        'concept': fields.char('Concept'),
+        'backend_id': fields.many2one(
+            'icops.backend', 'ICOPS Backends'),
+    }
+
+
+class icops_model(orm.AbstractModel):
+    _name = 'icops.model'
+
+    def _is_locked(self, cr, uid, ids, name, arg, context=None):
+        res = {}
+        for obj in self.browse(cr, uid, ids, context=context):
+            pool = self.pool.get('icops.record')
+            record_ids = pool.search(
+                cr, uid, [('record_id', '=', obj.id),
+                          ('model', '=', self._name)])
+
+            res[obj.id] = True if record_ids else False
+        return res
+
+    def _check_icops(self, cr, uid, ids, context=None):
+        context = context or {}
+        if isinstance(ids, int):
+            ids = [ids]
+        if 'icops' in context:
+            return
+        fields = ['locked', 'temp_unlock']
+        for obj in self.read(cr, uid, ids, fields, context=context):
+            if obj['temp_unlock']:
+                return
+            elif obj['locked']:
+                raise osv.except_osv(
+                    'ICOPS Error',
+                    'This object is locked by an intercompany process')
+
+    _columns = {
+        'locked': fields.function(
+            _is_locked, type='boolean', string='Is Locked', store=False),
+        # To handle special workflow
+        'temp_unlock': fields.boolean('Temporary Unlock')
+    }
+
+    def action_unlock(self, cr, uid, ids, context):
+        pool = self.pool.get('icops.record')
+        for obj in self.browse(cr, uid, ids, context=context):
+            record_ids = pool.search(
+                cr, uid, [('record_id', '=', obj.id),
+                          ('model', '=', self._name)])
+            pool.unlink(cr, uid, record_ids, context=context)
+
+
+def add_checkpoint(session, model_name, record_id, backend_id):
+    """ Add a row in the model ``connector.checkpoint`` for a record,
+    meaning it has to be reviewed by a user.
+
+    :param session: current session
+    :type session: :py:class:openerp.addons.connector.session.ConnectorSession
+    :param model_name: name of the model of the record to be reviewed
+    :type model_name: str
+    :param record_id: ID of the record to be reviewed
+    :type record_id: int
+    :param backend_id: ID of the Coswin Backend
+    :type backend_id: int
+    """
+    return checkpoint.add_checkpoint(session, model_name, record_id,
+                                     'icops.backend', backend_id)

=== added file 'base_intercompany/consumer.py'
--- base_intercompany/consumer.py	1970-01-01 00:00:00 +0000
+++ base_intercompany/consumer.py	2014-01-28 03:02:10 +0000
@@ -0,0 +1,77 @@
+# -*- coding: utf-8 -*-
+##############################################################################
+#
+#    OpenERP, Open Source Management Solution
+#    Copyright (c) 2010-2014 Elico Corp. All Rights Reserved.
+#    Augustin Cisterne-Kaas <augustin.cisterne-kaas@xxxxxxxxxxxxxx>
+#    Eric Caudal <eric.caudal@xxxxxxxxxxxxxx>
+
+#    This program is free software: you can redistribute it and/or modify
+#    it under the terms of the GNU Affero General Public License as
+#    published by the Free Software Foundation, either version 3 of the
+#    License, or (at your option) any later version.
+#
+#    This program is distributed in the hope that it will be useful,
+#    but WITHOUT ANY WARRANTY; without even the implied warranty of
+#    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+#    GNU Affero General Public License for more details.
+#
+#    You should have received a copy of the GNU Affero General Public License
+#    along with this program.  If not, see <http://www.gnu.org/licenses/>.
+#
+##############################################################################
+from openerp.addons.connector.event import (on_record_write,
+                                            on_record_create,
+                                            on_record_unlink
+                                            )
+from .unit.export_synchronizer import (
+    export_record)
+
+_MODEL_NAMES = ()
+_BIND_MODEL_NAMES = ()
+_UNLINK_MODEL_NAMES = ()
+_UNLINK_BIND_MODEL_NAMES = ()
+
+
+@on_record_create(model_names=_BIND_MODEL_NAMES)
+@on_record_write(model_names=_BIND_MODEL_NAMES)
+def delay_export(session, model_name, record_id, fields=None):
+    """ Delay a job which export a binding record.
+
+    (A binding record being a ``icops.res.partner``,
+    ``icops.sale.order``, ...)
+    """
+    export_record(session, model_name, record_id, fields=fields)
+
+
+@on_record_write(model_names=_MODEL_NAMES)
+def delay_export_all_bindings(session, model_name, record_id, fields=None):
+    """ Delay a job which export all the bindings of a record.
+
+    In this case, it is called on records of normal models and will delay
+    the export for all the bindings.
+    """
+    model = session.pool.get(model_name)
+    record = model.browse(session.cr, session.uid,
+                          record_id, context=session.context)
+    for binding in record.icops_bind_ids:
+        export_record(session, binding._model._name, binding.id,
+                      fields=fields)
+
+
+@on_record_unlink(model_names=_UNLINK_MODEL_NAMES)
+def delay_unlink(session, model_name, record_id):
+    """ Delay a job which delete a record on Magento.
+
+    Called on binding records."""
+    fields = {'icops_delete': True}
+    delay_export_all_bindings(session, model_name, record_id, fields)
+
+
+@on_record_unlink(model_names=_UNLINK_BIND_MODEL_NAMES)
+def delay_unlink_binding(session, model_name, record_id):
+    """ Delay a job which delete a record on Magento.
+
+    Called on binding records."""
+    fields = {'icops_delete': True}
+    delay_export(session, model_name, record_id, fields)

=== added file 'base_intercompany/icops_model.py'
--- base_intercompany/icops_model.py	1970-01-01 00:00:00 +0000
+++ base_intercompany/icops_model.py	2014-01-28 03:02:10 +0000
@@ -0,0 +1,92 @@
+# -*- coding: utf-8 -*-
+##############################################################################
+#
+#    OpenERP, Open Source Management Solution
+#    Copyright (c) 2010-2014 Elico Corp. All Rights Reserved.
+#    Augustin Cisterne-Kaas <augustin.cisterne-kaas@xxxxxxxxxxxxxx>
+#    Eric Caudal <eric.caudal@xxxxxxxxxxxxxx>
+
+#    This program is free software: you can redistribute it and/or modify
+#    it under the terms of the GNU Affero General Public License as
+#    published by the Free Software Foundation, either version 3 of the
+#    License, or (at your option) any later version.
+#
+#    This program is distributed in the hope that it will be useful,
+#    but WITHOUT ANY WARRANTY; without even the implied warranty of
+#    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+#    GNU Affero General Public License for more details.
+#
+#    You should have received a copy of the GNU Affero General Public License
+#    along with this program.  If not, see <http://www.gnu.org/licenses/>.
+#
+##############################################################################
+
+import logging
+from openerp.osv import fields, orm
+
+_logger = logging.getLogger(__name__)
+
+
+class icops_backend(orm.Model):
+    _name = 'icops.backend'
+    _description = 'ICOPS Backend'
+    _inherit = 'connector.backend'
+
+    _backend_type = 'icops'
+
+    def _select_versions(self, cr, uid, context=None):
+        """ Available versions
+
+        Can be inherited to add custom versions.
+        """
+        return [('7.0', '7.0')]
+
+    _columns = {
+        'version': fields.selection(
+            _select_versions,
+            string='Version',
+            required=True),
+        'company_id': fields.many2one(
+            'res.company', string="Company", required=True),
+        'icops_ids': fields.one2many(
+            'res.intercompany', 'backend_id', 'Inter-Company Setup'),
+        'icops_uid': fields.many2one(
+            'res.users', 'IC User',
+            required=True,
+            domain="[('company_id', '=', company_id)]",
+            help="User to create update unlink IC records"),
+        'model': fields.related(
+            'backend_to', 'icops_uid', type='many2one',
+            relation='res.users', readonly=True, string='IC User'),
+    }
+
+    _defaults = {
+        'version': '7.0',
+    }
+
+    def _icops_backend(self, cr, uid, callback, domain=None,
+                       context=None):
+        if domain is None:
+            domain = []
+            ids = self.search(cr, uid, domain, context=context)
+            if ids:
+                callback(cr, uid, ids, context=context)
+
+    def prepare_binding(self, cr, uid, data, context=None):
+        context = context or {}
+        icops_bind_ids = []
+        if not 'icops_bind_ids' in data:
+            data['icops_bind_ids'] = None
+        if not 'icops' in context and not data['icops_bind_ids']:
+            user = self.pool.get('res.users').browse(
+                cr, uid, uid, context)
+            backend_pool = self.pool.get('icops.backend')
+            backend_ids = backend_pool.search(
+                cr, uid, [('company_id', '=', user.company_id.id)])
+            if backend_ids:
+                backends = backend_pool.browse(
+                    cr, uid, backend_ids, context)
+                for backend in backends:
+                    icops_bind_ids.append(
+                        (0, 0, {'backend_id': backend.id}))
+        return icops_bind_ids

=== added file 'base_intercompany/icops_model_view.xml'
--- base_intercompany/icops_model_view.xml	1970-01-01 00:00:00 +0000
+++ base_intercompany/icops_model_view.xml	2014-01-28 03:02:10 +0000
@@ -0,0 +1,59 @@
+<?xml version="1.0" encoding="utf-8"?>
+<openerp>
+    <data>
+        <record id="view_icops_backend_form" model="ir.ui.view">
+            <field name="name">icops.backend.form</field>
+            <field name="model">icops.backend</field>
+            <field name="arch" type="xml">
+                <form string="ICOPS Backend" version="7.0">
+                    <sheet>
+                        <label for="name" class="oe_edit_only"/>
+                        <h1>
+                            <field name="name" class="oe_inline" />
+                        </h1>
+                        <group name="icops" string="ICOPS Configuration">
+                            <group colspan="4" col="4">
+                                <field name="version" cols="4" />
+                                <field name="company_id" cols="2" />
+                                <field name="icops_uid" cols="2" />
+                            </group>
+                        </group>
+                        <field name="icops_ids" context="{'active_id': active_id}">
+                            <tree editable="bottom">
+                                <field name="concept"/> 
+                                <field name="model" invisible="1" /> 
+                                <field name="backend_id" invisible="1" />
+                                <field name="backend_to" />
+                                <field name='icops_uid' invisible="1" />
+                                <field name="on_create" />
+                                <field name="on_write" />
+                                <field name="on_unlink" />
+                                <field name="on_confirm" />
+                                <field name="on_cancel" />
+                            </tree>
+                        </field>
+                    </sheet>
+                </form>
+            </field>
+        </record>
+
+        <record id="view_icops_backend_tree" model="ir.ui.view">
+            <field name="name">icops.backend.tree</field>
+            <field name="model">icops.backend</field>
+            <field name="arch" type="xml">
+                <tree string="ICOPS Backend" version="7.0">
+                    <field name="name"/>
+                    <field name="company_id" />
+                </tree>
+            </field>
+        </record>
+
+        <record id="action_icops_backend" model="ir.actions.act_window">
+            <field name="name">ICOPS Backends</field>
+            <field name="res_model">icops.backend</field>
+            <field name="view_type">form</field>
+            <field name="view_mode">tree,form</field>
+            <field name="view_id" ref="view_icops_backend_tree"/>
+        </record>
+    </data>
+</openerp>

=== added directory 'base_intercompany/security'
=== added file 'base_intercompany/security/ir.model.access.csv'
--- base_intercompany/security/ir.model.access.csv	1970-01-01 00:00:00 +0000
+++ base_intercompany/security/ir.model.access.csv	2014-01-28 03:02:10 +0000
@@ -0,0 +1,3 @@
+id,name,model_id:id,group_id:id,perm_read,perm_write,perm_create,perm_unlink
+access_res_intercompany_manager,res intercompany manager,base_intercompany.model_res_intercompany,connector.group_connector_manager,1,1,1,1
+access_icops_backend_manager,icops backend manager,base_intercompany.model_icops_backend,connector.group_connector_manager,1,1,1,1
\ No newline at end of file

=== added directory 'base_intercompany/static'
=== added directory 'base_intercompany/static/description'
=== added file 'base_intercompany/static/description/Intercompany_setup.png'
Binary files base_intercompany/static/description/Intercompany_setup.png	1970-01-01 00:00:00 +0000 and base_intercompany/static/description/Intercompany_setup.png	2014-01-28 03:02:10 +0000 differ
=== added file 'base_intercompany/static/description/index.html'
--- base_intercompany/static/description/index.html	1970-01-01 00:00:00 +0000
+++ base_intercompany/static/description/index.html	2014-01-28 03:02:10 +0000
@@ -0,0 +1,25 @@
+<section class="oe_container">
+    <div class="oe_row">
+        <h2 class="oe_slogan">base_intercompany</h2> 
+        <h3 class="oe_slogan">Inter-Company Processing (ICOPS) <br /></h3>
+        <h4 class="oe_slogan"><a href="http://www.elico-corp.com";>By Elico Corp</a></h4>
+            <p>
+                This module is the structure designed to manage Inter-company Process (ICOPS) and allows 2 companies to create objects in each other. This module needs to be installed with one of the following modules:
+            </p>
+            <ul>
+                <li>Base Intercompany Sale</li>
+                <li>Base Intercompany Stock (in development)</li>
+                <li>Any module which extends the Base Intercompany module</li>
+            </ul>
+            <p>
+                TODO: demo data to be improved<br />
+                Blueprint: <a href="https://blueprints.launchpad.net/multi-company/+spec/icops";>https://blueprints.launchpad.net/multi-company/+spec/icops</a><br />
+            </p>
+            <div class="oe_row_img oe_centered oe_mt32">
+                <img class="oe_picture oe_screenshot" src="Intercompany_setup.png" />
+            </div>
+    </div>
+    <div class="oe_row oe_centeralign oe_more_space">
+        <a href="http://www.elico-corp.com/saas/"; class="oe_button oe_big">Start your <span class="oe_emph">free</span> trial</a>
+    </div>
+</section>

=== added directory 'base_intercompany/static/src'
=== added directory 'base_intercompany/static/src/img'
=== added file 'base_intercompany/static/src/img/icon.png'
Binary files base_intercompany/static/src/img/icon.png	1970-01-01 00:00:00 +0000 and base_intercompany/static/src/img/icon.png	2014-01-28 03:02:10 +0000 differ
=== added directory 'base_intercompany/unit'
=== added file 'base_intercompany/unit/__init__.py'
--- base_intercompany/unit/__init__.py	1970-01-01 00:00:00 +0000
+++ base_intercompany/unit/__init__.py	2014-01-28 03:02:10 +0000
@@ -0,0 +1,26 @@
+# -*- coding: utf-8 -*-
+##############################################################################
+#
+#    OpenERP, Open Source Management Solution
+#    Copyright (c) 2010-2014 Elico Corp. All Rights Reserved.
+#    Augustin Cisterne-Kaas <augustin.cisterne-kaas@xxxxxxxxxxxxxx>
+#    Eric Caudal <eric.caudal@xxxxxxxxxxxxxx>
+#
+#    This program is free software: you can redistribute it and/or modify
+#    it under the terms of the GNU Affero General Public License as
+#    published by the Free Software Foundation, either version 3 of the
+#    License, or (at your option) any later version.
+#
+#    This program is distributed in the hope that it will be useful,
+#    but WITHOUT ANY WARRANTY; without even the implied warranty of
+#    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+#    GNU Affero General Public License for more details.
+#
+#    You should have received a copy of the GNU Affero General Public License
+#    along with this program.  If not, see <http://www.gnu.org/licenses/>.
+#
+##############################################################################
+
+import binder
+import export_synchronizer
+import backend_adapter

=== added file 'base_intercompany/unit/backend_adapter.py'
--- base_intercompany/unit/backend_adapter.py	1970-01-01 00:00:00 +0000
+++ base_intercompany/unit/backend_adapter.py	2014-01-28 03:02:10 +0000
@@ -0,0 +1,151 @@
+# -*- coding: utf-8 -*-
+##############################################################################
+#
+#    OpenERP, Open Source Management Solution
+#    Copyright (c) 2010-2014 Elico Corp. All Rights Reserved.
+#    Augustin Cisterne-Kaas <augustin.cisterne-kaas@xxxxxxxxxxxxxx>
+#    Eric Caudal <eric.caudal@xxxxxxxxxxxxxx>
+
+#    This program is free software: you can redistribute it and/or modify
+#    it under the terms of the GNU Affero General Public License as
+#    published by the Free Software Foundation, either version 3 of the
+#    License, or (at your option) any later version.
+#
+#    This program is distributed in the hope that it will be useful,
+#    but WITHOUT ANY WARRANTY; without even the implied warranty of
+#    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+#    GNU Affero General Public License for more details.
+#
+#    You should have received a copy of the GNU Affero General Public License
+#    along with this program.  If not, see <http://www.gnu.org/licenses/>.
+#
+##############################################################################
+
+import logging
+from openerp.addons.connector.unit.backend_adapter import CRUDAdapter
+
+_logger = logging.getLogger(__name__)
+recorder = {}
+
+
+def call_to_key(method, arguments):
+    """ Used to 'freeze' the method and arguments of a call to Coswin
+    so they can be hashable; they will be stored in a dict.
+
+    Used in both the recorder and the tests.
+    """
+    def freeze(arg):
+        if isinstance(arg, dict):
+            items = dict((key, freeze(value)) for key, value
+                         in arg.iteritems())
+            return frozenset(items.iteritems())
+        elif isinstance(arg, list):
+            return tuple([freeze(item) for item in arg])
+        else:
+            return arg
+
+    new_args = []
+    for arg in arguments:
+        new_args.append(freeze(arg))
+    return (method, tuple(new_args))
+
+
+def record(method, arguments, result):
+    """ Utility function which can be used to record test data
+    during synchronisations. Call it from CoswinOracleAdapter._call
+
+    Then ``output_recorder`` can be used to write the data recorded
+    to a file.
+    """
+    recorder[call_to_key(method, arguments)] = result
+
+
+def output_recorder(filename):
+    import pprint
+    with open(filename, 'w') as f:
+        pprint.pprint(recorder, f)
+    _logger.debug('recorder written to file %s', filename)
+
+
+# class ICOPSLocation(object):
+
+#     def __init__(self, location, db_name, username, password):
+#         self.location = location
+#         self.db_name = db_name
+#         self.username = username
+#         self.password = password
+
+
+class GenericAdapter(CRUDAdapter):
+
+    """ External Records Adapter for Coswin """
+
+    def __init__(self, environment):
+        """
+
+        :param environment: current environment (backend, session, ...)
+        :type environment: :py:class:`connector.connector.Environment`
+        """
+        super(GenericAdapter, self).__init__(environment)
+        # self.icops = ICOPSLocation(self.backend_record.icops_server,
+        #                              self.backend_record.backup_server)
+
+    def search(self, filters=None):
+        """ Search records according to some criterias
+        and returns a list of ids """
+        raise NotImplementedError
+
+    def read(self, id, attributes=None):
+        """ Returns the information of a record """
+        raise NotImplementedError
+
+    def search_read(self, filters=None):
+        """ Search records according to some criterias
+        and returns their information"""
+        raise NotImplementedError
+
+    def create(self, data):
+        """ Create a record on the external system """
+        raise NotImplementedError
+
+    def write(self, id, data):
+        """ Update records on the external system """
+        raise NotImplementedError
+
+    def delete(self, id):
+        """ Delete a record on the external system """
+        raise NotImplementedError
+
+    def _write(self, text):
+        return True
+
+
+class ICOPSAdapter(GenericAdapter):
+
+    _model_name = None
+
+    def __init__(self, environment):
+        super(GenericAdapter, self).__init__(environment)
+        self._icops = None
+        self._backend_to = None
+
+    def _get_pool(self):
+        raise NotImplementedError
+
+    def create(self, data):
+        sess = self.session
+        pool = self._get_pool()
+        return pool.create(
+            sess.cr, self._backend_to.icops_uid.id, data, {'icops': True})
+
+    def write(self, id, data):
+        sess = self.session
+        pool = self._get_pool()
+        pool.write(sess.cr, self._backend_to.icops_uid.id, id, data,
+                   {'icops': True})
+
+    def delete(self, id):
+        sess = self.session
+        pool = self._get_pool()
+        pool.unlink(sess.cr, self._backend_to.icops_uid.id, [id],
+                    {'icops': True})

=== added file 'base_intercompany/unit/binder.py'
--- base_intercompany/unit/binder.py	1970-01-01 00:00:00 +0000
+++ base_intercompany/unit/binder.py	2014-01-28 03:02:10 +0000
@@ -0,0 +1,122 @@
+# -*- coding: utf-8 -*-
+##############################################################################
+#
+#    OpenERP, Open Source Management Solution
+#    Copyright (c) 2010-2014 Elico Corp. All Rights Reserved.
+#    Augustin Cisterne-Kaas <augustin.cisterne-kaas@xxxxxxxxxxxxxx>
+#    Eric Caudal <eric.caudal@xxxxxxxxxxxxxx>
+
+#    This program is free software: you can redistribute it and/or modify
+#    it under the terms of the GNU Affero General Public License as
+#    published by the Free Software Foundation, either version 3 of the
+#    License, or (at your option) any later version.
+#
+#    This program is distributed in the hope that it will be useful,
+#    but WITHOUT ANY WARRANTY; without even the implied warranty of
+#    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+#    GNU Affero General Public License for more details.
+#
+#    You should have received a copy of the GNU Affero General Public License
+#    along with this program.  If not, see <http://www.gnu.org/licenses/>.
+#
+##############################################################################
+
+from datetime import datetime
+from openerp.tools import DEFAULT_SERVER_DATETIME_FORMAT
+from openerp.addons.connector.connector import Binder
+from ..backend import icops
+
+
+class ICOPSBinder(Binder):
+    """ Generic Binder for ICOPS """
+
+
+@icops
+class ICOPSModelBinder(ICOPSBinder):
+    """
+    Bindings are done directly on the binding model.
+
+    Binding models are models called ``icops.{normal_model}``,
+    like ``icops.res.partner`` or ``icops.sale.order``.
+    They are ``_inherits`` of the normal models and contains
+    the ICOPS ID, the ID of the ICOPS Backend and the additional
+    fields belonging to the ICOPS instance.
+    """
+    _model_name = [
+        'icops.sale.order',
+        'icops.sale.order.line',
+        'icops.purchase.order',
+        'icops.purchase.order.line'
+    ]
+
+    def to_openerp(self, external_id, unwrap=False):
+        """ Give the OpenERP ID for an external ID
+
+        :param external_id: external ID for which we want the OpenERP ID
+        :param unwrap: if True, returns the openerp_id of the icops_xxxx
+        record, else return the id (binding id) of that record
+        :return: a record ID, depending on the value of unwrap,
+                 or None if the external_id is not mapped
+        :rtype: int
+        """
+        binding_ids = self.session.search(
+            self.model._name,
+            [('icops_id', '=', external_id),
+             ('backend_id', '=', self.backend_record.id)])
+        if not binding_ids:
+            return None
+        assert len(binding_ids) == 1, "Several records found: %s" % binding_ids
+        binding_id = binding_ids[0]
+        if unwrap:
+            return self.session.read(self.model._name,
+                                     binding_id,
+                                     ['openerp_id'])['openerp_id'][0]
+        else:
+            return binding_id
+
+    def to_backend(self, binding_id):
+        """ Give the external ID for an OpenERP ID
+
+        :param binding_id: OpenERP ID for which we want the external id
+        :return: backend identifier of the record
+        """
+        record = self.session.browse(self.model._name,
+                                     binding_id)
+        assert record
+        res = {}
+        for icops in record.icops_ids:
+            key = '%s_%s' % (icops.backend_id.id, icops.concept)
+            res[key] = {'id': icops.record_id}
+        return res
+
+    def bind(self, records, binding_id):
+        """ Create the link between an external ID and an OpenERP ID and
+        update the last synchronization date.
+
+        :param external_ids: External ID to bind
+        :param binding_id: OpenERP ID to bind
+        :type binding_id: int
+        """
+        # avoid to trigger the export when we modify the `icops_id`
+        if not records:
+            return
+        context = self.session.context.copy()
+        context['icops'] = True
+        now_fmt = datetime.now().strftime(DEFAULT_SERVER_DATETIME_FORMAT)
+        icops_ids = []
+        for record, record_id in records.items():
+            backend_id, concept = record.split('_')
+
+            icops_id = (0, 0, {'record_id': record_id['id'],
+                        'backend_id': backend_id,
+                        'concept': concept,
+                        'binding_id': binding_id,
+                        'model': record_id['model']})
+            icops_ids.append(icops_id)
+
+        self.environment.model.write(
+            self.session.cr,
+            self.session.uid,
+            binding_id,
+            {'icops_ids': icops_ids, 'sync_date': now_fmt},
+            context=context)

=== added file 'base_intercompany/unit/export_synchronizer.py'
--- base_intercompany/unit/export_synchronizer.py	1970-01-01 00:00:00 +0000
+++ base_intercompany/unit/export_synchronizer.py	2014-01-28 03:02:10 +0000
@@ -0,0 +1,244 @@
+# -*- coding: utf-8 -*-
+##############################################################################
+#
+#    OpenERP, Open Source Management Solution
+#    Copyright (c) 2010-2014 Elico Corp. All Rights Reserved.
+#    Augustin Cisterne-Kaas <augustin.cisterne-kaas@xxxxxxxxxxxxxx>
+#    Eric Caudal <eric.caudal@xxxxxxxxxxxxxx>
+
+#    This program is free software: you can redistribute it and/or modify
+#    it under the terms of the GNU Affero General Public License as
+#    published by the Free Software Foundation, either version 3 of the
+#    License, or (at your option) any later version.
+#
+#    This program is distributed in the hope that it will be useful,
+#    but WITHOUT ANY WARRANTY; without even the implied warranty of
+#    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+#    GNU Affero General Public License for more details.
+#
+#    You should have received a copy of the GNU Affero General Public License
+#    along with this program.  If not, see <http://www.gnu.org/licenses/>.
+#
+##############################################################################
+
+import logging
+from openerp.tools.translate import _
+from openerp.addons.connector.queue.job import job
+from openerp.addons.connector.unit.synchronizer import ExportSynchronizer
+from ..connector import get_environment
+from openerp.addons.connector.exception import MappingError
+from osv import osv
+
+_logger = logging.getLogger(__name__)
+
+
+"""
+
+Exporters for ICOPS.
+
+In addition to its export job, an exporter has to:
+
+* check in ICOPS if the record has been updated more recently than the
+  last sync date and if yes, delay an import
+* call the ``bind`` method of the binder to update the last sync date
+
+"""
+
+
+class ICOPSBaseExporter(ExportSynchronizer):
+
+    """ Base exporter for ICOPS """
+
+    def __init__(self, environment):
+        """
+        :param environment: current environment (backend, session, ...)
+        :type environment: :py:class:`connector.connector.Environment`
+        """
+        super(ICOPSBaseExporter, self).__init__(environment)
+        self.binding_id = None
+        self.icops_ids = {}
+
+    def _get_openerp_data(self):
+        """ Return the raw OpenERP data for ``self.binding_id`` """
+        return self.session.browse(self.model._name, self.binding_id)
+
+    def run(self, binding_id, *args, **kwargs):
+        """ Run the synchronization
+
+        :param binding_id: identifier of the binding record to export
+        """
+        self.binding_id = binding_id
+        self.binding_record = self._get_openerp_data()
+
+        self.icops_ids = self.binder.to_backend(self.binding_id)
+        result = self._run(*args, **kwargs)
+        self.binder.bind(self.icops_ids, self.binding_id)
+        return result
+
+    def _run(self):
+        """ Flow of the synchronization, implemented in inherited classes"""
+        raise NotImplementedError
+
+
+class ICOPSExporter(ICOPSBaseExporter):
+
+    _concepts = None
+    """ A common flow for the exports to ICOPS """
+    def __init__(self, environment):
+        """
+        :param environment: current environment (backend, session, ...)
+        :type environment: :py:class:`connector.connector.Environment`
+        """
+        super(ICOPSExporter, self).__init__(environment)
+        self.binding_record = None
+
+    def _has_to_skip(self):
+        """ Return True if the export can be skipped """
+        return False
+
+    def _routing(self, record, fields=None):
+            fields = fields or []
+            icops = self.mapper._icops
+            icops_id = self._get_icops_id(
+                icops.backend_to.id, icops.concept)
+            if not icops_id:
+                return
+            if 'icops_delete' in fields:
+                self._delete(icops_id)
+            else:
+                self._custom_routing(icops_id, record, fields)
+
+    def _custom_routing(self, id, record, fields=None):
+        self._write(id, record)
+
+    def _run(self, fields=None):
+        """ Flow of the synchronization, implemented in inherited classes"""
+        assert self.binding_id
+        assert self.binding_record
+
+        if not self.icops_ids:
+            fields = None  # should be created with all the fields
+
+        if self._has_to_skip():
+            return
+
+        nb_records = 0
+        icops_ids = {}
+        for icops in self._get_icops():
+            backend = self._get_backend_with_permission(icops)
+            self._set_icops(icops, backend)
+            try:
+                self._map_data(fields=fields)
+            except MappingError as e:
+                continue
+            if self.icops_ids:
+                record = self.mapper.data
+                if not record:
+                    continue
+                nb_records += 1
+                self._validate_data(record)
+                self._routing(record, fields)
+            else:
+                record = self.mapper.data_for_create
+                if not record:
+                    continue
+                nb_records += 1
+                self._validate_data(record)
+                key = '%s_%s' % (icops.backend_to.id, icops.concept)
+                icops_ids[key] = {
+                    'id': self._create(record),
+                    'model': self.backend_adapter._get_pool()._name
+                }
+
+        self.icops_ids = icops_ids
+
+        if nb_records == 0:
+            return _('Nothing to export.')
+        return _('Record exported.')
+
+    def _get_backend_with_permission(self, icops):
+        sess = self.session
+        backend_pool = sess.pool.get('icops.backend')
+        return backend_pool.browse(
+            sess.cr, icops.icops_uid.id, icops.backend_to.id)
+
+    def _get_icops(self):
+        res = []
+        sess = self.session
+        user_pool = sess.pool.get('res.users')
+        user = user_pool.browse(sess.cr, sess.uid, sess.uid)
+        backend_pool = sess.pool.get('icops.backend')
+        backend_ids = backend_pool.search(
+            sess.cr, sess.uid, [('company_id', '=', user.company_id.id)])
+        if not backend_ids:
+            return res
+        intercompany_pool = sess.pool.get('res.intercompany')
+        intercompany_ids = intercompany_pool.search(
+            sess.cr, sess.uid,
+            [('backend_id', '=', backend_ids[0]),
+             ('concept', 'in', self._concepts),
+             ('model', '=', self.binding_record.openerp_id._name)])
+        res = intercompany_pool.browse(sess.cr, sess.uid, intercompany_ids)
+        return res
+
+    def _set_icops(self, icops, backend):
+        self.mapper._icops = icops
+        self.backend_adapter._icops = icops
+        self.mapper._backend_to = backend
+        self.backend_adapter._backend_to = backend
+
+    def _create(self, data):
+        if not self.backend_adapter._icops.on_create:
+            raise osv.except_osv('ICOPS Error', 'Can\'t create')
+        return self.backend_adapter.create(data)
+
+    def _write(self, id, data):
+        context = self.session.context or {}
+        if not self.backend_adapter._icops.on_write and not 'icops' in context:
+            raise osv.except_osv('ICOPS Error', 'Can\'t write')
+        self.backend_adapter.write(id, data)
+
+    def _confirm(self, id):
+        if not self.backend_adapter._icops.on_confirm:
+            raise osv.except_osv('ICOPS Error', 'Can\'t confirm')
+        self.backend_adapter.confirm(id)
+
+    def _cancel(self, id):
+        if not self.backend_adapter._icops.on_cancel:
+            raise osv.except_osv('ICOPS Error', 'Can\'t cancel')
+        self.backend_adapter.cancel(id)
+
+    def _delete(self, id):
+        if not self.backend_adapter._icops.on_unlink:
+            raise osv.except_osv('ICOPS Error', 'Can\'t delete')
+        self.backend_adapter.delete(id)
+
+    def _map_data(self, fields=None):
+        """ Convert the external record to OpenERP """
+        self.mapper.convert(self.binding_record, fields=fields)
+
+    def _validate_data(self, data):
+        """ Check if the values to export are correct
+
+        Pro-actively check before the ``Model.create`` or
+        ``Model.update`` if some fields are missing
+
+        Raise `InvalidDataError`
+        """
+        return
+
+    def _get_icops_id(self, backend_id, concept):
+        key = '%s_%s' % (backend_id, concept)
+        try:
+            return self.icops_ids[key]['id']
+        except:
+            return None
+
+
+@job
+def export_record(session, model_name, binding_id, fields=None):
+    """ Export a record on ICOPS """
+    record = session.browse(model_name, binding_id)
+    env = get_environment(session, model_name, record.backend_id.id)
+    exporter = env.get_connector_unit(ICOPSExporter)
+    return exporter.run(binding_id, fields=fields)

=== added file 'base_intercompany/unit/mapper.py'
--- base_intercompany/unit/mapper.py	1970-01-01 00:00:00 +0000
+++ base_intercompany/unit/mapper.py	2014-01-28 03:02:10 +0000
@@ -0,0 +1,52 @@
+# -*- coding: utf-8 -*-
+##############################################################################
+#
+#    OpenERP, Open Source Management Solution
+#    Copyright (c) 2010-2014 Elico Corp. All Rights Reserved.
+#    Augustin Cisterne-Kaas <augustin.cisterne-kaas@xxxxxxxxxxxxxx>
+#    Eric Caudal <eric.caudal@xxxxxxxxxxxxxx>
+
+#    This program is free software: you can redistribute it and/or modify
+#    it under the terms of the GNU Affero General Public License as
+#    published by the Free Software Foundation, either version 3 of the
+#    License, or (at your option) any later version.
+#
+#    This program is distributed in the hope that it will be useful,
+#    but WITHOUT ANY WARRANTY; without even the implied warranty of
+#    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+#    GNU Affero General Public License for more details.
+#
+#    You should have received a copy of the GNU Affero General Public License
+#    along with this program.  If not, see <http://www.gnu.org/licenses/>.
+#
+##############################################################################
+from openerp.addons.connector.unit.mapper import ExportMapper
+
+
+class ICOPSExportMapper(ExportMapper):
+    def __init__(self, environment):
+        """
+
+        :param environment: current environment (backend, session, ...)
+        :type environment: :py:class:`connector.connector.Environment`
+        """
+        super(ICOPSExportMapper, self).__init__(environment)
+        self._icops = None
+        self._backend_to = None
+
+    def _format_child_rows(self, child_records):
+        return [(5, 0)] + [(0, 0, data) for data in child_records]
+
+    def _init_child_mapper(self, model_name):
+        mapper = super(ICOPSExportMapper, self)._init_child_mapper(model_name)
+        mapper._icops = self._icops
+        mapper._backend_to = self._backend_to
+        return mapper
+
+    def _get_mapping(self, name, record):
+        res = {}
+        for method in dir(self):
+            if method.startswith('%s_' % name):
+                new_dict = getattr(self, method)(record)
+                res = dict(res.items() + new_dict.items())
+        return res

=== added directory 'base_intercompany_sale'
=== added file 'base_intercompany_sale/__init__.py'
--- base_intercompany_sale/__init__.py	1970-01-01 00:00:00 +0000
+++ base_intercompany_sale/__init__.py	2014-01-28 03:02:10 +0000
@@ -0,0 +1,28 @@
+# -*- coding: utf-8 -*-
+##############################################################################
+#
+#    OpenERP, Open Source Management Solution
+#    Copyright (c) 2010-2014 Elico Corp. All Rights Reserved.
+#    Augustin Cisterne-Kaas <augustin.cisterne-kaas@xxxxxxxxxxxxxx>
+#    Eric Caudal <eric.caudal@xxxxxxxxxxxxxx>
+
+#    This program is free software: you can redistribute it and/or modify
+#    it under the terms of the GNU Affero General Public License as
+#    published by the Free Software Foundation, either version 3 of the
+#    License, or (at your option) any later version.
+#
+#    This program is distributed in the hope that it will be useful,
+#    but WITHOUT ANY WARRANTY; without even the implied warranty of
+#    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+#    GNU Affero General Public License for more details.
+#
+#    You should have received a copy of the GNU Affero General Public License
+#    along with this program.  If not, see <http://www.gnu.org/licenses/>.
+#
+##############################################################################
+import connector
+import consumer
+import company
+import icops_model
+import sale
+import purchase

=== added file 'base_intercompany_sale/__openerp__.py'
--- base_intercompany_sale/__openerp__.py	1970-01-01 00:00:00 +0000
+++ base_intercompany_sale/__openerp__.py	2014-01-28 03:02:10 +0000
@@ -0,0 +1,50 @@
+# -*- coding: utf-8 -*-
+##############################################################################
+#
+#    OpenERP, Open Source Management Solution
+#    Copyright (c) 2010-2014 Elico Corp. All Rights Reserved.
+#    Augustin Cisterne-Kaas <augustin.cisterne-kaas@xxxxxxxxxxxxxx>
+#    Eric Caudal <eric.caudal@xxxxxxxxxxxxxx>
+
+#    This program is free software: you can redistribute it and/or modify
+#    it under the terms of the GNU Affero General Public License as
+#    published by the Free Software Foundation, either version 3 of the
+#    License, or (at your option) any later version.
+#
+#    This program is distributed in the hope that it will be useful,
+#    but WITHOUT ANY WARRANTY; without even the implied warranty of
+#    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+#    GNU Affero General Public License for more details.
+#
+#    You should have received a copy of the GNU Affero General Public License
+#    along with this program.  If not, see <http://www.gnu.org/licenses/>.
+#
+##############################################################################
+
+{'name': 'Base Intercompany Sale',
+ 'version': '0.3',
+ 'category': 'Sales Management',
+ 'depends': ['base_intercompany', 'sale', 'purchase'],
+ 'author': 'Elico Corp',
+ 'license': 'AGPL-3',
+ 'website': 'https://www.elico-corp.com',
+ 'description': """
+This module is an extension designed to manage Inter-company Process (ICOPS)
+and allows 2 companies to create Sale Orders and Purchase Order in each other.
+     - Sale Order to Purchase Order (so2po)
+     - Sale Order to Sale Order (so2so)
+     - Purchase Order to Sale Order (po2so)
+     - Handles the following events: Create, Update, Delete, Confirm and Cancel
+
+TODO: demo data to be improved.\n
+Blueprint: https://blueprints.launchpad.net/multi-company/+spec/icops
+""",
+ 'images': [],
+ 'demo': ['base_intercompany_sale_demo.xml'],
+ 'data': ['security/ir.model.access.csv',
+          'icops_model_view.xml',
+          'sale_view.xml',
+          'purchase_view.xml'],
+ 'installable': True,
+ 'application': False,
+ }

=== added file 'base_intercompany_sale/base_intercompany_sale_demo.xml'
--- base_intercompany_sale/base_intercompany_sale_demo.xml	1970-01-01 00:00:00 +0000
+++ base_intercompany_sale/base_intercompany_sale_demo.xml	2014-01-28 03:02:10 +0000
@@ -0,0 +1,53 @@
+<?xml version="1.0" encoding="utf-8"?>
+<openerp>
+    <data noupdate="1">
+
+        <record id="base_intercompany.user_origin" model="res.users">
+            <field name="groups_id" eval="[(4, ref('base.group_sale_manager')), (4, ref('purchase.group_purchase_manager')), (4, ref('stock.group_stock_manager')), (4, ref('stock.group_locations'))]"/>
+        </record>
+
+        <record id="base_intercompany.user_destination" model="res.users">
+            <field name="groups_id" eval="[(4, ref('base.group_sale_manager')), (4, ref('purchase.group_purchase_manager')), (4, ref('stock.group_stock_manager')), (4, ref('stock.group_locations'))]"/>
+        </record>
+
+        <record id="warehouse_origin" model="stock.warehouse">
+            <field name="name">Warehouse Origin</field>
+            <field name="company_id" ref="base_intercompany.company_origin" />
+        </record>
+
+        <record id="warehouse_destination" model="stock.warehouse">
+            <field name="name">Warehouse Destination</field>
+            <field name="company_id" ref="base_intercompany.company_destination" />
+        </record>
+
+        <record id="shop_origin" model="sale.shop">
+            <field name="name">Shop Origin</field>
+            <field name="warehouse_id" ref="warehouse_origin" />
+            <field name="payment_default_id" ref="account.account_payment_term_immediate" />
+            <field name="company_id" ref="base_intercompany.company_origin" />
+        </record>
+
+        <record id="shop_destination" model="sale.shop">
+            <field name="name">Shop Destination</field>
+            <field name="warehouse_id" ref="warehouse_destination" />
+            <field name="payment_default_id" ref="account.account_payment_term_immediate" />
+            <field name="company_id" ref="base_intercompany.company_destination" />
+        </record>
+
+        <!-- ICOPS Setup -->
+        <record id="base_intercompany.backend_origin" model="icops.backend">
+            <field name="icops_shop_id" ref="shop_origin" />
+            <field name="icops_ids" eval="[(0, 0, {'concept': 'so2po', 'backend_to': ref('base_intercompany.backend_destination'), 'on_create': True, 'on_write': True, 'on_unlink': True, 'on_confirm': True, 'on_cancel': True})]" />
+        </record>
+
+        <record id="base_intercompany.backend_destination" model="icops.backend">
+            <field name="icops_shop_id" ref="shop_destination" />
+        </record>
+
+        <!-- Remove company_id from stock -->
+        <function model="stock.location" name="write">
+             <function eval="[[]]" model="stock.location" name="search"/>
+             <value eval="{'company_id': None}" />
+        </function>
+    </data>
+</openerp>
\ No newline at end of file

=== added file 'base_intercompany_sale/company.py'
--- base_intercompany_sale/company.py	1970-01-01 00:00:00 +0000
+++ base_intercompany_sale/company.py	2014-01-28 03:02:10 +0000
@@ -0,0 +1,56 @@
+    # -*- coding: utf-8 -*-
+##############################################################################
+#
+#    OpenERP, Open Source Management Solution
+#    Copyright (c) 2010-2014 Elico Corp. All Rights Reserved.
+#    Augustin Cisterne-Kaas <augustin.cisterne-kaas@xxxxxxxxxxxxxx>
+#    Eric Caudal <eric.caudal@xxxxxxxxxxxxxx>
+
+#    This program is free software: you can redistribute it and/or modify
+#    it under the terms of the GNU Affero General Public License as
+#    published by the Free Software Foundation, either version 3 of the
+#    License, or (at your option) any later version.
+#
+#    This program is distributed in the hope that it will be useful,
+#    but WITHOUT ANY WARRANTY; without even the implied warranty of
+#    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+#    GNU Affero General Public License for more details.
+#
+#    You should have received a copy of the GNU Affero General Public License
+#    along with this program.  If not, see <http://www.gnu.org/licenses/>.
+#
+##############################################################################
+from openerp.osv import fields, orm
+
+
+class res_intercompany(orm.Model):
+    _inherit = 'res.intercompany'
+
+    def _select_concepts(self, cr, uid, context=None):
+        """ Available concepts
+
+        Can be inherited to add custom versions.
+        """
+        res = super(res_intercompany, self)._select_concepts(cr, uid, context)
+        res += [('so2po', 'SO to PO'),
+                ('so2so', 'SO to SO'),
+                ('po2so', 'PO to SO')]
+        return res
+
+    def _select_models(self, cr, uid, context=None):
+        """ Available Object names
+
+        Can be inherited to add custom versions.
+        """
+        res = super(res_intercompany, self)._select_models(
+            cr, uid, context)
+        new_dict = {'so2po': 'sale.order',
+                    'so2so': 'sale.order',
+                    'po2so': 'purchase.order'}
+        res = dict(res.items() + new_dict.items())
+        return res
+
+    _columns = {
+        'concept': fields.selection(_select_concepts, string="Concept",
+                                    required=True),
+    }

=== added file 'base_intercompany_sale/connector.py'
--- base_intercompany_sale/connector.py	1970-01-01 00:00:00 +0000
+++ base_intercompany_sale/connector.py	2014-01-28 03:02:10 +0000
@@ -0,0 +1,32 @@
+# -*- coding: utf-8 -*-
+##############################################################################
+#
+#    OpenERP, Open Source Management Solution
+#    Copyright (c) 2010-2014 Elico Corp. All Rights Reserved.
+#    Augustin Cisterne-Kaas <augustin.cisterne-kaas@xxxxxxxxxxxxxx>
+#    Eric Caudal <eric.caudal@xxxxxxxxxxxxxx>
+
+#    This program is free software: you can redistribute it and/or modify
+#    it under the terms of the GNU Affero General Public License as
+#    published by the Free Software Foundation, either version 3 of the
+#    License, or (at your option) any later version.
+#
+#    This program is distributed in the hope that it will be useful,
+#    but WITHOUT ANY WARRANTY; without even the implied warranty of
+#    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+#    GNU Affero General Public License for more details.
+#
+#    You should have received a copy of the GNU Affero General Public License
+#    along with this program.  If not, see <http://www.gnu.org/licenses/>.
+#
+##############################################################################
+from openerp.osv import orm
+
+
+class base_intercompany_sale_installed(orm.AbstractModel):
+    """Empty model used to know if the module is installed on the
+    database.
+
+    If the model is in the registry, the module is installed.
+    """
+    _name = 'base_intercompany_sale.installed'

=== added file 'base_intercompany_sale/consumer.py'
--- base_intercompany_sale/consumer.py	1970-01-01 00:00:00 +0000
+++ base_intercompany_sale/consumer.py	2014-01-28 03:02:10 +0000
@@ -0,0 +1,79 @@
+# -*- coding: utf-8 -*-
+##############################################################################
+#
+#    OpenERP, Open Source Management Solution
+#    Copyright (c) 2010-2014 Elico Corp. All Rights Reserved.
+#    Augustin Cisterne-Kaas <augustin.cisterne-kaas@xxxxxxxxxxxxxx>
+#    Eric Caudal <eric.caudal@xxxxxxxxxxxxxx>
+
+#    This program is free software: you can redistribute it and/or modify
+#    it under the terms of the GNU Affero General Public License as
+#    published by the Free Software Foundation, either version 3 of the
+#    License, or (at your option) any later version.
+#
+#    This program is distributed in the hope that it will be useful,
+#    but WITHOUT ANY WARRANTY; without even the implied warranty of
+#    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+#    GNU Affero General Public License for more details.
+#
+#    You should have received a copy of the GNU Affero General Public License
+#    along with this program.  If not, see <http://www.gnu.org/licenses/>.
+#
+##############################################################################
+from openerp.addons.connector.event import (on_record_write,
+                                            on_record_create,
+                                            on_record_unlink
+                                            )
+from openerp.addons.base_intercompany.unit.export_synchronizer import (
+    export_record)
+
+_MODEL_NAMES = ('sale.order', 'sale.order.line',
+                'purchase.order', 'purchase.order.line')
+_BIND_MODEL_NAMES = ('icops.sale.order', 'icops.sale.order.line',
+                     'icops.purchase.order', 'icops.purchase.order.line')
+_UNLINK_MODEL_NAMES = ('sale.order', 'purchase.order')
+_UNLINK_BIND_MODEL_NAMES = ('icops.sale.order', 'icops.purchase.order')
+
+
+@on_record_create(model_names=_BIND_MODEL_NAMES)
+@on_record_write(model_names=_BIND_MODEL_NAMES)
+def delay_export(session, model_name, record_id, fields=None):
+    """ Delay a job which export a binding record.
+
+    (A binding record being a ``icops.res.partner``,
+    ``icops.sale.order``, ...)
+    """
+    export_record(session, model_name, record_id, fields=fields)
+
+
+@on_record_write(model_names=_MODEL_NAMES)
+def delay_export_all_bindings(session, model_name, record_id, fields=None):
+    """ Delay a job which export all the bindings of a record.
+
+    In this case, it is called on records of normal models and will delay
+    the export for all the bindings.
+    """
+    model = session.pool.get(model_name)
+    record = model.browse(session.cr, session.uid,
+                          record_id, context=session.context)
+    for binding in record.icops_bind_ids:
+        export_record(session, binding._model._name, binding.id,
+                      fields=fields)
+
+
+@on_record_unlink(model_names=_UNLINK_MODEL_NAMES)
+def delay_unlink(session, model_name, record_id):
+    """ Delay a job which delete a record on Magento.
+
+    Called on binding records."""
+    fields = {'icops_delete': True}
+    delay_export_all_bindings(session, model_name, record_id, fields)
+
+
+@on_record_unlink(model_names=_UNLINK_BIND_MODEL_NAMES)
+def delay_unlink_binding(session, model_name, record_id):
+    """ Delay a job which delete a record on Magento.
+
+    Called on binding records."""
+    fields = {'icops_delete': True}
+    delay_export(session, model_name, record_id, fields)

=== added file 'base_intercompany_sale/icops_model.py'
--- base_intercompany_sale/icops_model.py	1970-01-01 00:00:00 +0000
+++ base_intercompany_sale/icops_model.py	2014-01-28 03:02:10 +0000
@@ -0,0 +1,33 @@
+# -*- coding: utf-8 -*-
+##############################################################################
+#
+#    OpenERP, Open Source Management Solution
+#    Copyright (c) 2010-2014 Elico Corp. All Rights Reserved.
+#    Augustin Cisterne-Kaas <augustin.cisterne-kaas@xxxxxxxxxxxxxx>
+#    Eric Caudal <eric.caudal@xxxxxxxxxxxxxx>
+
+#    This program is free software: you can redistribute it and/or modify
+#    it under the terms of the GNU Affero General Public License as
+#    published by the Free Software Foundation, either version 3 of the
+#    License, or (at your option) any later version.
+#
+#    This program is distributed in the hope that it will be useful,
+#    but WITHOUT ANY WARRANTY; without even the implied warranty of
+#    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+#    GNU Affero General Public License for more details.
+#
+#    You should have received a copy of the GNU Affero General Public License
+#    along with this program.  If not, see <http://www.gnu.org/licenses/>.
+#
+##############################################################################
+from openerp.osv import fields, orm
+
+
+class icops_backend(orm.Model):
+    _inherit = 'icops.backend'
+
+    _columns = {
+        'icops_shop_id': fields.many2one(
+            'sale.shop', 'IC Default location',
+            required=True),
+    }

=== added file 'base_intercompany_sale/icops_model_view.xml'
--- base_intercompany_sale/icops_model_view.xml	1970-01-01 00:00:00 +0000
+++ base_intercompany_sale/icops_model_view.xml	2014-01-28 03:02:10 +0000
@@ -0,0 +1,15 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<openerp>
+    <data>
+        <record id="view_icops_backend_form" model="ir.ui.view">
+            <field name="name">icops.backend.form</field>
+            <field name="model">icops.backend</field>
+            <field name="inherit_id" ref="base_intercompany.view_icops_backend_form" />
+            <field name="arch" type="xml">
+                <field name="icops_uid" position="after">
+                    <field name="icops_shop_id" cols="2" />
+                </field>
+            </field>
+        </record>
+    </data>
+</openerp>
\ No newline at end of file

=== added file 'base_intercompany_sale/purchase.py'
--- base_intercompany_sale/purchase.py	1970-01-01 00:00:00 +0000
+++ base_intercompany_sale/purchase.py	2014-01-28 03:02:10 +0000
@@ -0,0 +1,297 @@
+# -*- coding: utf-8 -*-
+##############################################################################
+#
+#    OpenERP, Open Source Management Solution
+#    Copyright (c) 2010-2014 Elico Corp. All Rights Reserved.
+#    Augustin Cisterne-Kaas <augustin.cisterne-kaas@xxxxxxxxxxxxxx>
+#    Eric Caudal <eric.caudal@xxxxxxxxxxxxxx>
+
+#    This program is free software: you can redistribute it and/or modify
+#    it under the terms of the GNU Affero General Public License as
+#    published by the Free Software Foundation, either version 3 of the
+#    License, or (at your option) any later version.
+#
+#    This program is distributed in the hope that it will be useful,
+#    but WITHOUT ANY WARRANTY; without even the implied warranty of
+#    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+#    GNU Affero General Public License for more details.
+#
+#    You should have received a copy of the GNU Affero General Public License
+#    along with this program.  If not, see <http://www.gnu.org/licenses/>.
+#
+##############################################################################
+from openerp.osv import fields, orm
+from openerp.addons.base_intercompany.backend import icops
+from openerp.addons.base_intercompany.unit.export_synchronizer import (
+    ICOPSExporter)
+from openerp.addons.base_intercompany.unit.mapper import ICOPSExportMapper
+from openerp.addons.connector.unit.mapper import mapping
+from openerp.addons.base_intercompany.unit.backend_adapter import (
+    ICOPSAdapter)
+from openerp.addons.connector.exception import MappingError
+
+
+class purchase_order(orm.Model):
+    _name = 'purchase.order'
+    _inherit = ['purchase.order', 'icops.model']
+
+    _columns = {
+        'openerp_id': fields.many2one('purchase.order',
+                                      string='Sale Order',
+                                      required=True,
+                                      ondelete='cascade'),
+        'icops_bind_ids': fields.one2many(
+            'icops.purchase.order', 'openerp_id',
+            string="ICOPS Bindings"),
+    }
+
+    def copy_data(self, cr, uid, id, default=None, context=None):
+        if default is None:
+            default = {}
+        default['icops_bind_ids'] = False
+        return super(purchase_order, self).copy_data(cr, uid, id,
+                                                     default=default,
+                                                     context=context)
+
+    def create(self, cr, uid, data, context=None):
+        data['icops_bind_ids'] = self.pool.get(
+            'icops.backend').prepare_binding(cr, uid, data, context)
+        return super(purchase_order, self).create(cr, uid, data, context)
+
+    def write(self, cr, uid, ids, data, context=None):
+        self._check_icops(cr, uid, ids, context=context)
+        return super(purchase_order, self).write(cr, uid, ids, data, context)
+
+    def unlink(self, cr, uid, ids, context=None):
+        self._check_icops(cr, uid, ids, context=context)
+        return super(purchase_order, self).unlink(cr, uid, ids, context)
+
+
+class icops_purchase_order(orm.Model):
+    _name = 'icops.purchase.order'
+    _inherit = 'icops.binding'
+    _inherits = {'purchase.order': 'openerp_id'}
+    _description = 'ICOPS Purchase Order'
+
+    _columns = {
+        'openerp_id': fields.many2one('purchase.order',
+                                      string='Product',
+                                      required=True,
+                                      ondelete='cascade'),
+        'backend_id': fields.many2one('icops.backend',
+                                      string='ICOPS Backend'),
+        'icops_order_line_ids': fields.one2many('icops.purchase.order.line',
+                                                'icops_order_id',
+                                                'ICOPS Order Lines'),
+    }
+
+
+class purchase_order_line(orm.Model):
+    _inherit = 'purchase.order.line'
+    _columns = {
+        'icops_bind_ids': fields.one2many(
+            'icops.purchase.order.line', 'openerp_id',
+            string="ICOPS Bindings"),
+    }
+
+    def copy_data(self, cr, uid, id, default=None, context=None):
+        if default is None:
+            default = {}
+        default['icops_bind_ids'] = False
+        return super(purchase_order_line, self).copy_data(cr, uid, id,
+                                                          default=default,
+                                                          context=context)
+
+
+class icops_purchase_order_line(orm.Model):
+    _name = 'icops.purchase.order.line'
+    _inherit = 'icops.binding'
+    _description = 'ICOPS Sale Order Line'
+    _inherits = {'purchase.order.line': 'openerp_id'}
+
+    def _get_lines_from_order(self, cr, uid, ids, context=None):
+        line_obj = self.pool.get('icops.purchase.order.line')
+        return line_obj.search(cr, uid,
+                               [('icops_order_id', 'in', ids)],
+                               context=context)
+    _columns = {
+        'icops_order_id': fields.many2one('icops.purchase.order',
+                                          'ICOPS Sale Order',
+                                          required=True,
+                                          ondelete='cascade',
+                                          select=True),
+        'openerp_id': fields.many2one('purchase.order.line',
+                                      string='Sale Order Line',
+                                      required=True,
+                                      ondelete='cascade'),
+        'backend_id': fields.related(
+            'icops_order_id', 'backend_id',
+            type='many2one',
+            relation='icops.backend',
+            string='ICOPS Backend',
+            store={'icops.purchase.order.line':
+                   (lambda self, cr, uid, ids, c=None: ids,
+                    ['icops_order_id'],
+                    10),
+                 'icops.purchase.order':
+                   (_get_lines_from_order, ['backend_id'], 20),
+                   },
+            readonly=True)
+    }
+
+    def create(self, cr, uid, vals, context=None):
+        icops_order_id = vals['icops_order_id']
+        info = self.pool['icops.purchase.order'].read(cr, uid,
+                        [icops_order_id],
+            ['openerp_id'],
+            context=context)
+        order_id = info[0]['openerp_id']
+        vals['order_id'] = order_id[0]
+        return super(icops_purchase_order_line, self).create(cr, uid, vals,
+                                                             context=context)
+
+
+@icops
+class PurchaseOrderAdapter(ICOPSAdapter):
+    _model_name = 'icops.purchase.order'
+
+    def _get_pool(self):
+        sess = self.session
+        return sess.pool.get('sale.order')
+
+    def confirm(self, id):
+        sess = self.session
+        pool = self._get_pool()
+        context = {'icops': True}
+        pool.write(
+            sess.cr, self._backend_to.icops_uid.id, [id],
+            {'temp_unlock': True}, context=context)
+        pool.action_wait(
+            sess.cr, self._backend_to.icops_uid.id, [id])
+        pool.write(
+            sess.cr, self._backend_to.icops_uid.id, [id],
+            {'temp_unlock': False}, context=context)
+
+    def cancel(self, id):
+        sess = self.session
+        pool = self._get_pool()
+        context = {'icops': True}
+        pool.write(
+            sess.cr, self._backend_to.icops_uid.id, [id],
+            {'temp_unlock': True}, context=context)
+        pool.action_cancel(sess.cr, self._backend_to.icops_uid.id, [id])
+        pool.write(
+            sess.cr, self._backend_to.icops_uid.id, [id],
+            {'temp_unlock': False}, context=context)
+
+
+@icops
+class PurchaseOrderExport(ICOPSExporter):
+    _model_name = ['icops.purchase.order']
+    _concepts = ['po2so']
+
+    def _custom_routing(self, id, record, fields=None):
+        if 'state' in fields:
+            state = record.pop('state')
+            if state == 'cancel':
+                self._cancel(id)
+            elif state == 'progress':
+                self._confirm(id)
+        elif fields:
+            self._write(id, record)
+
+
+@icops
+class PurchaseOrderExportMapper(ICOPSExportMapper):
+    _model_name = 'icops.purchase.order'
+
+    children = [
+        ('order_line', 'order_line', 'icops.purchase.order.line')
+    ]
+
+    @mapping
+    def origin(self, record):
+        return {'origin': 'ICOPS: %s' % record.name}
+
+    @mapping
+    def state(self, record):
+        state = record.state
+        if record.state == 'approved':
+            state = 'progress'
+        return {'state': state}
+
+    @mapping
+    def address(self, record):
+        sess = self.session
+        partner = record.company_id.partner_id
+        partner_pool = sess.pool.get('res.partner')
+        addr = partner_pool.address_get(sess.cr, sess.uid, [partner.id],
+                                        ['delivery', 'invoice', 'contact'])
+        return {
+            'partner_invoice_id': addr['invoice'],
+            'partner_shipping_id': addr['delivery']
+        }
+
+    @mapping
+    def icops(self, record):
+        if not self._backend_to:
+            raise MappingError("Could not find an ICOPS backend")
+        sess = self.session
+        backend = self._backend_to
+        ic_uid = backend.icops_uid.id
+        company = backend.company_id
+        partner_pool = sess.pool.get('res.partner')
+        partner_id = record.company_id.partner_id.id
+        partner = partner_pool.browse(sess.cr, ic_uid, partner_id)
+        pricelist = (partner.property_product_pricelist.id
+                     if partner.property_product_pricelist
+                     else False)
+        fiscal_position = partner.property_account_position
+        payment_term = partner.property_payment_term
+        shop = self._backend_to.icops_shop_id
+        if company.partner_id.id != record.partner_id.id:
+            raise MappingError("Wrong partner")
+        return {
+            'company_id': company.id,
+            'partner_id': partner.id,
+            'pricelist_id': pricelist,
+            'fiscal_position': fiscal_position.id,
+            'payment_term': payment_term.id,
+            'user_id': ic_uid,
+            'shop_id': shop.id
+        }
+
+
+@icops
+class PurchaseOrderLineExportMapper(ICOPSExportMapper):
+    _model_name = 'icops.purchase.order.line'
+
+    direct = [('name', 'name')]
+
+    @mapping
+    def product(self, record):
+        return {
+            'product_id': record.product_id.id,
+            'product_uom': record.product_uom.id,
+            'product_uom_qty': record.product_qty
+        }
+
+    @mapping
+    def price(self, record):
+        if not record.product_id:
+            return {'price_unit': 0}
+        sess = self.session
+        backend = self._backend_to
+        ic_uid = backend.icops_uid.id
+        partner_pool = sess.pool.get('res.partner')
+        partner_id = record.order_id.company_id.partner_id.id
+        partner = partner_pool.browse(sess.cr, ic_uid, partner_id)
+        pricelist_id = (partner.property_product_pricelist.id
+                        if partner.property_product_pricelist
+                        else False)
+        pricelist_pool = sess.pool.get('product.pricelist')
+        price_unit = pricelist_pool.price_get(
+            sess.cr, ic_uid, [pricelist_id],
+            record.product_id.id, record.product_qty)
+        price = price_unit[int(pricelist_id)]
+        return {'price_unit': price}

=== added file 'base_intercompany_sale/purchase_view.xml'
--- base_intercompany_sale/purchase_view.xml	1970-01-01 00:00:00 +0000
+++ base_intercompany_sale/purchase_view.xml	2014-01-28 03:02:10 +0000
@@ -0,0 +1,26 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<openerp>
+    <data>
+        <record id="purchase_order_form" model="ir.ui.view">
+            <field name="name">purchase.order.form</field>
+            <field name="model">purchase.order</field>
+            <field name="inherit_id" ref="purchase.purchase_order_form" />
+            <field name="arch" type="xml">
+                <xpath expr="//header/button[last()]" position="after">
+                    <button name="action_unlock" string="Unlock" type="object" attrs="{'invisible':[('locked', '=', False)]}" class="oe_highlight" />
+                </xpath>
+                <xpath expr="//page[last()]" position="after">
+                    <page string="ICOPS">
+                        <field name="locked" invisible="1" />
+                        <field name="icops_bind_ids">
+                            <tree string="ICOPS Backend" editable="bottom">
+                                <field name="backend_id" />
+                                <field name="icops_ids" />
+                            </tree>
+                        </field>
+                    </page>
+                </xpath>
+            </field>
+        </record>
+    </data>
+</openerp>
\ No newline at end of file

=== added file 'base_intercompany_sale/sale.py'
--- base_intercompany_sale/sale.py	1970-01-01 00:00:00 +0000
+++ base_intercompany_sale/sale.py	2014-01-28 03:02:10 +0000
@@ -0,0 +1,388 @@
+# -*- coding: utf-8 -*-
+##############################################################################
+#
+#    OpenERP, Open Source Management Solution
+#    Copyright (c) 2010-2014 Elico Corp. All Rights Reserved.
+#    Augustin Cisterne-Kaas <augustin.cisterne-kaas@xxxxxxxxxxxxxx>
+#    Eric Caudal <eric.caudal@xxxxxxxxxxxxxx>
+
+#    This program is free software: you can redistribute it and/or modify
+#    it under the terms of the GNU Affero General Public License as
+#    published by the Free Software Foundation, either version 3 of the
+#    License, or (at your option) any later version.
+#
+#    This program is distributed in the hope that it will be useful,
+#    but WITHOUT ANY WARRANTY; without even the implied warranty of
+#    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+#    GNU Affero General Public License for more details.
+#
+#    You should have received a copy of the GNU Affero General Public License
+#    along with this program.  If not, see <http://www.gnu.org/licenses/>.
+#
+##############################################################################
+from openerp.osv import fields, orm
+from openerp.addons.base_intercompany.backend import icops
+from openerp.addons.base_intercompany.unit.export_synchronizer import (
+    ICOPSExporter)
+from openerp.addons.base_intercompany.unit.mapper import ICOPSExportMapper
+from openerp.addons.connector.unit.mapper import mapping
+from openerp.addons.connector.exception import MappingError
+from openerp.addons.base_intercompany.unit.backend_adapter import ICOPSAdapter
+
+
+class sale_order(orm.Model):
+    _name = 'sale.order'
+    _inherit = ['sale.order', 'icops.model']
+
+    _columns = {
+        'openerp_id': fields.many2one('sale.order',
+                                      string='Sale Order',
+                                      required=True,
+                                      ondelete='cascade'),
+        'icops_bind_ids': fields.one2many(
+            'icops.sale.order', 'openerp_id',
+            string="ICOPS Bindings"),
+    }
+
+    def copy_data(self, cr, uid, id, default=None, context=None):
+        if default is None:
+            default = {}
+        default['icops_bind_ids'] = False
+        return super(sale_order, self).copy_data(cr, uid, id,
+                                                 default=default,
+                                                 context=context)
+
+    def create(self, cr, uid, data, context=None):
+        data['icops_bind_ids'] = self.pool.get(
+            'icops.backend').prepare_binding(cr, uid, data, context)
+        return super(sale_order, self).create(cr, uid, data, context)
+
+    def write(self, cr, uid, ids, data, context=None):
+        self._check_icops(cr, uid, ids, context=context)
+        return super(sale_order, self).write(cr, uid, ids, data, context)
+
+    def unlink(self, cr, uid, ids, context=None):
+        self._check_icops(cr, uid, ids, context=context)
+        return super(sale_order, self).unlink(cr, uid, ids, context)
+
+
+class icops_sale_order(orm.Model):
+    _name = 'icops.sale.order'
+    _inherit = 'icops.binding'
+    _inherits = {'sale.order': 'openerp_id'}
+    _description = 'ICOPS Sale Order'
+
+    _columns = {
+        'openerp_id': fields.many2one('sale.order',
+                                      string='SaleOrder',
+                                      required=True,
+                                      ondelete='cascade'),
+        'backend_id': fields.many2one('icops.backend',
+                                      string='ICOPS Backend'),
+        'icops_order_line_ids': fields.one2many('icops.sale.order.line',
+                                                'icops_order_id',
+                                                'ICOPS Order Lines'),
+
+    }
+
+
+class sale_order_line(orm.Model):
+    _inherit = 'sale.order.line'
+    _columns = {
+        'icops_bind_ids': fields.one2many(
+            'icops.sale.order.line', 'openerp_id',
+            string="ICOPS Bindings"),
+    }
+
+    def copy_data(self, cr, uid, id, default=None, context=None):
+        if default is None:
+            default = {}
+        default['icops_bind_ids'] = False
+        return super(sale_order_line, self).copy_data(cr, uid, id,
+                                                      default=default,
+                                                      context=context)
+
+
+class icops_sale_order_line(orm.Model):
+    _name = 'icops.sale.order.line'
+    _inherit = 'icops.binding'
+    _description = 'ICOPS Sale Order Line'
+    _inherits = {'sale.order.line': 'openerp_id'}
+
+    def _get_lines_from_order(self, cr, uid, ids, context=None):
+        line_obj = self.pool.get('icops.sale.order.line')
+        return line_obj.search(cr, uid,
+                               [('icops_order_id', 'in', ids)],
+                               context=context)
+    _columns = {
+        'icops_order_id': fields.many2one('icops.sale.order',
+                                          'ICOPS Sale Order',
+                                          required=True,
+                                          ondelete='cascade',
+                                          select=True),
+        'openerp_id': fields.many2one('sale.order.line',
+                                      string='Sale Order Line',
+                                      required=True,
+                                      ondelete='cascade'),
+        'backend_id': fields.related(
+            'icops_order_id', 'backend_id',
+            type='many2one',
+            relation='icops.backend',
+            string='ICOPS Backend',
+            store={'icops.sale.order.line':
+                   (lambda self, cr, uid, ids, c=None: ids,
+                    ['icops_order_id'],
+                    10),
+                 'icops.sale.order':
+                   (_get_lines_from_order, ['backend_id'], 20),
+                   },
+            readonly=True)
+    }
+
+    def create(self, cr, uid, vals, context=None):
+        icops_order_id = vals['icops_order_id']
+        info = self.pool['icops.sale.order'].read(cr, uid,
+                        [icops_order_id],
+            ['openerp_id'],
+            context=context)
+        order_id = info[0]['openerp_id']
+        vals['order_id'] = order_id[0]
+        return super(icops_sale_order_line, self).create(cr, uid, vals,
+                                                         context=context)
+
+
+@icops
+class SaleOrderAdapter(ICOPSAdapter):
+    _model_name = 'icops.sale.order'
+
+    def _get_pool(self):
+        sess = self.session
+        name = ('purchase.order'
+                if self._icops.concept == 'so2po' else 'sale.order')
+        return sess.pool.get(name)
+
+    def confirm(self, id):
+        sess = self.session
+        pool = self._get_pool()
+        context = {'icops': True}
+        pool.write(
+            sess.cr, self._backend_to.icops_uid.id, [id],
+            {'temp_unlock': True}, context)
+        if hasattr(pool, 'wkf_confirm_order'):
+            pool.wkf_confirm_order(
+                sess.cr, self._backend_to.icops_uid.id, [id])
+        else:
+            pool.action_wait(
+                sess.cr, self._backend_to.icops_uid.id, [id])
+        pool.write(
+            sess.cr, self._backend_to.icops_uid.id, [id],
+            {'temp_unlock': False}, context)
+
+    def cancel(self, id):
+        sess = self.session
+        pool = self._get_pool()
+        context = {'icops': True}
+        pool.write(
+            sess.cr, self._backend_to.icops_uid.id, [id],
+            {'temp_unlock': True}, context)
+        pool.action_cancel(sess.cr, self._backend_to.icops_uid.id, [id])
+        pool.write(
+            sess.cr, self._backend_to.icops_uid.id, [id],
+            {'temp_unlock': False}, context)
+
+
+@icops
+class SaleOrderExport(ICOPSExporter):
+    _model_name = ['icops.sale.order']
+    _concepts = ['so2po', 'so2so']
+
+    def _custom_routing(self, id, record, fields=None):
+        if 'state' in fields:
+            state = record.pop('state')
+            if state == 'cancel':
+                self._cancel(id)
+            elif state in ('approved', 'progress', 'manual'):
+                self._confirm(id)
+        elif fields:
+            self._write(id, record)
+
+
+@icops
+class SaleOrderExportMapper(ICOPSExportMapper):
+    _model_name = 'icops.sale.order'
+
+    children = [
+        ('order_line', 'order_line', 'icops.sale.order.line')
+    ]
+
+    def _partner(self, record, is_po=False):
+        sess = self.session
+        backend = self._backend_to
+        ic_uid = backend.icops_uid.id
+        partner_pool = sess.pool.get('res.partner')
+        partner_id = record.company_id.partner_id.id
+        partner = partner_pool.browse(sess.cr, ic_uid, partner_id)
+        pricelist_id = None
+        payment_term_id = None
+        if is_po:
+            pricelist_id = (partner.property_product_pricelist_purchase.id
+                            if partner.property_product_pricelist_purchase
+                            else False)
+            payment_term_id = (partner.property_supplier_payment_term.id
+                               if partner.property_supplier_payment_term
+                               else False)
+        else:
+            pricelist_id = (partner.property_product_pricelist.id
+                            if partner.property_product_pricelist
+                            else False)
+            payment_term_id = (partner.property_payment_term.id
+                               if partner.property_payment_term
+                               else False)
+        fiscal_position_id = (partner.property_account_position.id
+                              if partner.property_account_position
+                              else False)
+        return {'partner_id': partner.id,
+                'pricelist_id': pricelist_id,
+                'payment_term_id': payment_term_id,
+                'fiscal_position': fiscal_position_id}
+
+    @mapping
+    def origin(self, record):
+        return {'origin': 'ICOPS: %s' % record.name}
+
+    @mapping
+    def map_all(self, record):
+        assert self._icops
+        return self._get_mapping(self._icops.concept, record)
+
+    def so2so_icops(self, record):
+        if not self._backend_to:
+            raise MappingError("Could not find an ICOPS backend")
+        backend = self._backend_to
+        icops_uid = backend.icops_uid.id
+        company = backend.company_id
+        partner = company.partner_id
+        pricelist = partner.property_product_pricelist
+        fiscal_position = partner.property_account_position
+        payment_term = partner.property_payment_term
+        shop = self._backend_to.icops_shop_id
+
+        return {
+            'company_id': company.id,
+            'partner_id': partner.id,
+            'pricelist_id': pricelist.id,
+            'fiscal_position': fiscal_position.id,
+            'payment_term': payment_term.id,
+            'user_id': icops_uid,
+            'shop_id': shop.id
+        }
+
+    def so2so_partner(self, record):
+        return self._partner(record)
+
+    def so2so_address(self, record):
+        sess = self.session
+        partner = record.company_id.partner_id
+        partner_pool = sess.pool.get('res.partner')
+        addr = partner_pool.address_get(sess.cr, sess.uid, [partner.id],
+                                        ['delivery', 'invoice', 'contact'])
+        return {
+            'partner_invoice_id': addr['invoice'],
+            'partner_shipping_id': addr['delivery']
+        }
+
+    def so2so_policy(self, record):
+        return {'order_policy': record.order_policy}
+
+    def so2so_state(self, record):
+        return {'state': record.state}
+
+    def so2po_partner(self, record):
+        return self._partner(record, True)
+
+    def so2po_icops(self, record):
+        if not self._backend_to:
+            raise MappingError("Could not find an ICOPS backend")
+        backend = self._backend_to
+        company = backend.company_id
+        shop = backend.icops_shop_id
+        warehouse = shop.warehouse_id
+        location = warehouse.lot_stock_id
+
+        if company.partner_id.id != record.partner_id.id:
+            raise MappingError("Wrong partner")
+        return {
+            'company_id': company.id,
+            'location_id': location.id
+        }
+
+    def so2po_state(self, record):
+        state = record.state
+        if record.state in ('progress', 'manual'):
+            state = 'approved'
+        return {'state': state}
+
+
+@icops
+class SaleOrderLineExportMapper(ICOPSExportMapper):
+    _model_name = 'icops.sale.order.line'
+
+    direct = [('name', 'name')]
+
+    def _price(self, record, is_po=False):
+        if not record.product_id:
+            return {'price_unit': 0}
+        sess = self.session
+        backend = self._backend_to
+        ic_uid = backend.icops_uid.id
+        partner_pool = sess.pool.get('res.partner')
+        partner_id = record.order_id.company_id.partner_id.id
+        partner = partner_pool.browse(sess.cr, ic_uid, partner_id)
+        pricelist_id = None
+        if is_po:
+            pricelist_id = (partner.property_product_pricelist_purchase.id
+                            if partner.property_product_pricelist_purchase
+                            else False)
+        else:
+            pricelist_id = (partner.property_product_pricelist.id
+                            if partner.property_product_pricelist
+                            else False)
+        pricelist_pool = sess.pool.get('product.pricelist')
+        price_unit = pricelist_pool.price_get(
+            sess.cr, ic_uid, [pricelist_id],
+            record.product_id.id, record.product_uom_qty)
+        price = price_unit[int(pricelist_id)]
+        return {'price_unit': price}
+
+    @mapping
+    def map_all(self, record):
+        assert self._icops
+        return self._get_mapping(self._icops.concept, record)
+
+    def so2so_price(self, record):
+        return self._price(record)
+
+    def so2so_product(self, record):
+        return {
+            'product_id': record.product_id.id,
+            'product_uom': record.product_uom.id,
+            'product_uom_qty': record.product_uom_qty
+        }
+
+    def so2po_price(self, record):
+        return self._price(record, True)
+
+    def so2po_product(self, record):
+        return {
+            'product_id': record.product_id.id,
+            'product_uom': record.product_uom.id,
+            'product_qty': record.product_uom_qty
+        }
+
+    def so2po_date_planned(self, record):
+        sess = self.session
+        order = record.order_id
+        order_pool = sess.pool.get(order._name)
+        date_planned = order_pool._get_date_planned(
+            sess.cr, sess.uid, order, record, order.date_order)
+        return {'date_planned': date_planned}

=== added file 'base_intercompany_sale/sale_view.xml'
--- base_intercompany_sale/sale_view.xml	1970-01-01 00:00:00 +0000
+++ base_intercompany_sale/sale_view.xml	2014-01-28 03:02:10 +0000
@@ -0,0 +1,26 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<openerp>
+    <data>
+        <record id="view_order_form" model="ir.ui.view">
+            <field name="name">sale.order</field>
+            <field name="model">sale.order</field>
+            <field name="inherit_id" ref="sale.view_order_form" />
+            <field name="arch" type="xml">
+                <xpath expr="//header/button[last()]" position="after">
+                    <button name="action_unlock" string="Unlock" type="object" attrs="{'invisible':[('locked', '=', False)]}" class="oe_highlight" />
+                </xpath>
+                <xpath expr="//page[last()]" position="after">
+                    <page string="ICOPS">
+                        <field name="locked" invisible="1" />
+                        <field name="icops_bind_ids">
+                            <tree string="ICOPS Backend" editable="bottom">
+                                <field name="backend_id" />
+                                <field name="icops_ids" />
+                            </tree>
+                        </field>
+                    </page>
+                </xpath>
+            </field>
+        </record>
+    </data>
+</openerp>
\ No newline at end of file

=== added directory 'base_intercompany_sale/security'
=== added file 'base_intercompany_sale/security/ir.model.access.csv'
--- base_intercompany_sale/security/ir.model.access.csv	1970-01-01 00:00:00 +0000
+++ base_intercompany_sale/security/ir.model.access.csv	2014-01-28 03:02:10 +0000
@@ -0,0 +1,21 @@
+id,name,model_id:id,group_id:id,perm_read,perm_write,perm_create,perm_unlink
+access_icops_sale_order_manager,icops sale order manager,base_intercompany_sale.model_icops_sale_order,base.group_sale_manager,1,1,1,1
+access_icops_sale_order_user,icops sale order user,base_intercompany_sale.model_icops_sale_order,base.group_sale_salesman,1,1,1,1
+access_icops_sale_order_line_manager,icops sale order line manager,base_intercompany_sale.model_icops_sale_order_line,base.group_sale_manager,1,1,1,1
+access_icops_sale_order_line_user,icops sale order line user,base_intercompany_sale.model_icops_sale_order_line,base.group_sale_salesman,1,1,1,1
+access_icops_backend_manager,icops backend manager,base_intercompany.model_icops_backend,base.group_sale_manager,1,1,1,1
+access_icops_backend_user,icops backend user,base_intercompany.model_icops_backend,base.group_sale_salesman,1,1,1,1
+access_res_intercompany_manager,res intercompany manager,base_intercompany.model_res_intercompany,base.group_sale_manager,1,1,1,1
+access_res_intercompany_user,res intercompany user,base_intercompany.model_res_intercompany,base.group_sale_salesman,1,1,1,1
+access_icops_record_manager,icops record manager,base_intercompany.model_icops_record,base.group_sale_manager,1,1,1,1
+access_icops_record_user,icops record user,base_intercompany.model_icops_record,base.group_sale_salesman,1,1,1,1
+access_icops_purchase_order_manager,icops purchase order manager,base_intercompany_sale.model_icops_purchase_order,purchase.group_purchase_manager,1,1,1,1
+access_icops_purchase_order_user,icops purchase order user,base_intercompany_sale.model_icops_purchase_order,purchase.group_purchase_user,1,1,1,1
+access_icops_purchase_order_line_manager,icops purchase order line manager,base_intercompany_sale.model_icops_purchase_order_line,purchase.group_purchase_manager,1,1,1,1
+access_icops_purchase_order_line_user,icops purchase order line user,base_intercompany_sale.model_icops_purchase_order_line,purchase.group_purchase_user,1,1,1,1
+access_icops_backend_manager,icops backend manager,base_intercompany.model_icops_backend,purchase.group_purchase_manager,1,1,1,1
+access_icops_backend_user,icops backend user,base_intercompany.model_icops_backend,purchase.group_purchase_user,1,1,1,1
+access_res_intercompany_manager,res intercompany manager,base_intercompany.model_res_intercompany,purchase.group_purchase_manager,1,1,1,1
+access_res_intercompany_user,res intercompany user,base_intercompany.model_res_intercompany,purchase.group_purchase_user,1,1,1,1
+access_icops_record_manager,icops record manager,base_intercompany.model_icops_record,purchase.group_purchase_manager,1,1,1,1
+access_icops_record_user,icops record user,base_intercompany.model_icops_record,purchase.group_purchase_user,1,1,1,1
\ No newline at end of file

=== added directory 'base_intercompany_sale/static'
=== added directory 'base_intercompany_sale/static/description'
=== added file 'base_intercompany_sale/static/description/Intercompany_setup.png'
Binary files base_intercompany_sale/static/description/Intercompany_setup.png	1970-01-01 00:00:00 +0000 and base_intercompany_sale/static/description/Intercompany_setup.png	2014-01-28 03:02:10 +0000 differ
=== added file 'base_intercompany_sale/static/description/index.html'
--- base_intercompany_sale/static/description/index.html	1970-01-01 00:00:00 +0000
+++ base_intercompany_sale/static/description/index.html	2014-01-28 03:02:10 +0000
@@ -0,0 +1,26 @@
+<section class="oe_container">
+    <div class="oe_row">
+        <h2 class="oe_slogan">base_intercompany_sale</h2> 
+        <h3 class="oe_slogan">Sale Inter-Company Processing (ICOPS) <br /></h3>
+        <h4 class="oe_slogan"><a href="http://www.elico-corp.com";>By Elico Corp</a></h4>
+            <p>
+                This module is an extension designed to manage Inter-company Process (ICOPS) and allows 2 companies to create Sale Orders and Purchase Order in each other.
+            </p>
+            <ul>
+                <li>Sale Order to Purchase Order (so2po)</li>
+                <li>Sale Order to Sale Order (so2so)</li>
+                <li>Purchase Order to Sale Order (po2so)</li>
+                <li>Handles the following events: Create, Update, Delete, Confirm and Cancel</li>
+            </ul>
+            <p>
+                TODO: demo data to be improved.<br />
+                Blueprint: <a href="https://blueprints.launchpad.net/multi-company/+spec/icops";>https://blueprints.launchpad.net/multi-company/+spec/icops</a><br />
+            </p>
+            <div class="oe_row_img oe_centered oe_mt32">
+                <img class="oe_picture oe_screenshot" src="Intercompany_setup.png" />
+            </div>
+    </div>
+    <div class="oe_row oe_centeralign oe_more_space">
+        <a href="http://www.elico-corp.com/saas/"; class="oe_button oe_big">Start your <span class="oe_emph">free</span> trial</a>
+    </div>
+</section>
\ No newline at end of file

=== added directory 'base_intercompany_sale/static/src'
=== added directory 'base_intercompany_sale/static/src/img'
=== added file 'base_intercompany_sale/static/src/img/icon.png'
Binary files base_intercompany_sale/static/src/img/icon.png	1970-01-01 00:00:00 +0000 and base_intercompany_sale/static/src/img/icon.png	2014-01-28 03:02:10 +0000 differ
=== added directory 'base_intercompany_sale/tests'
=== added file 'base_intercompany_sale/tests/__init__.py'
--- base_intercompany_sale/tests/__init__.py	1970-01-01 00:00:00 +0000
+++ base_intercompany_sale/tests/__init__.py	2014-01-28 03:02:10 +0000
@@ -0,0 +1,30 @@
+# -*- coding: utf-8 -*-
+##############################################################################
+#
+#    OpenERP, Open Source Management Solution
+#    Copyright (c) 2010-2014 Elico Corp. All Rights Reserved.
+#    Augustin Cisterne-Kaas <augustin.cisterne-kaas@xxxxxxxxxxxxxx>
+#    Eric Caudal <eric.caudal@xxxxxxxxxxxxxx>
+#
+#    This program is free software: you can redistribute it and/or modify
+#    it under the terms of the GNU Affero General Public License as
+#    published by the Free Software Foundation, either version 3 of the
+#    License, or (at your option) any later version.
+#
+#    This program is distributed in the hope that it will be useful,
+#    but WITHOUT ANY WARRANTY; without even the implied warranty of
+#    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+#    GNU Affero General Public License for more details.
+#
+#    You should have received a copy of the GNU Affero General Public License
+#    along with this program.  If not, see <http://www.gnu.org/licenses/>.
+#
+##############################################################################
+import test_sale
+
+fast_suite = [
+]
+
+checks = [
+    test_sale
+]

=== added file 'base_intercompany_sale/tests/test_sale.py'
--- base_intercompany_sale/tests/test_sale.py	1970-01-01 00:00:00 +0000
+++ base_intercompany_sale/tests/test_sale.py	2014-01-28 03:02:10 +0000
@@ -0,0 +1,181 @@
+# -*- coding: utf-8 -*-
+##############################################################################
+#
+#    OpenERP, Open Source Management Solution
+#    Copyright (c) 2010-2014 Elico Corp. All Rights Reserved.
+#    Augustin Cisterne-Kaas <augustin.cisterne-kaas@xxxxxxxxxxxxxx>
+#    Eric Caudal <eric.caudal@xxxxxxxxxxxxxx>
+#
+#    This program is free software: you can redistribute it and/or modify
+#    it under the terms of the GNU Affero General Public License as
+#    published by the Free Software Foundation, either version 3 of the
+#    License, or (at your option) any later version.
+#
+#    This program is distributed in the hope that it will be useful,
+#    but WITHOUT ANY WARRANTY; without even the implied warranty of
+#    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+#    GNU Affero General Public License for more details.
+#
+#    You should have received a copy of the GNU Affero General Public License
+#    along with this program.  If not, see <http://www.gnu.org/licenses/>.
+#
+##############################################################################
+import unittest2
+from datetime import datetime, timedelta
+import openerp
+import openerp.tests.common as common
+from openerp.osv import osv
+
+DB = common.DB
+ADMIN_USER_ID = common.ADMIN_USER_ID
+
+
+class test_sale(object):
+
+    def configure(self):
+        # get super user
+        self.m = self.registry('ir.model.data')
+        # get companies
+        self.company_origin = self.m.get_object(
+            self.cr, self.uid, 'base_intercompany', 'company_origin')
+
+        self.company_destination = self.m.get_object(
+            self.cr, self.uid, 'base_intercompany', 'company_destination')
+
+        self.user_origin = self.m.get_object(
+            self.cr, self.uid, 'base_intercompany', 'user_origin')
+
+        self.user_destination = self.m.get_object(
+            self.cr, self.uid, 'base_intercompany', 'user_destination')
+
+        self.shop_origin = self.m.get_object(
+            self.cr, self.uid, 'base_intercompany_sale', 'shop_origin')
+
+        self.backend_origin = self.m.get_object(
+            self.cr, self.uid, 'base_intercompany', 'backend_origin')
+
+        self.backend_destination = self.m.get_object(
+            self.cr, self.uid, 'base_intercompany', 'backend_destination')
+
+    def set_backend(self, on_create=True, on_write=True,
+                    on_unlink=True, on_confirm=True, on_cancel=True):
+        assert hasattr(self, 'concept')
+        backend_pool = self.registry('icops.backend')
+        backend_pool.write(self.cr, ADMIN_USER_ID, self.backend_origin.id, {
+            'icops_ids': [(5, 0), (0, 0, {
+                'backend_id': self.backend_origin.id,
+                'backend_to': self.backend_destination.id,
+                'concept': self.concept,
+                'on_create': on_create,
+                'on_write': on_write,
+                'on_unlink': on_unlink,
+                'on_confirm': on_confirm,
+                'on_cancel': on_cancel
+            })]
+        })
+
+    def create_object(self):
+        pool_origin = self.registry(self.model_origin)
+        uid = self.user_origin.id
+        obj_id = pool_origin.create(
+            self.cr, self.user_origin.id, self.data_obj)
+        obj = pool_origin.browse(self.cr, uid, obj_id)
+        bind_ids = obj.icops_bind_ids
+        self.assertEqual(len(bind_ids), 1)
+        bind_id = bind_ids[0]
+        icops_ids = bind_id.icops_ids
+        self.assertEqual(len(icops_ids), 1)
+        icops = icops_ids[0]
+        ic_uid = icops.backend_id.icops_uid
+        self.assertEqual(icops.model, self.model_destination)
+        pool_destination = self.registry(icops.model)
+        obj_dest = pool_destination.browse(
+            self.cr, ic_uid, icops.record_id)
+        self.assertIsNotNone(obj_dest)
+        return obj, obj_dest
+
+    def write_object(self, obj, obj_dest):
+        pool_origin = self.registry(self.model_origin)
+        uid = self.user_origin.id
+        self.assertEqual(len(obj.order_line), 0)
+        self.assertEqual(len(obj_dest.order_line), 0)
+        pool_origin.write(self.cr, uid, obj.id, {
+            'order_line': [(5, 0), (0, 0, self.data_line)]
+        })
+        self.assertEqual(len(obj.order_line), 2)
+        self.assertEqual(len(obj_dest.order_line), 2)
+
+
+    def test_01_creation(self):
+        self.set_backend()
+        self.create_object()
+
+    def test_02_creation_raise_exception(self):
+        self.set_backend(on_create=False)
+        self.assertRaises(
+            osv.except_osv, self.create_object)
+
+    def test_03_creation_without_write_permission(self):
+        self.set_backend(on_write=False)
+        self.create_object()
+
+    # def test_04_write(self):
+    #     self.set_backend()
+    #     pool_origin = self.registry(self.model_origin)
+    #     obj, obj_dest = self.create_object()
+    #     self.write_object(obj, obj_dest)
+
+
+class test_sale_so2po(common.TransactionCase, test_sale):
+
+    def setUp(self):
+        super(test_sale_so2po, self).setUp()
+        self.configure()
+        self.concept = 'so2po'
+        self.model_origin = 'sale.order'
+        self.model_destination = 'purchase.order'
+        self.data_obj = {
+            'partner_id': self.company_destination.partner_id.id,
+            'partner_invoice_id': self.company_destination.partner_id.id,
+            'partner_shipping_id': self.company_destination.partner_id.id,
+            'shop_id': self.shop_origin.id,
+            'date_order': datetime.now().strftime('%Y-%m-%d'),
+            'pricelist_id': self.user_origin.property_product_pricelist.id}
+
+        self.data_line = {
+            'name': 'Test product',
+            'product_uom': 1,
+            'product_uom_qty': 1,
+            'price_unit': 100
+        }
+
+
+class test_sale_so2so(common.TransactionCase, test_sale):
+
+    def setUp(self):
+        super(test_sale_so2so, self).setUp()
+        self.configure()
+        self.concept = 'so2so'
+        self.model_origin = 'sale.order'
+        self.model_destination = 'sale.order'
+        self.data_obj = {
+            'partner_id': self.company_destination.partner_id.id,
+            'partner_invoice_id': self.company_destination.partner_id.id,
+            'partner_shipping_id': self.company_destination.partner_id.id,
+            'shop_id': self.shop_origin.id,
+            'date_order': datetime.now().strftime('%Y-%m-%d'),
+            'pricelist_id': self.user_origin.property_product_pricelist.id}
+
+        self.data_line = {
+            'name': 'Test product',
+            'product_uom': 1,
+            'product_uom_qty': 1,
+            'price_unit': 100
+        }
+# class test_sale_po2so(common.TransactionCase, test_sale):
+
+#     def setUp(self):
+#         super(test_sale_po2so, self).setUp()
+#         self.configure()
+#         self.model_origin = 'purchase.order'
+#         self.model_destination = 'sale.order'


Follow ups