openerp-community-reviewer team mailing list archive
-
openerp-community-reviewer team
-
Mailing list archive
-
Message #03221
[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