Merge lp:~ajite/multi-company/multi-company-add-0001 into lp:multi-company

Status: Needs review
Proposed branch: lp:~ajite/multi-company/multi-company-add-0001
Merge into: lp:multi-company
Diff against target: 2897 lines (+2703/-0)
33 files modified
base_intercompany/__init__.py (+27/-0)
base_intercompany/__openerp__.py (+49/-0)
base_intercompany/backend.py (+30/-0)
base_intercompany/base_intercompany_demo.xml (+76/-0)
base_intercompany/base_intercompany_menu.xml (+22/-0)
base_intercompany/company.py (+142/-0)
base_intercompany/connector.py (+136/-0)
base_intercompany/consumer.py (+77/-0)
base_intercompany/icops_model.py (+92/-0)
base_intercompany/icops_model_view.xml (+59/-0)
base_intercompany/security/ir.model.access.csv (+3/-0)
base_intercompany/static/description/index.html (+25/-0)
base_intercompany/unit/__init__.py (+26/-0)
base_intercompany/unit/backend_adapter.py (+151/-0)
base_intercompany/unit/binder.py (+122/-0)
base_intercompany/unit/export_synchronizer.py (+258/-0)
base_intercompany/unit/mapper.py (+66/-0)
base_intercompany_sale/__init__.py (+28/-0)
base_intercompany_sale/__openerp__.py (+50/-0)
base_intercompany_sale/base_intercompany_sale_demo.xml (+53/-0)
base_intercompany_sale/company.py (+56/-0)
base_intercompany_sale/connector.py (+32/-0)
base_intercompany_sale/consumer.py (+79/-0)
base_intercompany_sale/icops_model.py (+33/-0)
base_intercompany_sale/icops_model_view.xml (+15/-0)
base_intercompany_sale/purchase.py (+296/-0)
base_intercompany_sale/purchase_view.xml (+26/-0)
base_intercompany_sale/sale.py (+390/-0)
base_intercompany_sale/sale_view.xml (+26/-0)
base_intercompany_sale/security/ir.model.access.csv (+21/-0)
base_intercompany_sale/static/description/index.html (+26/-0)
base_intercompany_sale/tests/__init__.py (+30/-0)
base_intercompany_sale/tests/test_sale.py (+181/-0)
To merge this branch: bzr merge lp:~ajite/multi-company/multi-company-add-0001
Reviewer Review Type Date Requested Status
Alexandre Fayolle - camptocamp Needs Resubmitting
Review via email: mp+203454@code.launchpad.net

Commit message

[ADD] base_intercompany and base_intercompany_sale modules.

Description of the change

Added base_intercompany and base_intercompany_sale modules.

To post a comment you must log in.
6. By Augustin Cisterne-Kaas <email address hidden>

[FIX] Made it compatible with the last version of the connector

7. By Augustin Cisterne-Kaas <email address hidden>

[FIX] A bug that was preventing to create normal SO/PO

Revision history for this message
Alexandre Fayolle - camptocamp (alexandre-fayolle-c2c) wrote :
Download full text (6.1 KiB)

This looks like a great module. However it needs some work before further
review is done:

1. this module will cause a crash at server startup if it is present in the addons path (not necessarily installed) and the connector module is not available in the addons path::

    CRITICAL test_70 openerp.modules.module: Couldn't load module web
    CRITICAL test_70 openerp.modules.module: No module named connector
    ERROR test_70 openerp: Failed to initialize database `test_70`.
    Traceback (most recent call last):
      File "/home/afayolle/work/oerp/openobject-server/7.0/openerp/cli/server.py", line 97, in preload_registry
        db, registry = openerp.pooler.get_db_and_pool(dbname,update_module=update_module)
      File "/home/afayolle/work/oerp/openobject-server/7.0/openerp/pooler.py", line 33, in get_db_and_pool
        registry = RegistryManager.get(db_name, force_demo, status, update_module)
      File "/home/afayolle/work/oerp/openobject-server/7.0/openerp/modules/registry.py", line 203, in get
        update_module)
      File "/home/afayolle/work/oerp/openobject-server/7.0/openerp/modules/registry.py", line 233, in new
        openerp.modules.load_modules(registry.db, force_demo, status, update_module)
      File "/home/afayolle/work/oerp/openobject-server/7.0/openerp/modules/loading.py", line 354, in load_modules
        loaded_modules, update_module)
      File "/home/afayolle/work/oerp/openobject-server/7.0/openerp/modules/loading.py", line 256, in load_marked_modules
        loaded, processed = load_module_graph(cr, graph, progressdict, report=report, skip_modules=loaded_modules, perform_checks=perform_checks)
      File "/home/afayolle/work/oerp/openobject-server/7.0/openerp/modules/loading.py", line 159, in load_module_graph
        load_openerp_module(package.name)
      File "/home/afayolle/work/oerp/openobject-server/7.0/openerp/modules/module.py", line 415, in load_openerp_module
        getattr(sys.modules['openerp.addons.' + module_name], info['post_load'])()
      File "/home/afayolle/work/oerp/web/7.0/addons/web/http.py", line 628, in wsgi_postload
        openerp.wsgi.register_wsgi_handler(Root())
      File "/home/afayolle/work/oerp/web/7.0/addons/web/http.py", line 517, in __init__
        self.load_addons()
      File "/home/afayolle/work/oerp/web/7.0/addons/web/http.py", line 580, in load_addons
        m = __import__('openerp.addons.' + module)
      File "/home/afayolle/work/oerp/openobject-server/7.0/openerp/modules/module.py", line 133, in load_module
        mod = imp.load_module('openerp.addons.' + module_part, f, path, descr)
      File "/home/afayolle/work/oca/multi-company/multi-company-add-0001/base_intercompany/__init__.py", line 23, in <module>
        import connector
      File "/home/afayolle/work/oca/multi-company/multi-company-add-0001/base_intercompany/connector.py", line 25, in <module>
        from openerp.addons.connector.connector import Environment
      File "/home/afayolle/work/oerp/openobject-server/7.0/openerp/modules/module.py", line 132, in load_module
        f, path, descr = imp.find_module(module_part, ad_paths)
    ImportError: No module named connector

2. The addon has tests (good...

Read more...

review: Needs Fixing (tried to install and run the tests)
Revision history for this message
Alexandre Fayolle - camptocamp (alexandre-fayolle-c2c) wrote :

Hello,

The management of the project has moved to Github: https://github.com/OCA/multi-company

Please migrate your merge proposal to Github. You may want to check https://github.com/OCA/maintainers-tools/wiki/How-to-move-a-Merge-Proposal-to-GitHub for an explanation on how to proceed.

Thanks for contributing to the project

review: Needs Resubmitting

Unmerged revisions

7. By Augustin Cisterne-Kaas <email address hidden>

[FIX] A bug that was preventing to create normal SO/PO

6. By Augustin Cisterne-Kaas <email address hidden>

[FIX] Made it compatible with the last version of the connector

5. By Augustin Cisterne-Kaas <email address hidden>

[ADD] Module base_intercompany and base_intercompany_sale

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== added directory 'base_intercompany'
2=== added file 'base_intercompany/__init__.py'
3--- base_intercompany/__init__.py 1970-01-01 00:00:00 +0000
4+++ base_intercompany/__init__.py 2014-02-23 01:10:59 +0000
5@@ -0,0 +1,27 @@
6+# -*- coding: utf-8 -*-
7+##############################################################################
8+#
9+# OpenERP, Open Source Management Solution
10+# Copyright (c) 2010-2014 Elico Corp. All Rights Reserved.
11+# Augustin Cisterne-Kaas <augustin.cisterne-kaas@elico-corp.com>
12+# Eric Caudal <eric.caudal@elico-corp.com>
13+
14+# This program is free software: you can redistribute it and/or modify
15+# it under the terms of the GNU Affero General Public License as
16+# published by the Free Software Foundation, either version 3 of the
17+# License, or (at your option) any later version.
18+#
19+# This program is distributed in the hope that it will be useful,
20+# but WITHOUT ANY WARRANTY; without even the implied warranty of
21+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
22+# GNU Affero General Public License for more details.
23+#
24+# You should have received a copy of the GNU Affero General Public License
25+# along with this program. If not, see <http://www.gnu.org/licenses/>.
26+#
27+##############################################################################
28+import connector
29+import consumer
30+import backend
31+import company
32+import icops_model
33
34=== added file 'base_intercompany/__openerp__.py'
35--- base_intercompany/__openerp__.py 1970-01-01 00:00:00 +0000
36+++ base_intercompany/__openerp__.py 2014-02-23 01:10:59 +0000
37@@ -0,0 +1,49 @@
38+# -*- coding: utf-8 -*-
39+##############################################################################
40+#
41+# OpenERP, Open Source Management Solution
42+# Copyright (c) 2010-2014 Elico Corp. All Rights Reserved.
43+# Augustin Cisterne-Kaas <augustin.cisterne-kaas@elico-corp.com>
44+# Eric Caudal <eric.caudal@elico-corp.com>
45+
46+# This program is free software: you can redistribute it and/or modify
47+# it under the terms of the GNU Affero General Public License as
48+# published by the Free Software Foundation, either version 3 of the
49+# License, or (at your option) any later version.
50+#
51+# This program is distributed in the hope that it will be useful,
52+# but WITHOUT ANY WARRANTY; without even the implied warranty of
53+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
54+# GNU Affero General Public License for more details.
55+#
56+# You should have received a copy of the GNU Affero General Public License
57+# along with this program. If not, see <http://www.gnu.org/licenses/>.
58+#
59+##############################################################################
60+
61+{'name': 'Base Intercompany',
62+ 'version': '0.4',
63+ 'category': 'Generic Modules',
64+ 'depends': ['connector'],
65+ 'author': 'Elico Corp',
66+ 'license': 'AGPL-3',
67+ 'website': 'https://www.elico-corp.com',
68+ 'description': """
69+This module is the structure designed to manage Inter-company Process (ICOPS)
70+and allows 2 companies to create objects in each other.
71+This module needs to be installed with one of the following modules:
72+ - Base Intercompany Sale
73+ - Base Intercompany Stock (in development)
74+ - Any module which extends the Base Intercompany module
75+
76+TODO: demo data to be improved.\n
77+Blueprint: https://blueprints.launchpad.net/multi-company/+spec/icops
78+""",
79+ 'images': [],
80+ 'demo': ['base_intercompany_demo.xml'],
81+ 'data': ['security/ir.model.access.csv',
82+ 'icops_model_view.xml',
83+ 'base_intercompany_menu.xml'],
84+ 'installable': True,
85+ 'application': False,
86+ }
87
88=== added file 'base_intercompany/backend.py'
89--- base_intercompany/backend.py 1970-01-01 00:00:00 +0000
90+++ base_intercompany/backend.py 2014-02-23 01:10:59 +0000
91@@ -0,0 +1,30 @@
92+# -*- coding: utf-8 -*-
93+##############################################################################
94+#
95+# OpenERP, Open Source Management Solution
96+# Copyright (c) 2010-2014 Elico Corp. All Rights Reserved.
97+# Augustin Cisterne-Kaas <augustin.cisterne-kaas@elico-corp.com>
98+# Eric Caudal <eric.caudal@elico-corp.com>
99+
100+# This program is free software: you can redistribute it and/or modify
101+# it under the terms of the GNU Affero General Public License as
102+# published by the Free Software Foundation, either version 3 of the
103+# License, or (at your option) any later version.
104+#
105+# This program is distributed in the hope that it will be useful,
106+# but WITHOUT ANY WARRANTY; without even the implied warranty of
107+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
108+# GNU Affero General Public License for more details.
109+#
110+# You should have received a copy of the GNU Affero General Public License
111+# along with this program. If not, see <http://www.gnu.org/licenses/>.
112+#
113+##############################################################################
114+import openerp.addons.connector.backend as backend
115+
116+
117+icops = backend.Backend('icops')
118+""" Generic ICOPS Backend """
119+
120+icops7 = backend.Backend(parent=icops, version='7.0')
121+""" ICOPS Backend for OpenERP 7 """
122
123=== added file 'base_intercompany/base_intercompany_demo.xml'
124--- base_intercompany/base_intercompany_demo.xml 1970-01-01 00:00:00 +0000
125+++ base_intercompany/base_intercompany_demo.xml 2014-02-23 01:10:59 +0000
126@@ -0,0 +1,76 @@
127+<?xml version="1.0" encoding="utf-8"?>
128+<openerp>
129+ <data noupdate="1">
130+ <record id="customer_origin" model="res.partner">
131+ <field name="name">Company Origin</field>
132+ </record>
133+
134+ <record id="supplier_destination" model="res.partner">
135+ <field name="name">Company Destination</field>
136+ <field name="supplier" eval="True" />
137+ </record>
138+
139+ <record id="company_origin" model="res.company">
140+ <field name="name">Company Origin</field>
141+ <field name="parent_id" ref="base.main_company"/>
142+ <field name="partner_id" ref="customer_origin" />
143+ </record>
144+
145+ <record id="company_destination" model="res.company">
146+ <field name="name">Company Destination</field>
147+ <field name="parent_id" ref="base.main_company"/>
148+ <field name="partner_id" ref="supplier_destination" />
149+ </record>
150+
151+ <record id="customer_origin" model="res.partner">
152+ <field name="company_id"></field>
153+ </record>
154+
155+ <record id="supplier_destination" model="res.partner">
156+ <field name="company_id"></field>
157+ </record>
158+
159+ <record id="partner_origin" model="res.partner">
160+ <field name="name">User Origin</field>
161+ <field name="company_id" ref="company_origin"/>
162+ <field name="customer" eval="False"/>
163+ </record>
164+
165+ <record id="user_origin" model="res.users">
166+ <field name="partner_id" ref="partner_origin"/>
167+ <field name="login">user.origin</field>
168+ <field name="password">user.origin</field>
169+ <field name="company_id" ref="company_origin"/>
170+ <field name="company_ids" eval="[(6,0,[ref('company_origin')])]" />
171+ <field name="groups_id" eval="[(6,0,[ref('base.group_user'), ref('base.group_partner_manager')])]"/>
172+ </record>
173+
174+ <record id="partner_destination" model="res.partner">
175+ <field name="name">User Destination</field>
176+ <field name="company_id" ref="company_destination"/>
177+ <field name="customer" eval="False"/>
178+ </record>
179+
180+ <record id="user_destination" model="res.users">
181+ <field name="partner_id" ref="partner_destination"/>
182+ <field name="login">user.destination</field>
183+ <field name="password">user.destination</field>
184+ <field name="company_id" ref="company_destination"/>
185+ <field name="company_ids" eval="[(6,0,[ref('company_destination')])]" />
186+ <field name="groups_id" eval="[(6,0,[ref('base.group_user'), ref('base.group_partner_manager')])]"/>
187+ </record>
188+
189+ <!-- ICOPS Setup -->
190+ <record id="backend_origin" model="icops.backend">
191+ <field name="name">Backend Origin</field>
192+ <field name="company_id" ref="company_origin" />
193+ <field name="icops_uid" ref="user_origin" />
194+ </record>
195+
196+ <record id="backend_destination" model="icops.backend">
197+ <field name="name">Backend Destination</field>
198+ <field name="company_id" ref="company_destination" />
199+ <field name="icops_uid" ref="user_destination" />
200+ </record>
201+ </data>
202+</openerp>
203\ No newline at end of file
204
205=== added file 'base_intercompany/base_intercompany_menu.xml'
206--- base_intercompany/base_intercompany_menu.xml 1970-01-01 00:00:00 +0000
207+++ base_intercompany/base_intercompany_menu.xml 2014-02-23 01:10:59 +0000
208@@ -0,0 +1,22 @@
209+<?xml version="1.0" encoding="utf-8"?>
210+<openerp>
211+ <data>
212+ <menuitem id="menu_icops_root"
213+ parent="connector.menu_connector_root"
214+ name="ICOPS"
215+ sequence="10"
216+ groups="connector.group_connector_manager"/>
217+
218+ <menuitem id="menu_icops_backend"
219+ name="Backends"
220+ parent="menu_icops_root"
221+ action="action_icops_backend"/>
222+
223+ <menuitem id="menu_server_root"
224+ parent="connector.menu_connector_root"
225+ name="Server"
226+ sequence="20"
227+ groups="connector.group_connector_manager"/>
228+
229+ </data>
230+</openerp>
231
232=== added file 'base_intercompany/company.py'
233--- base_intercompany/company.py 1970-01-01 00:00:00 +0000
234+++ base_intercompany/company.py 2014-02-23 01:10:59 +0000
235@@ -0,0 +1,142 @@
236+# -*- coding: utf-8 -*-
237+##############################################################################
238+#
239+# OpenERP, Open Source Management Solution
240+# Copyright (c) 2010-2014 Elico Corp. All Rights Reserved.
241+# Augustin Cisterne-Kaas <augustin.cisterne-kaas@elico-corp.com>
242+# Eric Caudal <eric.caudal@elico-corp.com>
243+
244+# This program is free software: you can redistribute it and/or modify
245+# it under the terms of the GNU Affero General Public License as
246+# published by the Free Software Foundation, either version 3 of the
247+# License, or (at your option) any later version.
248+#
249+# This program is distributed in the hope that it will be useful,
250+# but WITHOUT ANY WARRANTY; without even the implied warranty of
251+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
252+# GNU Affero General Public License for more details.
253+#
254+# You should have received a copy of the GNU Affero General Public License
255+# along with this program. If not, see <http://www.gnu.org/licenses/>.
256+#
257+##############################################################################
258+
259+from openerp.osv import orm, fields
260+
261+
262+class res_intercompany(orm.Model):
263+ _name = 'res.intercompany'
264+ _description = 'Inter-Company'
265+
266+ def _select_concepts(self, cr, uid, context=None):
267+ """ Available concepts
268+
269+ Can be inherited to add custom versions.
270+ """
271+ return []
272+
273+ def _select_models(self, cr, uid, context=None):
274+ """ Available Object names
275+
276+ Can be inherited to add custom versions.
277+ """
278+ return {}
279+
280+ def _get_model(self, cr, uid, ids, name, arg, context=None):
281+ """ Available versions
282+
283+ Can be inherited to add custom versions.
284+ """
285+ res = {}
286+ for intercompany in self.browse(cr, uid, ids, context):
287+ model = None
288+ if intercompany.concept:
289+ model = self._select_models(
290+ cr, uid)[intercompany.concept]
291+ res[intercompany.id] = model
292+ return res
293+
294+ def get_intercompany(self, cr, uid, obj_id,
295+ obj_class_name, obj_name, context=None):
296+ """
297+ get company from and to
298+ """
299+ if isinstance(obj_id, list):
300+ obj_id = obj_id[0]
301+ assert isinstance(obj_id, int) or isinstance(obj_id, long)
302+ obj = self.pool.get(obj_class_name).browse(
303+ cr,
304+ uid,
305+ obj_id,
306+ context=None)
307+ company = obj.company_id
308+ ic_uid_ids = None
309+ company_to_ids = []
310+ for ic in company.intercompany_ids:
311+ if ic.concept == obj_name:
312+ company_to_ids.append(ic.company_to.id)
313+ ic_uid_ids = ic.icops_uid.id
314+ if company.intercompany_ids:
315+ ic_uid_ids = company.intercompany_ids[0].icops_uid.id
316+ return obj.company_id.id, company_to_ids, ic_uid_ids
317+
318+ def _check_intercompany_user(self, cr, uid, ids, context=None):
319+ for ic in self.browse(cr, uid, ids, context=context):
320+ if not ic.icops_uid:
321+ return False
322+ return True
323+
324+ _columns = {
325+ 'backend_id': fields.many2one('icops.backend', 'Original Backend',
326+ required=True, ondelete='cascade'),
327+ 'backend_to': fields.many2one('icops.backend', 'Destination Backend',
328+ required=True, ondelete='cascade'),
329+ 'concept': fields.selection(_select_concepts, string="Concept",
330+ required=True),
331+ 'model': fields.function(_get_model, type='char',
332+ string='Object', store=False),
333+ 'icops_uid': fields.related(
334+ 'backend_to', 'icops_uid', type='many2one',
335+ relation='res.users', readonly=True, string='IC User'),
336+ 'on_create': fields.boolean('Create'),
337+ 'on_write': fields.boolean('Update'),
338+ 'on_unlink': fields.boolean('Delete'),
339+ 'on_confirm': fields.boolean('Confirm'),
340+ 'on_cancel': fields.boolean('Cancel')
341+ }
342+
343+ _constraints = [
344+ (_check_intercompany_user, 'Please set IC user for the Company first',
345+ ['icops_uid'])
346+ ]
347+
348+ _defaults = {
349+ 'backend_id': lambda self, cr, uid, c: c.get('active_id', False),
350+ }
351+ # _sql_constraints = [(
352+ # 'company_from_company_to_unique', 'unique(company_from, company_to)',
353+ # 'A setup for that company already exists')]
354+
355+ def check_need_create_intercompany_object(
356+ self, cr, uid, company_from, company_to, concept,
357+ event, return_list_type=False, regular=False):
358+ """ @company_from
359+ @company_to
360+ @o2o_field_name file name.Example, so2po po2so ,,,,
361+ @node value of o2o_field_name , draft, confirm
362+ @return_list_type, the return type is list or boolean
363+ """
364+
365+ if type(company_to) != list:
366+ company_to = [company_to]
367+ request = [('company_from', '=', company_from),
368+ ('concept', '=', concept),
369+ (event, '=', True)]
370+ if not regular:
371+ request.append(('company_to', 'in', company_to))
372+ intercompany_ids = self.search(
373+ cr, uid, request)
374+ if return_list_type:
375+ return intercompany_ids
376+ else:
377+ return intercompany_ids and True or False
378
379=== added file 'base_intercompany/connector.py'
380--- base_intercompany/connector.py 1970-01-01 00:00:00 +0000
381+++ base_intercompany/connector.py 2014-02-23 01:10:59 +0000
382@@ -0,0 +1,136 @@
383+# -*- coding: utf-8 -*-
384+##############################################################################
385+#
386+# OpenERP, Open Source Management Solution
387+# Copyright (c) 2010-2014 Elico Corp. All Rights Reserved.
388+# Augustin Cisterne-Kaas <augustin.cisterne-kaas@elico-corp.com>
389+# Eric Caudal <eric.caudal@elico-corp.com>
390+
391+# This program is free software: you can redistribute it and/or modify
392+# it under the terms of the GNU Affero General Public License as
393+# published by the Free Software Foundation, either version 3 of the
394+# License, or (at your option) any later version.
395+#
396+# This program is distributed in the hope that it will be useful,
397+# but WITHOUT ANY WARRANTY; without even the implied warranty of
398+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
399+# GNU Affero General Public License for more details.
400+#
401+# You should have received a copy of the GNU Affero General Public License
402+# along with this program. If not, see <http://www.gnu.org/licenses/>.
403+#
404+##############################################################################
405+
406+from openerp.osv import orm, osv, fields
407+from openerp.addons.connector.connector import Environment
408+from openerp.addons.connector.checkpoint import checkpoint
409+
410+
411+class base_intercompany_installed(orm.AbstractModel):
412+ """Empty model used to know if the module is installed on the
413+ database.
414+
415+ If the model is in the registry, the module is installed.
416+ """
417+ _name = 'base_intercompany.installed'
418+
419+
420+def get_environment(session, model_name, backend_id):
421+ """ Create an environment to work with. """
422+ backend_record = session.browse('icops.backend', backend_id)
423+ env = Environment(backend_record, session, model_name)
424+ lang_code = 'en_US'
425+ env.set_lang(code=lang_code)
426+ return env
427+
428+
429+class icops_binding(orm.AbstractModel):
430+ _name = 'icops.binding'
431+ _inherit = 'external.binding'
432+ _description = 'Coswin Binding (abstract)'
433+
434+ _columns = {
435+ 'backend_id': fields.many2one(
436+ 'icops.backend',
437+ 'ICOPS Backend',
438+ required=True,
439+ ondelete='restrict'),
440+ 'icops_ids': fields.one2many(
441+ 'icops.record', 'binding_id',
442+ string="ICOPS Record"),
443+ }
444+
445+
446+class icops_record(orm.Model):
447+ _name = 'icops.record'
448+
449+ _columns = {
450+ 'binding_id': fields.integer('ICOPS Binding'),
451+ 'record_id': fields.integer('ICOPS ID'),
452+ 'model': fields.char('Model'),
453+ 'concept': fields.char('Concept'),
454+ 'backend_id': fields.many2one(
455+ 'icops.backend', 'ICOPS Backends'),
456+ }
457+
458+
459+class icops_model(orm.AbstractModel):
460+ _name = 'icops.model'
461+
462+ def _is_locked(self, cr, uid, ids, name, arg, context=None):
463+ res = {}
464+ for obj in self.browse(cr, uid, ids, context=context):
465+ pool = self.pool.get('icops.record')
466+ record_ids = pool.search(
467+ cr, uid, [('record_id', '=', obj.id),
468+ ('model', '=', self._name)])
469+
470+ res[obj.id] = True if record_ids else False
471+ return res
472+
473+ def _check_icops(self, cr, uid, ids, context=None):
474+ context = context or {}
475+ if isinstance(ids, int):
476+ ids = [ids]
477+ if 'icops' in context:
478+ return
479+ fields = ['locked', 'temp_unlock']
480+ for obj in self.read(cr, uid, ids, fields, context=context):
481+ if obj['temp_unlock']:
482+ return
483+ elif obj['locked']:
484+ raise osv.except_osv(
485+ 'ICOPS Error',
486+ 'This object is locked by an intercompany process')
487+
488+ _columns = {
489+ 'locked': fields.function(
490+ _is_locked, type='boolean', string='Is Locked', store=False),
491+ # To handle special workflow
492+ 'temp_unlock': fields.boolean('Temporary Unlock')
493+ }
494+
495+ def action_unlock(self, cr, uid, ids, context):
496+ pool = self.pool.get('icops.record')
497+ for obj in self.browse(cr, uid, ids, context=context):
498+ record_ids = pool.search(
499+ cr, uid, [('record_id', '=', obj.id),
500+ ('model', '=', self._name)])
501+ pool.unlink(cr, uid, record_ids, context=context)
502+
503+
504+def add_checkpoint(session, model_name, record_id, backend_id):
505+ """ Add a row in the model ``connector.checkpoint`` for a record,
506+ meaning it has to be reviewed by a user.
507+
508+ :param session: current session
509+ :type session: :py:class:openerp.addons.connector.session.ConnectorSession
510+ :param model_name: name of the model of the record to be reviewed
511+ :type model_name: str
512+ :param record_id: ID of the record to be reviewed
513+ :type record_id: int
514+ :param backend_id: ID of the Coswin Backend
515+ :type backend_id: int
516+ """
517+ return checkpoint.add_checkpoint(session, model_name, record_id,
518+ 'icops.backend', backend_id)
519
520=== added file 'base_intercompany/consumer.py'
521--- base_intercompany/consumer.py 1970-01-01 00:00:00 +0000
522+++ base_intercompany/consumer.py 2014-02-23 01:10:59 +0000
523@@ -0,0 +1,77 @@
524+# -*- coding: utf-8 -*-
525+##############################################################################
526+#
527+# OpenERP, Open Source Management Solution
528+# Copyright (c) 2010-2014 Elico Corp. All Rights Reserved.
529+# Augustin Cisterne-Kaas <augustin.cisterne-kaas@elico-corp.com>
530+# Eric Caudal <eric.caudal@elico-corp.com>
531+
532+# This program is free software: you can redistribute it and/or modify
533+# it under the terms of the GNU Affero General Public License as
534+# published by the Free Software Foundation, either version 3 of the
535+# License, or (at your option) any later version.
536+#
537+# This program is distributed in the hope that it will be useful,
538+# but WITHOUT ANY WARRANTY; without even the implied warranty of
539+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
540+# GNU Affero General Public License for more details.
541+#
542+# You should have received a copy of the GNU Affero General Public License
543+# along with this program. If not, see <http://www.gnu.org/licenses/>.
544+#
545+##############################################################################
546+from openerp.addons.connector.event import (on_record_write,
547+ on_record_create,
548+ on_record_unlink
549+ )
550+from .unit.export_synchronizer import (
551+ export_record)
552+
553+_MODEL_NAMES = ()
554+_BIND_MODEL_NAMES = ()
555+_UNLINK_MODEL_NAMES = ()
556+_UNLINK_BIND_MODEL_NAMES = ()
557+
558+
559+@on_record_create(model_names=_BIND_MODEL_NAMES)
560+@on_record_write(model_names=_BIND_MODEL_NAMES)
561+def delay_export(session, model_name, record_id, fields=None):
562+ """ Delay a job which export a binding record.
563+
564+ (A binding record being a ``icops.res.partner``,
565+ ``icops.sale.order``, ...)
566+ """
567+ export_record(session, model_name, record_id, fields=fields)
568+
569+
570+@on_record_write(model_names=_MODEL_NAMES)
571+def delay_export_all_bindings(session, model_name, record_id, fields=None):
572+ """ Delay a job which export all the bindings of a record.
573+
574+ In this case, it is called on records of normal models and will delay
575+ the export for all the bindings.
576+ """
577+ model = session.pool.get(model_name)
578+ record = model.browse(session.cr, session.uid,
579+ record_id, context=session.context)
580+ for binding in record.icops_bind_ids:
581+ export_record(session, binding._model._name, binding.id,
582+ fields=fields)
583+
584+
585+@on_record_unlink(model_names=_UNLINK_MODEL_NAMES)
586+def delay_unlink(session, model_name, record_id):
587+ """ Delay a job which delete a record on Magento.
588+
589+ Called on binding records."""
590+ fields = {'icops_delete': True}
591+ delay_export_all_bindings(session, model_name, record_id, fields)
592+
593+
594+@on_record_unlink(model_names=_UNLINK_BIND_MODEL_NAMES)
595+def delay_unlink_binding(session, model_name, record_id):
596+ """ Delay a job which delete a record on Magento.
597+
598+ Called on binding records."""
599+ fields = {'icops_delete': True}
600+ delay_export(session, model_name, record_id, fields)
601
602=== added file 'base_intercompany/icops_model.py'
603--- base_intercompany/icops_model.py 1970-01-01 00:00:00 +0000
604+++ base_intercompany/icops_model.py 2014-02-23 01:10:59 +0000
605@@ -0,0 +1,92 @@
606+# -*- coding: utf-8 -*-
607+##############################################################################
608+#
609+# OpenERP, Open Source Management Solution
610+# Copyright (c) 2010-2014 Elico Corp. All Rights Reserved.
611+# Augustin Cisterne-Kaas <augustin.cisterne-kaas@elico-corp.com>
612+# Eric Caudal <eric.caudal@elico-corp.com>
613+
614+# This program is free software: you can redistribute it and/or modify
615+# it under the terms of the GNU Affero General Public License as
616+# published by the Free Software Foundation, either version 3 of the
617+# License, or (at your option) any later version.
618+#
619+# This program is distributed in the hope that it will be useful,
620+# but WITHOUT ANY WARRANTY; without even the implied warranty of
621+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
622+# GNU Affero General Public License for more details.
623+#
624+# You should have received a copy of the GNU Affero General Public License
625+# along with this program. If not, see <http://www.gnu.org/licenses/>.
626+#
627+##############################################################################
628+
629+import logging
630+from openerp.osv import fields, orm
631+
632+_logger = logging.getLogger(__name__)
633+
634+
635+class icops_backend(orm.Model):
636+ _name = 'icops.backend'
637+ _description = 'ICOPS Backend'
638+ _inherit = 'connector.backend'
639+
640+ _backend_type = 'icops'
641+
642+ def _select_versions(self, cr, uid, context=None):
643+ """ Available versions
644+
645+ Can be inherited to add custom versions.
646+ """
647+ return [('7.0', '7.0')]
648+
649+ _columns = {
650+ 'version': fields.selection(
651+ _select_versions,
652+ string='Version',
653+ required=True),
654+ 'company_id': fields.many2one(
655+ 'res.company', string="Company", required=True),
656+ 'icops_ids': fields.one2many(
657+ 'res.intercompany', 'backend_id', 'Inter-Company Setup'),
658+ 'icops_uid': fields.many2one(
659+ 'res.users', 'IC User',
660+ required=True,
661+ domain="[('company_id', '=', company_id)]",
662+ help="User to create update unlink IC records"),
663+ 'model': fields.related(
664+ 'backend_to', 'icops_uid', type='many2one',
665+ relation='res.users', readonly=True, string='IC User'),
666+ }
667+
668+ _defaults = {
669+ 'version': '7.0',
670+ }
671+
672+ def _icops_backend(self, cr, uid, callback, domain=None,
673+ context=None):
674+ if domain is None:
675+ domain = []
676+ ids = self.search(cr, uid, domain, context=context)
677+ if ids:
678+ callback(cr, uid, ids, context=context)
679+
680+ def prepare_binding(self, cr, uid, data, context=None):
681+ context = context or {}
682+ icops_bind_ids = []
683+ if not 'icops_bind_ids' in data:
684+ data['icops_bind_ids'] = None
685+ if not 'icops' in context and not data['icops_bind_ids']:
686+ user = self.pool.get('res.users').browse(
687+ cr, uid, uid, context)
688+ backend_pool = self.pool.get('icops.backend')
689+ backend_ids = backend_pool.search(
690+ cr, uid, [('company_id', '=', user.company_id.id)])
691+ if backend_ids:
692+ backends = backend_pool.browse(
693+ cr, uid, backend_ids, context)
694+ for backend in backends:
695+ icops_bind_ids.append(
696+ (0, 0, {'backend_id': backend.id}))
697+ return icops_bind_ids
698
699=== added file 'base_intercompany/icops_model_view.xml'
700--- base_intercompany/icops_model_view.xml 1970-01-01 00:00:00 +0000
701+++ base_intercompany/icops_model_view.xml 2014-02-23 01:10:59 +0000
702@@ -0,0 +1,59 @@
703+<?xml version="1.0" encoding="utf-8"?>
704+<openerp>
705+ <data>
706+ <record id="view_icops_backend_form" model="ir.ui.view">
707+ <field name="name">icops.backend.form</field>
708+ <field name="model">icops.backend</field>
709+ <field name="arch" type="xml">
710+ <form string="ICOPS Backend" version="7.0">
711+ <sheet>
712+ <label for="name" class="oe_edit_only"/>
713+ <h1>
714+ <field name="name" class="oe_inline" />
715+ </h1>
716+ <group name="icops" string="ICOPS Configuration">
717+ <group colspan="4" col="4">
718+ <field name="version" cols="4" />
719+ <field name="company_id" cols="2" />
720+ <field name="icops_uid" cols="2" />
721+ </group>
722+ </group>
723+ <field name="icops_ids" context="{'active_id': active_id}">
724+ <tree editable="bottom">
725+ <field name="concept"/>
726+ <field name="model" invisible="1" />
727+ <field name="backend_id" invisible="1" />
728+ <field name="backend_to" />
729+ <field name='icops_uid' invisible="1" />
730+ <field name="on_create" />
731+ <field name="on_write" />
732+ <field name="on_unlink" />
733+ <field name="on_confirm" />
734+ <field name="on_cancel" />
735+ </tree>
736+ </field>
737+ </sheet>
738+ </form>
739+ </field>
740+ </record>
741+
742+ <record id="view_icops_backend_tree" model="ir.ui.view">
743+ <field name="name">icops.backend.tree</field>
744+ <field name="model">icops.backend</field>
745+ <field name="arch" type="xml">
746+ <tree string="ICOPS Backend" version="7.0">
747+ <field name="name"/>
748+ <field name="company_id" />
749+ </tree>
750+ </field>
751+ </record>
752+
753+ <record id="action_icops_backend" model="ir.actions.act_window">
754+ <field name="name">ICOPS Backends</field>
755+ <field name="res_model">icops.backend</field>
756+ <field name="view_type">form</field>
757+ <field name="view_mode">tree,form</field>
758+ <field name="view_id" ref="view_icops_backend_tree"/>
759+ </record>
760+ </data>
761+</openerp>
762
763=== added directory 'base_intercompany/security'
764=== added file 'base_intercompany/security/ir.model.access.csv'
765--- base_intercompany/security/ir.model.access.csv 1970-01-01 00:00:00 +0000
766+++ base_intercompany/security/ir.model.access.csv 2014-02-23 01:10:59 +0000
767@@ -0,0 +1,3 @@
768+id,name,model_id:id,group_id:id,perm_read,perm_write,perm_create,perm_unlink
769+access_res_intercompany_manager,res intercompany manager,base_intercompany.model_res_intercompany,connector.group_connector_manager,1,1,1,1
770+access_icops_backend_manager,icops backend manager,base_intercompany.model_icops_backend,connector.group_connector_manager,1,1,1,1
771\ No newline at end of file
772
773=== added directory 'base_intercompany/static'
774=== added directory 'base_intercompany/static/description'
775=== added file 'base_intercompany/static/description/Intercompany_setup.png'
776Binary files base_intercompany/static/description/Intercompany_setup.png 1970-01-01 00:00:00 +0000 and base_intercompany/static/description/Intercompany_setup.png 2014-02-23 01:10:59 +0000 differ
777=== added file 'base_intercompany/static/description/index.html'
778--- base_intercompany/static/description/index.html 1970-01-01 00:00:00 +0000
779+++ base_intercompany/static/description/index.html 2014-02-23 01:10:59 +0000
780@@ -0,0 +1,25 @@
781+<section class="oe_container">
782+ <div class="oe_row">
783+ <h2 class="oe_slogan">base_intercompany</h2>
784+ <h3 class="oe_slogan">Inter-Company Processing (ICOPS) <br /></h3>
785+ <h4 class="oe_slogan"><a href="http://www.elico-corp.com">By Elico Corp</a></h4>
786+ <p>
787+ 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:
788+ </p>
789+ <ul>
790+ <li>Base Intercompany Sale</li>
791+ <li>Base Intercompany Stock (in development)</li>
792+ <li>Any module which extends the Base Intercompany module</li>
793+ </ul>
794+ <p>
795+ TODO: demo data to be improved<br />
796+ Blueprint: <a href="https://blueprints.launchpad.net/multi-company/+spec/icops">https://blueprints.launchpad.net/multi-company/+spec/icops</a><br />
797+ </p>
798+ <div class="oe_row_img oe_centered oe_mt32">
799+ <img class="oe_picture oe_screenshot" src="Intercompany_setup.png" />
800+ </div>
801+ </div>
802+ <div class="oe_row oe_centeralign oe_more_space">
803+ <a href="http://www.elico-corp.com/saas/" class="oe_button oe_big">Start your <span class="oe_emph">free</span> trial</a>
804+ </div>
805+</section>
806
807=== added directory 'base_intercompany/static/src'
808=== added directory 'base_intercompany/static/src/img'
809=== added file 'base_intercompany/static/src/img/icon.png'
810Binary files base_intercompany/static/src/img/icon.png 1970-01-01 00:00:00 +0000 and base_intercompany/static/src/img/icon.png 2014-02-23 01:10:59 +0000 differ
811=== added directory 'base_intercompany/unit'
812=== added file 'base_intercompany/unit/__init__.py'
813--- base_intercompany/unit/__init__.py 1970-01-01 00:00:00 +0000
814+++ base_intercompany/unit/__init__.py 2014-02-23 01:10:59 +0000
815@@ -0,0 +1,26 @@
816+# -*- coding: utf-8 -*-
817+##############################################################################
818+#
819+# OpenERP, Open Source Management Solution
820+# Copyright (c) 2010-2014 Elico Corp. All Rights Reserved.
821+# Augustin Cisterne-Kaas <augustin.cisterne-kaas@elico-corp.com>
822+# Eric Caudal <eric.caudal@elico-corp.com>
823+#
824+# This program is free software: you can redistribute it and/or modify
825+# it under the terms of the GNU Affero General Public License as
826+# published by the Free Software Foundation, either version 3 of the
827+# License, or (at your option) any later version.
828+#
829+# This program is distributed in the hope that it will be useful,
830+# but WITHOUT ANY WARRANTY; without even the implied warranty of
831+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
832+# GNU Affero General Public License for more details.
833+#
834+# You should have received a copy of the GNU Affero General Public License
835+# along with this program. If not, see <http://www.gnu.org/licenses/>.
836+#
837+##############################################################################
838+
839+import binder
840+import export_synchronizer
841+import backend_adapter
842
843=== added file 'base_intercompany/unit/backend_adapter.py'
844--- base_intercompany/unit/backend_adapter.py 1970-01-01 00:00:00 +0000
845+++ base_intercompany/unit/backend_adapter.py 2014-02-23 01:10:59 +0000
846@@ -0,0 +1,151 @@
847+# -*- coding: utf-8 -*-
848+##############################################################################
849+#
850+# OpenERP, Open Source Management Solution
851+# Copyright (c) 2010-2014 Elico Corp. All Rights Reserved.
852+# Augustin Cisterne-Kaas <augustin.cisterne-kaas@elico-corp.com>
853+# Eric Caudal <eric.caudal@elico-corp.com>
854+
855+# This program is free software: you can redistribute it and/or modify
856+# it under the terms of the GNU Affero General Public License as
857+# published by the Free Software Foundation, either version 3 of the
858+# License, or (at your option) any later version.
859+#
860+# This program is distributed in the hope that it will be useful,
861+# but WITHOUT ANY WARRANTY; without even the implied warranty of
862+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
863+# GNU Affero General Public License for more details.
864+#
865+# You should have received a copy of the GNU Affero General Public License
866+# along with this program. If not, see <http://www.gnu.org/licenses/>.
867+#
868+##############################################################################
869+
870+import logging
871+from openerp.addons.connector.unit.backend_adapter import CRUDAdapter
872+
873+_logger = logging.getLogger(__name__)
874+recorder = {}
875+
876+
877+def call_to_key(method, arguments):
878+ """ Used to 'freeze' the method and arguments of a call to Coswin
879+ so they can be hashable; they will be stored in a dict.
880+
881+ Used in both the recorder and the tests.
882+ """
883+ def freeze(arg):
884+ if isinstance(arg, dict):
885+ items = dict((key, freeze(value)) for key, value
886+ in arg.iteritems())
887+ return frozenset(items.iteritems())
888+ elif isinstance(arg, list):
889+ return tuple([freeze(item) for item in arg])
890+ else:
891+ return arg
892+
893+ new_args = []
894+ for arg in arguments:
895+ new_args.append(freeze(arg))
896+ return (method, tuple(new_args))
897+
898+
899+def record(method, arguments, result):
900+ """ Utility function which can be used to record test data
901+ during synchronisations. Call it from CoswinOracleAdapter._call
902+
903+ Then ``output_recorder`` can be used to write the data recorded
904+ to a file.
905+ """
906+ recorder[call_to_key(method, arguments)] = result
907+
908+
909+def output_recorder(filename):
910+ import pprint
911+ with open(filename, 'w') as f:
912+ pprint.pprint(recorder, f)
913+ _logger.debug('recorder written to file %s', filename)
914+
915+
916+# class ICOPSLocation(object):
917+
918+# def __init__(self, location, db_name, username, password):
919+# self.location = location
920+# self.db_name = db_name
921+# self.username = username
922+# self.password = password
923+
924+
925+class GenericAdapter(CRUDAdapter):
926+
927+ """ External Records Adapter for Coswin """
928+
929+ def __init__(self, environment):
930+ """
931+
932+ :param environment: current environment (backend, session, ...)
933+ :type environment: :py:class:`connector.connector.Environment`
934+ """
935+ super(GenericAdapter, self).__init__(environment)
936+ # self.icops = ICOPSLocation(self.backend_record.icops_server,
937+ # self.backend_record.backup_server)
938+
939+ def search(self, filters=None):
940+ """ Search records according to some criterias
941+ and returns a list of ids """
942+ raise NotImplementedError
943+
944+ def read(self, id, attributes=None):
945+ """ Returns the information of a record """
946+ raise NotImplementedError
947+
948+ def search_read(self, filters=None):
949+ """ Search records according to some criterias
950+ and returns their information"""
951+ raise NotImplementedError
952+
953+ def create(self, data):
954+ """ Create a record on the external system """
955+ raise NotImplementedError
956+
957+ def write(self, id, data):
958+ """ Update records on the external system """
959+ raise NotImplementedError
960+
961+ def delete(self, id):
962+ """ Delete a record on the external system """
963+ raise NotImplementedError
964+
965+ def _write(self, text):
966+ return True
967+
968+
969+class ICOPSAdapter(GenericAdapter):
970+
971+ _model_name = None
972+
973+ def __init__(self, environment):
974+ super(GenericAdapter, self).__init__(environment)
975+ self._icops = None
976+ self._backend_to = None
977+
978+ def _get_pool(self):
979+ raise NotImplementedError
980+
981+ def create(self, data):
982+ sess = self.session
983+ pool = self._get_pool()
984+ return pool.create(
985+ sess.cr, self._backend_to.icops_uid.id, data, {'icops': True})
986+
987+ def write(self, id, data):
988+ sess = self.session
989+ pool = self._get_pool()
990+ pool.write(sess.cr, self._backend_to.icops_uid.id, id, data,
991+ {'icops': True})
992+
993+ def delete(self, id):
994+ sess = self.session
995+ pool = self._get_pool()
996+ pool.unlink(sess.cr, self._backend_to.icops_uid.id, [id],
997+ {'icops': True})
998
999=== added file 'base_intercompany/unit/binder.py'
1000--- base_intercompany/unit/binder.py 1970-01-01 00:00:00 +0000
1001+++ base_intercompany/unit/binder.py 2014-02-23 01:10:59 +0000
1002@@ -0,0 +1,122 @@
1003+# -*- coding: utf-8 -*-
1004+##############################################################################
1005+#
1006+# OpenERP, Open Source Management Solution
1007+# Copyright (c) 2010-2014 Elico Corp. All Rights Reserved.
1008+# Augustin Cisterne-Kaas <augustin.cisterne-kaas@elico-corp.com>
1009+# Eric Caudal <eric.caudal@elico-corp.com>
1010+
1011+# This program is free software: you can redistribute it and/or modify
1012+# it under the terms of the GNU Affero General Public License as
1013+# published by the Free Software Foundation, either version 3 of the
1014+# License, or (at your option) any later version.
1015+#
1016+# This program is distributed in the hope that it will be useful,
1017+# but WITHOUT ANY WARRANTY; without even the implied warranty of
1018+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
1019+# GNU Affero General Public License for more details.
1020+#
1021+# You should have received a copy of the GNU Affero General Public License
1022+# along with this program. If not, see <http://www.gnu.org/licenses/>.
1023+#
1024+##############################################################################
1025+
1026+from datetime import datetime
1027+from openerp.tools import DEFAULT_SERVER_DATETIME_FORMAT
1028+from openerp.addons.connector.connector import Binder
1029+from ..backend import icops
1030+
1031+
1032+class ICOPSBinder(Binder):
1033+ """ Generic Binder for ICOPS """
1034+
1035+
1036+@icops
1037+class ICOPSModelBinder(ICOPSBinder):
1038+ """
1039+ Bindings are done directly on the binding model.
1040+
1041+ Binding models are models called ``icops.{normal_model}``,
1042+ like ``icops.res.partner`` or ``icops.sale.order``.
1043+ They are ``_inherits`` of the normal models and contains
1044+ the ICOPS ID, the ID of the ICOPS Backend and the additional
1045+ fields belonging to the ICOPS instance.
1046+ """
1047+ _model_name = [
1048+ 'icops.sale.order',
1049+ 'icops.sale.order.line',
1050+ 'icops.purchase.order',
1051+ 'icops.purchase.order.line'
1052+ ]
1053+
1054+ def to_openerp(self, external_id, unwrap=False):
1055+ """ Give the OpenERP ID for an external ID
1056+
1057+ :param external_id: external ID for which we want the OpenERP ID
1058+ :param unwrap: if True, returns the openerp_id of the icops_xxxx
1059+ record, else return the id (binding id) of that record
1060+ :return: a record ID, depending on the value of unwrap,
1061+ or None if the external_id is not mapped
1062+ :rtype: int
1063+ """
1064+ binding_ids = self.session.search(
1065+ self.model._name,
1066+ [('icops_id', '=', external_id),
1067+ ('backend_id', '=', self.backend_record.id)])
1068+ if not binding_ids:
1069+ return None
1070+ assert len(binding_ids) == 1, "Several records found: %s" % binding_ids
1071+ binding_id = binding_ids[0]
1072+ if unwrap:
1073+ return self.session.read(self.model._name,
1074+ binding_id,
1075+ ['openerp_id'])['openerp_id'][0]
1076+ else:
1077+ return binding_id
1078+
1079+ def to_backend(self, binding_id):
1080+ """ Give the external ID for an OpenERP ID
1081+
1082+ :param binding_id: OpenERP ID for which we want the external id
1083+ :return: backend identifier of the record
1084+ """
1085+ record = self.session.browse(self.model._name,
1086+ binding_id)
1087+ assert record
1088+ res = {}
1089+ for icops in record.icops_ids:
1090+ key = '%s_%s' % (icops.backend_id.id, icops.concept)
1091+ res[key] = {'id': icops.record_id}
1092+ return res
1093+
1094+ def bind(self, records, binding_id):
1095+ """ Create the link between an external ID and an OpenERP ID and
1096+ update the last synchronization date.
1097+
1098+ :param external_ids: External ID to bind
1099+ :param binding_id: OpenERP ID to bind
1100+ :type binding_id: int
1101+ """
1102+ # avoid to trigger the export when we modify the `icops_id`
1103+ if not records:
1104+ return
1105+ context = self.session.context.copy()
1106+ context['icops'] = True
1107+ now_fmt = datetime.now().strftime(DEFAULT_SERVER_DATETIME_FORMAT)
1108+ icops_ids = []
1109+ for record, record_id in records.items():
1110+ backend_id, concept = record.split('_')
1111+
1112+ icops_id = (0, 0, {'record_id': record_id['id'],
1113+ 'backend_id': backend_id,
1114+ 'concept': concept,
1115+ 'binding_id': binding_id,
1116+ 'model': record_id['model']})
1117+ icops_ids.append(icops_id)
1118+
1119+ self.environment.model.write(
1120+ self.session.cr,
1121+ self.session.uid,
1122+ binding_id,
1123+ {'icops_ids': icops_ids, 'sync_date': now_fmt},
1124+ context=context)
1125
1126=== added file 'base_intercompany/unit/export_synchronizer.py'
1127--- base_intercompany/unit/export_synchronizer.py 1970-01-01 00:00:00 +0000
1128+++ base_intercompany/unit/export_synchronizer.py 2014-02-23 01:10:59 +0000
1129@@ -0,0 +1,258 @@
1130+# -*- coding: utf-8 -*-
1131+##############################################################################
1132+#
1133+# OpenERP, Open Source Management Solution
1134+# Copyright (c) 2010-2014 Elico Corp. All Rights Reserved.
1135+# Augustin Cisterne-Kaas <augustin.cisterne-kaas@elico-corp.com>
1136+# Eric Caudal <eric.caudal@elico-corp.com>
1137+
1138+# This program is free software: you can redistribute it and/or modify
1139+# it under the terms of the GNU Affero General Public License as
1140+# published by the Free Software Foundation, either version 3 of the
1141+# License, or (at your option) any later version.
1142+#
1143+# This program is distributed in the hope that it will be useful,
1144+# but WITHOUT ANY WARRANTY; without even the implied warranty of
1145+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
1146+# GNU Affero General Public License for more details.
1147+#
1148+# You should have received a copy of the GNU Affero General Public License
1149+# along with this program. If not, see <http://www.gnu.org/licenses/>.
1150+#
1151+##############################################################################
1152+
1153+import logging
1154+from openerp.tools.translate import _
1155+from openerp.addons.connector.queue.job import job
1156+from openerp.addons.connector.unit.synchronizer import ExportSynchronizer
1157+from ..connector import get_environment
1158+from openerp.addons.connector.exception import MappingError
1159+from osv import osv
1160+
1161+_logger = logging.getLogger(__name__)
1162+
1163+
1164+"""
1165+
1166+Exporters for ICOPS.
1167+
1168+In addition to its export job, an exporter has to:
1169+
1170+* check in ICOPS if the record has been updated more recently than the
1171+ last sync date and if yes, delay an import
1172+* call the ``bind`` method of the binder to update the last sync date
1173+
1174+"""
1175+
1176+
1177+class ICOPSBaseExporter(ExportSynchronizer):
1178+
1179+ """ Base exporter for ICOPS """
1180+
1181+ def __init__(self, environment):
1182+ """
1183+ :param environment: current environment (backend, session, ...)
1184+ :type environment: :py:class:`connector.connector.Environment`
1185+ """
1186+ super(ICOPSBaseExporter, self).__init__(environment)
1187+ self.binding_id = None
1188+ self.icops_ids = {}
1189+
1190+ def _get_openerp_data(self):
1191+ """ Return the raw OpenERP data for ``self.binding_id`` """
1192+ return self.session.browse(self.model._name, self.binding_id)
1193+
1194+ def run(self, binding_id, *args, **kwargs):
1195+ """ Run the synchronization
1196+
1197+ :param binding_id: identifier of the binding record to export
1198+ """
1199+ self.binding_id = binding_id
1200+ self.binding_record = self._get_openerp_data()
1201+
1202+ self.icops_ids = self.binder.to_backend(self.binding_id)
1203+ result = self._run(*args, **kwargs)
1204+ self.binder.bind(self.icops_ids, self.binding_id)
1205+ return result
1206+
1207+ def _run(self):
1208+ """ Flow of the synchronization, implemented in inherited classes"""
1209+ raise NotImplementedError
1210+
1211+
1212+class ICOPSExporter(ICOPSBaseExporter):
1213+
1214+ _concepts = None
1215+ """ A common flow for the exports to ICOPS """
1216+ def __init__(self, environment):
1217+ """
1218+ :param environment: current environment (backend, session, ...)
1219+ :type environment: :py:class:`connector.connector.Environment`
1220+ """
1221+ super(ICOPSExporter, self).__init__(environment)
1222+ self.binding_record = None
1223+
1224+ def _has_to_skip(self):
1225+ """ Return True if the export can be skipped """
1226+ return False
1227+
1228+ def _routing(self, record, fields=None):
1229+ fields = fields or []
1230+ icops = self.mapper._icops
1231+ icops_id = self._get_icops_id(
1232+ icops.backend_to.id, icops.concept)
1233+ if not icops_id:
1234+ return
1235+ if 'icops_delete' in fields:
1236+ self._delete(icops_id)
1237+ else:
1238+ self._custom_routing(icops_id, record, fields)
1239+
1240+ def _custom_routing(self, id, record, fields=None):
1241+ self._write(id, record)
1242+
1243+ def _run(self, fields=None):
1244+ """ Flow of the synchronization, implemented in inherited classes"""
1245+ assert self.binding_id
1246+ assert self.binding_record
1247+
1248+ if not self.icops_ids:
1249+ fields = None # should be created with all the fields
1250+
1251+ if self._has_to_skip():
1252+ return
1253+
1254+ nb_records = 0
1255+ icops_ids = {}
1256+
1257+ for icops in self._get_icops():
1258+ backend = self._get_backend_with_permission(icops)
1259+ self._set_icops(icops, backend)
1260+ map_record = self._map_data(fields=fields)
1261+ if self.icops_ids:
1262+ record = None
1263+ try:
1264+ record = self._update_data(map_record, fields=fields)
1265+ except MappingError as e:
1266+ continue
1267+ if not record:
1268+ continue
1269+ nb_records += 1
1270+ self._validate_data(record)
1271+ self._routing(record, fields)
1272+ else:
1273+ record = None
1274+ try:
1275+ record = self._create_data(map_record, fields=fields)
1276+ except MappingError as e:
1277+ continue
1278+ if not record:
1279+ continue
1280+ nb_records += 1
1281+ key = '%s_%s' % (icops.backend_to.id, icops.concept)
1282+ icops_ids[key] = {
1283+ 'id': self._create(record),
1284+ 'model': self.backend_adapter._get_pool()._name
1285+ }
1286+
1287+ self.icops_ids = icops_ids
1288+
1289+ if nb_records == 0:
1290+ return _('Nothing to export.')
1291+ return _('Record exported.')
1292+
1293+ def _get_backend_with_permission(self, icops):
1294+ sess = self.session
1295+ backend_pool = sess.pool.get('icops.backend')
1296+ return backend_pool.browse(
1297+ sess.cr, icops.icops_uid.id, icops.backend_to.id)
1298+
1299+ def _get_icops(self):
1300+ res = []
1301+ sess = self.session
1302+ user_pool = sess.pool.get('res.users')
1303+ user = user_pool.browse(sess.cr, sess.uid, sess.uid)
1304+ backend_pool = sess.pool.get('icops.backend')
1305+ backend_ids = backend_pool.search(
1306+ sess.cr, sess.uid, [('company_id', '=', user.company_id.id)])
1307+ if not backend_ids:
1308+ return res
1309+ intercompany_pool = sess.pool.get('res.intercompany')
1310+ intercompany_ids = intercompany_pool.search(
1311+ sess.cr, sess.uid,
1312+ [('backend_id', '=', backend_ids[0]),
1313+ ('concept', 'in', self._concepts),
1314+ ('model', '=', self.binding_record.openerp_id._name)])
1315+ res = intercompany_pool.browse(sess.cr, sess.uid, intercompany_ids)
1316+ return res
1317+
1318+ def _set_icops(self, icops, backend):
1319+ self.mapper._icops = icops
1320+ self.mapper._backend_to = backend
1321+ self.backend_adapter._icops = icops
1322+ self.backend_adapter._backend_to = backend
1323+
1324+ def _create(self, data):
1325+ if not self.backend_adapter._icops.on_create:
1326+ raise osv.except_osv('ICOPS Error', 'Can\'t create')
1327+ self._validate_data(data)
1328+ return self.backend_adapter.create(data)
1329+
1330+ def _write(self, id, data):
1331+ context = self.session.context or {}
1332+ if not self.backend_adapter._icops.on_write and not 'icops' in context:
1333+ raise osv.except_osv('ICOPS Error', 'Can\'t write')
1334+ self.backend_adapter.write(id, data)
1335+
1336+ def _confirm(self, id):
1337+ if not self.backend_adapter._icops.on_confirm:
1338+ raise osv.except_osv('ICOPS Error', 'Can\'t confirm')
1339+ self.backend_adapter.confirm(id)
1340+
1341+ def _cancel(self, id):
1342+ if not self.backend_adapter._icops.on_cancel:
1343+ raise osv.except_osv('ICOPS Error', 'Can\'t cancel')
1344+ self.backend_adapter.cancel(id)
1345+
1346+ def _delete(self, id):
1347+ if not self.backend_adapter._icops.on_unlink:
1348+ raise osv.except_osv('ICOPS Error', 'Can\'t delete')
1349+ self.backend_adapter.delete(id)
1350+
1351+ def _map_data(self, fields=None):
1352+ """ Convert the external record to OpenERP """
1353+ return self.mapper.map_record(self.binding_record)
1354+
1355+ def _create_data(self, map_record, fields=None, **kwargs):
1356+ """ Get the data to pass to :py:meth:`_create` """
1357+ return map_record.values(for_create=True, fields=fields, **kwargs)
1358+
1359+ def _update_data(self, map_record, fields=None, **kwargs):
1360+ """ Get the data to pass to :py:meth:`_update` """
1361+ return map_record.values(fields=fields, **kwargs)
1362+
1363+ def _validate_data(self, data):
1364+ """ Check if the values to export are correct
1365+
1366+ Pro-actively check before the ``Model.create`` or
1367+ ``Model.update`` if some fields are missing
1368+
1369+ Raise `InvalidDataError`
1370+ """
1371+ return
1372+
1373+ def _get_icops_id(self, backend_id, concept):
1374+ key = '%s_%s' % (backend_id, concept)
1375+ try:
1376+ return self.icops_ids[key]['id']
1377+ except:
1378+ return None
1379+
1380+
1381+@job
1382+def export_record(session, model_name, binding_id, fields=None):
1383+ """ Export a record on ICOPS """
1384+ record = session.browse(model_name, binding_id)
1385+ env = get_environment(session, model_name, record.backend_id.id)
1386+ exporter = env.get_connector_unit(ICOPSExporter)
1387+ return exporter.run(binding_id, fields=fields)
1388
1389=== added file 'base_intercompany/unit/mapper.py'
1390--- base_intercompany/unit/mapper.py 1970-01-01 00:00:00 +0000
1391+++ base_intercompany/unit/mapper.py 2014-02-23 01:10:59 +0000
1392@@ -0,0 +1,66 @@
1393+# -*- coding: utf-8 -*-
1394+##############################################################################
1395+#
1396+# OpenERP, Open Source Management Solution
1397+# Copyright (c) 2010-2014 Elico Corp. All Rights Reserved.
1398+# Augustin Cisterne-Kaas <augustin.cisterne-kaas@elico-corp.com>
1399+# Eric Caudal <eric.caudal@elico-corp.com>
1400+
1401+# This program is free software: you can redistribute it and/or modify
1402+# it under the terms of the GNU Affero General Public License as
1403+# published by the Free Software Foundation, either version 3 of the
1404+# License, or (at your option) any later version.
1405+#
1406+# This program is distributed in the hope that it will be useful,
1407+# but WITHOUT ANY WARRANTY; without even the implied warranty of
1408+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
1409+# GNU Affero General Public License for more details.
1410+#
1411+# You should have received a copy of the GNU Affero General Public License
1412+# along with this program. If not, see <http://www.gnu.org/licenses/>.
1413+#
1414+##############################################################################
1415+from openerp.addons.connector.unit.mapper import (ExportMapper, ExportMapChild)
1416+
1417+
1418+class ICOPSExportMapChild(ExportMapChild):
1419+ """ :py:class:`MapChild` for the Exports """
1420+
1421+ def _child_mapper(self):
1422+ mapper = self.get_connector_unit_for_model(
1423+ ExportMapper, self.model._name)
1424+ mapper._icops = self._icops
1425+ mapper._backend_to = self._backend_to
1426+ return mapper
1427+
1428+ def format_items(self, items_values):
1429+ items = super(ICOPSExportMapChild, self).format_items(items_values)
1430+ return [(5, 0)] + [(0, 0, data) for data in items]
1431+
1432+
1433+class ICOPSExportMapper(ExportMapper):
1434+ _map_child_class = ICOPSExportMapChild
1435+
1436+ def __init__(self, environment):
1437+ """
1438+
1439+ :param environment: current environment (backend, session, ...)
1440+ :type environment: :py:class:`connector.connector.Environment`
1441+ """
1442+ super(ICOPSExportMapper, self).__init__(environment)
1443+ self._icops = None
1444+ self._backend_to = None
1445+
1446+ def _get_map_child_unit(self, model_name):
1447+ mapper = super(ICOPSExportMapper, self)._get_map_child_unit(model_name)
1448+ mapper._icops = self._icops
1449+ mapper._backend_to = self._backend_to
1450+ return mapper
1451+
1452+ def _get_mapping(self, name, record):
1453+ res = {}
1454+ for method in dir(self):
1455+ if method.startswith('%s_' % name):
1456+ new_dict = getattr(self, method)(record)
1457+ res = dict(res.items() + new_dict.items())
1458+ return res
1459
1460=== added directory 'base_intercompany_sale'
1461=== added file 'base_intercompany_sale/__init__.py'
1462--- base_intercompany_sale/__init__.py 1970-01-01 00:00:00 +0000
1463+++ base_intercompany_sale/__init__.py 2014-02-23 01:10:59 +0000
1464@@ -0,0 +1,28 @@
1465+# -*- coding: utf-8 -*-
1466+##############################################################################
1467+#
1468+# OpenERP, Open Source Management Solution
1469+# Copyright (c) 2010-2014 Elico Corp. All Rights Reserved.
1470+# Augustin Cisterne-Kaas <augustin.cisterne-kaas@elico-corp.com>
1471+# Eric Caudal <eric.caudal@elico-corp.com>
1472+
1473+# This program is free software: you can redistribute it and/or modify
1474+# it under the terms of the GNU Affero General Public License as
1475+# published by the Free Software Foundation, either version 3 of the
1476+# License, or (at your option) any later version.
1477+#
1478+# This program is distributed in the hope that it will be useful,
1479+# but WITHOUT ANY WARRANTY; without even the implied warranty of
1480+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
1481+# GNU Affero General Public License for more details.
1482+#
1483+# You should have received a copy of the GNU Affero General Public License
1484+# along with this program. If not, see <http://www.gnu.org/licenses/>.
1485+#
1486+##############################################################################
1487+import connector
1488+import consumer
1489+import company
1490+import icops_model
1491+import sale
1492+import purchase
1493
1494=== added file 'base_intercompany_sale/__openerp__.py'
1495--- base_intercompany_sale/__openerp__.py 1970-01-01 00:00:00 +0000
1496+++ base_intercompany_sale/__openerp__.py 2014-02-23 01:10:59 +0000
1497@@ -0,0 +1,50 @@
1498+# -*- coding: utf-8 -*-
1499+##############################################################################
1500+#
1501+# OpenERP, Open Source Management Solution
1502+# Copyright (c) 2010-2014 Elico Corp. All Rights Reserved.
1503+# Augustin Cisterne-Kaas <augustin.cisterne-kaas@elico-corp.com>
1504+# Eric Caudal <eric.caudal@elico-corp.com>
1505+
1506+# This program is free software: you can redistribute it and/or modify
1507+# it under the terms of the GNU Affero General Public License as
1508+# published by the Free Software Foundation, either version 3 of the
1509+# License, or (at your option) any later version.
1510+#
1511+# This program is distributed in the hope that it will be useful,
1512+# but WITHOUT ANY WARRANTY; without even the implied warranty of
1513+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
1514+# GNU Affero General Public License for more details.
1515+#
1516+# You should have received a copy of the GNU Affero General Public License
1517+# along with this program. If not, see <http://www.gnu.org/licenses/>.
1518+#
1519+##############################################################################
1520+
1521+{'name': 'Base Intercompany Sale',
1522+ 'version': '0.4',
1523+ 'category': 'Sales Management',
1524+ 'depends': ['base_intercompany', 'sale', 'purchase'],
1525+ 'author': 'Elico Corp',
1526+ 'license': 'AGPL-3',
1527+ 'website': 'https://www.elico-corp.com',
1528+ 'description': """
1529+This module is an extension designed to manage Inter-company Process (ICOPS)
1530+and allows 2 companies to create Sale Orders and Purchase Order in each other.
1531+ - Sale Order to Purchase Order (so2po)
1532+ - Sale Order to Sale Order (so2so)
1533+ - Purchase Order to Sale Order (po2so)
1534+ - Handles the following events: Create, Update, Delete, Confirm and Cancel
1535+
1536+TODO: demo data to be improved.\n
1537+Blueprint: https://blueprints.launchpad.net/multi-company/+spec/icops
1538+""",
1539+ 'images': [],
1540+ 'demo': ['base_intercompany_sale_demo.xml'],
1541+ 'data': ['security/ir.model.access.csv',
1542+ 'icops_model_view.xml',
1543+ 'sale_view.xml',
1544+ 'purchase_view.xml'],
1545+ 'installable': True,
1546+ 'application': False,
1547+ }
1548
1549=== added file 'base_intercompany_sale/base_intercompany_sale_demo.xml'
1550--- base_intercompany_sale/base_intercompany_sale_demo.xml 1970-01-01 00:00:00 +0000
1551+++ base_intercompany_sale/base_intercompany_sale_demo.xml 2014-02-23 01:10:59 +0000
1552@@ -0,0 +1,53 @@
1553+<?xml version="1.0" encoding="utf-8"?>
1554+<openerp>
1555+ <data noupdate="1">
1556+
1557+ <record id="base_intercompany.user_origin" model="res.users">
1558+ <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'))]"/>
1559+ </record>
1560+
1561+ <record id="base_intercompany.user_destination" model="res.users">
1562+ <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'))]"/>
1563+ </record>
1564+
1565+ <record id="warehouse_origin" model="stock.warehouse">
1566+ <field name="name">Warehouse Origin</field>
1567+ <field name="company_id" ref="base_intercompany.company_origin" />
1568+ </record>
1569+
1570+ <record id="warehouse_destination" model="stock.warehouse">
1571+ <field name="name">Warehouse Destination</field>
1572+ <field name="company_id" ref="base_intercompany.company_destination" />
1573+ </record>
1574+
1575+ <record id="shop_origin" model="sale.shop">
1576+ <field name="name">Shop Origin</field>
1577+ <field name="warehouse_id" ref="warehouse_origin" />
1578+ <field name="payment_default_id" ref="account.account_payment_term_immediate" />
1579+ <field name="company_id" ref="base_intercompany.company_origin" />
1580+ </record>
1581+
1582+ <record id="shop_destination" model="sale.shop">
1583+ <field name="name">Shop Destination</field>
1584+ <field name="warehouse_id" ref="warehouse_destination" />
1585+ <field name="payment_default_id" ref="account.account_payment_term_immediate" />
1586+ <field name="company_id" ref="base_intercompany.company_destination" />
1587+ </record>
1588+
1589+ <!-- ICOPS Setup -->
1590+ <record id="base_intercompany.backend_origin" model="icops.backend">
1591+ <field name="icops_shop_id" ref="shop_origin" />
1592+ <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})]" />
1593+ </record>
1594+
1595+ <record id="base_intercompany.backend_destination" model="icops.backend">
1596+ <field name="icops_shop_id" ref="shop_destination" />
1597+ </record>
1598+
1599+ <!-- Remove company_id from stock -->
1600+ <function model="stock.location" name="write">
1601+ <function eval="[[]]" model="stock.location" name="search"/>
1602+ <value eval="{'company_id': None}" />
1603+ </function>
1604+ </data>
1605+</openerp>
1606\ No newline at end of file
1607
1608=== added file 'base_intercompany_sale/company.py'
1609--- base_intercompany_sale/company.py 1970-01-01 00:00:00 +0000
1610+++ base_intercompany_sale/company.py 2014-02-23 01:10:59 +0000
1611@@ -0,0 +1,56 @@
1612+ # -*- coding: utf-8 -*-
1613+##############################################################################
1614+#
1615+# OpenERP, Open Source Management Solution
1616+# Copyright (c) 2010-2014 Elico Corp. All Rights Reserved.
1617+# Augustin Cisterne-Kaas <augustin.cisterne-kaas@elico-corp.com>
1618+# Eric Caudal <eric.caudal@elico-corp.com>
1619+
1620+# This program is free software: you can redistribute it and/or modify
1621+# it under the terms of the GNU Affero General Public License as
1622+# published by the Free Software Foundation, either version 3 of the
1623+# License, or (at your option) any later version.
1624+#
1625+# This program is distributed in the hope that it will be useful,
1626+# but WITHOUT ANY WARRANTY; without even the implied warranty of
1627+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
1628+# GNU Affero General Public License for more details.
1629+#
1630+# You should have received a copy of the GNU Affero General Public License
1631+# along with this program. If not, see <http://www.gnu.org/licenses/>.
1632+#
1633+##############################################################################
1634+from openerp.osv import fields, orm
1635+
1636+
1637+class res_intercompany(orm.Model):
1638+ _inherit = 'res.intercompany'
1639+
1640+ def _select_concepts(self, cr, uid, context=None):
1641+ """ Available concepts
1642+
1643+ Can be inherited to add custom versions.
1644+ """
1645+ res = super(res_intercompany, self)._select_concepts(cr, uid, context)
1646+ res += [('so2po', 'SO to PO'),
1647+ ('so2so', 'SO to SO'),
1648+ ('po2so', 'PO to SO')]
1649+ return res
1650+
1651+ def _select_models(self, cr, uid, context=None):
1652+ """ Available Object names
1653+
1654+ Can be inherited to add custom versions.
1655+ """
1656+ res = super(res_intercompany, self)._select_models(
1657+ cr, uid, context)
1658+ new_dict = {'so2po': 'sale.order',
1659+ 'so2so': 'sale.order',
1660+ 'po2so': 'purchase.order'}
1661+ res = dict(res.items() + new_dict.items())
1662+ return res
1663+
1664+ _columns = {
1665+ 'concept': fields.selection(_select_concepts, string="Concept",
1666+ required=True),
1667+ }
1668
1669=== added file 'base_intercompany_sale/connector.py'
1670--- base_intercompany_sale/connector.py 1970-01-01 00:00:00 +0000
1671+++ base_intercompany_sale/connector.py 2014-02-23 01:10:59 +0000
1672@@ -0,0 +1,32 @@
1673+# -*- coding: utf-8 -*-
1674+##############################################################################
1675+#
1676+# OpenERP, Open Source Management Solution
1677+# Copyright (c) 2010-2014 Elico Corp. All Rights Reserved.
1678+# Augustin Cisterne-Kaas <augustin.cisterne-kaas@elico-corp.com>
1679+# Eric Caudal <eric.caudal@elico-corp.com>
1680+
1681+# This program is free software: you can redistribute it and/or modify
1682+# it under the terms of the GNU Affero General Public License as
1683+# published by the Free Software Foundation, either version 3 of the
1684+# License, or (at your option) any later version.
1685+#
1686+# This program is distributed in the hope that it will be useful,
1687+# but WITHOUT ANY WARRANTY; without even the implied warranty of
1688+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
1689+# GNU Affero General Public License for more details.
1690+#
1691+# You should have received a copy of the GNU Affero General Public License
1692+# along with this program. If not, see <http://www.gnu.org/licenses/>.
1693+#
1694+##############################################################################
1695+from openerp.osv import orm
1696+
1697+
1698+class base_intercompany_sale_installed(orm.AbstractModel):
1699+ """Empty model used to know if the module is installed on the
1700+ database.
1701+
1702+ If the model is in the registry, the module is installed.
1703+ """
1704+ _name = 'base_intercompany_sale.installed'
1705
1706=== added file 'base_intercompany_sale/consumer.py'
1707--- base_intercompany_sale/consumer.py 1970-01-01 00:00:00 +0000
1708+++ base_intercompany_sale/consumer.py 2014-02-23 01:10:59 +0000
1709@@ -0,0 +1,79 @@
1710+# -*- coding: utf-8 -*-
1711+##############################################################################
1712+#
1713+# OpenERP, Open Source Management Solution
1714+# Copyright (c) 2010-2014 Elico Corp. All Rights Reserved.
1715+# Augustin Cisterne-Kaas <augustin.cisterne-kaas@elico-corp.com>
1716+# Eric Caudal <eric.caudal@elico-corp.com>
1717+
1718+# This program is free software: you can redistribute it and/or modify
1719+# it under the terms of the GNU Affero General Public License as
1720+# published by the Free Software Foundation, either version 3 of the
1721+# License, or (at your option) any later version.
1722+#
1723+# This program is distributed in the hope that it will be useful,
1724+# but WITHOUT ANY WARRANTY; without even the implied warranty of
1725+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
1726+# GNU Affero General Public License for more details.
1727+#
1728+# You should have received a copy of the GNU Affero General Public License
1729+# along with this program. If not, see <http://www.gnu.org/licenses/>.
1730+#
1731+##############################################################################
1732+from openerp.addons.connector.event import (on_record_write,
1733+ on_record_create,
1734+ on_record_unlink
1735+ )
1736+from openerp.addons.base_intercompany.unit.export_synchronizer import (
1737+ export_record)
1738+
1739+_MODEL_NAMES = ('sale.order', 'sale.order.line',
1740+ 'purchase.order', 'purchase.order.line')
1741+_BIND_MODEL_NAMES = ('icops.sale.order', 'icops.sale.order.line',
1742+ 'icops.purchase.order', 'icops.purchase.order.line')
1743+_UNLINK_MODEL_NAMES = ('sale.order', 'purchase.order')
1744+_UNLINK_BIND_MODEL_NAMES = ('icops.sale.order', 'icops.purchase.order')
1745+
1746+
1747+@on_record_create(model_names=_BIND_MODEL_NAMES)
1748+@on_record_write(model_names=_BIND_MODEL_NAMES)
1749+def delay_export(session, model_name, record_id, fields=None):
1750+ """ Delay a job which export a binding record.
1751+
1752+ (A binding record being a ``icops.res.partner``,
1753+ ``icops.sale.order``, ...)
1754+ """
1755+ export_record(session, model_name, record_id, fields=fields)
1756+
1757+
1758+@on_record_write(model_names=_MODEL_NAMES)
1759+def delay_export_all_bindings(session, model_name, record_id, fields=None):
1760+ """ Delay a job which export all the bindings of a record.
1761+
1762+ In this case, it is called on records of normal models and will delay
1763+ the export for all the bindings.
1764+ """
1765+ model = session.pool.get(model_name)
1766+ record = model.browse(session.cr, session.uid,
1767+ record_id, context=session.context)
1768+ for binding in record.icops_bind_ids:
1769+ export_record(session, binding._model._name, binding.id,
1770+ fields=fields)
1771+
1772+
1773+@on_record_unlink(model_names=_UNLINK_MODEL_NAMES)
1774+def delay_unlink(session, model_name, record_id):
1775+ """ Delay a job which delete a record on Magento.
1776+
1777+ Called on binding records."""
1778+ fields = {'icops_delete': True}
1779+ delay_export_all_bindings(session, model_name, record_id, fields)
1780+
1781+
1782+@on_record_unlink(model_names=_UNLINK_BIND_MODEL_NAMES)
1783+def delay_unlink_binding(session, model_name, record_id):
1784+ """ Delay a job which delete a record on Magento.
1785+
1786+ Called on binding records."""
1787+ fields = {'icops_delete': True}
1788+ delay_export(session, model_name, record_id, fields)
1789
1790=== added file 'base_intercompany_sale/icops_model.py'
1791--- base_intercompany_sale/icops_model.py 1970-01-01 00:00:00 +0000
1792+++ base_intercompany_sale/icops_model.py 2014-02-23 01:10:59 +0000
1793@@ -0,0 +1,33 @@
1794+# -*- coding: utf-8 -*-
1795+##############################################################################
1796+#
1797+# OpenERP, Open Source Management Solution
1798+# Copyright (c) 2010-2014 Elico Corp. All Rights Reserved.
1799+# Augustin Cisterne-Kaas <augustin.cisterne-kaas@elico-corp.com>
1800+# Eric Caudal <eric.caudal@elico-corp.com>
1801+
1802+# This program is free software: you can redistribute it and/or modify
1803+# it under the terms of the GNU Affero General Public License as
1804+# published by the Free Software Foundation, either version 3 of the
1805+# License, or (at your option) any later version.
1806+#
1807+# This program is distributed in the hope that it will be useful,
1808+# but WITHOUT ANY WARRANTY; without even the implied warranty of
1809+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
1810+# GNU Affero General Public License for more details.
1811+#
1812+# You should have received a copy of the GNU Affero General Public License
1813+# along with this program. If not, see <http://www.gnu.org/licenses/>.
1814+#
1815+##############################################################################
1816+from openerp.osv import fields, orm
1817+
1818+
1819+class icops_backend(orm.Model):
1820+ _inherit = 'icops.backend'
1821+
1822+ _columns = {
1823+ 'icops_shop_id': fields.many2one(
1824+ 'sale.shop', 'IC Default location',
1825+ required=True),
1826+ }
1827
1828=== added file 'base_intercompany_sale/icops_model_view.xml'
1829--- base_intercompany_sale/icops_model_view.xml 1970-01-01 00:00:00 +0000
1830+++ base_intercompany_sale/icops_model_view.xml 2014-02-23 01:10:59 +0000
1831@@ -0,0 +1,15 @@
1832+<?xml version="1.0" encoding="UTF-8"?>
1833+<openerp>
1834+ <data>
1835+ <record id="view_icops_backend_form" model="ir.ui.view">
1836+ <field name="name">icops.backend.form</field>
1837+ <field name="model">icops.backend</field>
1838+ <field name="inherit_id" ref="base_intercompany.view_icops_backend_form" />
1839+ <field name="arch" type="xml">
1840+ <field name="icops_uid" position="after">
1841+ <field name="icops_shop_id" cols="2" />
1842+ </field>
1843+ </field>
1844+ </record>
1845+ </data>
1846+</openerp>
1847\ No newline at end of file
1848
1849=== added file 'base_intercompany_sale/purchase.py'
1850--- base_intercompany_sale/purchase.py 1970-01-01 00:00:00 +0000
1851+++ base_intercompany_sale/purchase.py 2014-02-23 01:10:59 +0000
1852@@ -0,0 +1,296 @@
1853+# -*- coding: utf-8 -*-
1854+##############################################################################
1855+#
1856+# OpenERP, Open Source Management Solution
1857+# Copyright (c) 2010-2014 Elico Corp. All Rights Reserved.
1858+# Augustin Cisterne-Kaas <augustin.cisterne-kaas@elico-corp.com>
1859+# Eric Caudal <eric.caudal@elico-corp.com>
1860+
1861+# This program is free software: you can redistribute it and/or modify
1862+# it under the terms of the GNU Affero General Public License as
1863+# published by the Free Software Foundation, either version 3 of the
1864+# License, or (at your option) any later version.
1865+#
1866+# This program is distributed in the hope that it will be useful,
1867+# but WITHOUT ANY WARRANTY; without even the implied warranty of
1868+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
1869+# GNU Affero General Public License for more details.
1870+#
1871+# You should have received a copy of the GNU Affero General Public License
1872+# along with this program. If not, see <http://www.gnu.org/licenses/>.
1873+#
1874+##############################################################################
1875+from openerp.osv import fields, orm
1876+from openerp.addons.base_intercompany.backend import icops
1877+from openerp.addons.base_intercompany.unit.export_synchronizer import (
1878+ ICOPSExporter)
1879+from openerp.addons.base_intercompany.unit.mapper import ICOPSExportMapper
1880+from openerp.addons.connector.unit.mapper import mapping
1881+from openerp.addons.base_intercompany.unit.backend_adapter import (
1882+ ICOPSAdapter)
1883+from openerp.addons.connector.exception import MappingError
1884+
1885+
1886+class purchase_order(orm.Model):
1887+ _name = 'purchase.order'
1888+ _inherit = ['purchase.order', 'icops.model']
1889+
1890+ _columns = {
1891+ 'openerp_id': fields.many2one('purchase.order',
1892+ string='Sale Order',
1893+ required=True,
1894+ ondelete='cascade'),
1895+ 'icops_bind_ids': fields.one2many(
1896+ 'icops.purchase.order', 'openerp_id',
1897+ string="ICOPS Bindings"),
1898+ }
1899+
1900+ def copy_data(self, cr, uid, id, default=None, context=None):
1901+ if default is None:
1902+ default = {}
1903+ default['icops_bind_ids'] = False
1904+ return super(purchase_order, self).copy_data(cr, uid, id,
1905+ default=default,
1906+ context=context)
1907+
1908+ def create(self, cr, uid, data, context=None):
1909+ data['icops_bind_ids'] = self.pool.get(
1910+ 'icops.backend').prepare_binding(cr, uid, data, context)
1911+ return super(purchase_order, self).create(cr, uid, data, context)
1912+
1913+ def write(self, cr, uid, ids, data, context=None):
1914+ self._check_icops(cr, uid, ids, context=context)
1915+ return super(purchase_order, self).write(cr, uid, ids, data, context)
1916+
1917+ def unlink(self, cr, uid, ids, context=None):
1918+ self._check_icops(cr, uid, ids, context=context)
1919+ return super(purchase_order, self).unlink(cr, uid, ids, context)
1920+
1921+
1922+class icops_purchase_order(orm.Model):
1923+ _name = 'icops.purchase.order'
1924+ _inherit = 'icops.binding'
1925+ _inherits = {'purchase.order': 'openerp_id'}
1926+ _description = 'ICOPS Purchase Order'
1927+
1928+ _columns = {
1929+ 'openerp_id': fields.many2one('purchase.order',
1930+ string='Product',
1931+ required=True,
1932+ ondelete='cascade'),
1933+ 'backend_id': fields.many2one('icops.backend',
1934+ string='ICOPS Backend'),
1935+ 'icops_order_line_ids': fields.one2many('icops.purchase.order.line',
1936+ 'icops_order_id',
1937+ 'ICOPS Order Lines'),
1938+ }
1939+
1940+
1941+class purchase_order_line(orm.Model):
1942+ _inherit = 'purchase.order.line'
1943+ _columns = {
1944+ 'icops_bind_ids': fields.one2many(
1945+ 'icops.purchase.order.line', 'openerp_id',
1946+ string="ICOPS Bindings"),
1947+ }
1948+
1949+ def copy_data(self, cr, uid, id, default=None, context=None):
1950+ if default is None:
1951+ default = {}
1952+ default['icops_bind_ids'] = False
1953+ return super(purchase_order_line, self).copy_data(cr, uid, id,
1954+ default=default,
1955+ context=context)
1956+
1957+
1958+class icops_purchase_order_line(orm.Model):
1959+ _name = 'icops.purchase.order.line'
1960+ _inherit = 'icops.binding'
1961+ _description = 'ICOPS Sale Order Line'
1962+ _inherits = {'purchase.order.line': 'openerp_id'}
1963+
1964+ def _get_lines_from_order(self, cr, uid, ids, context=None):
1965+ line_obj = self.pool.get('icops.purchase.order.line')
1966+ return line_obj.search(cr, uid,
1967+ [('icops_order_id', 'in', ids)],
1968+ context=context)
1969+ _columns = {
1970+ 'icops_order_id': fields.many2one('icops.purchase.order',
1971+ 'ICOPS Sale Order',
1972+ required=True,
1973+ ondelete='cascade',
1974+ select=True),
1975+ 'openerp_id': fields.many2one('purchase.order.line',
1976+ string='Sale Order Line',
1977+ required=True,
1978+ ondelete='cascade'),
1979+ 'backend_id': fields.related(
1980+ 'icops_order_id', 'backend_id',
1981+ type='many2one',
1982+ relation='icops.backend',
1983+ string='ICOPS Backend',
1984+ store={'icops.purchase.order.line':
1985+ (lambda self, cr, uid, ids, c=None: ids,
1986+ ['icops_order_id'],
1987+ 10),
1988+ 'icops.purchase.order':
1989+ (_get_lines_from_order, ['backend_id'], 20),
1990+ },
1991+ readonly=True)
1992+ }
1993+
1994+ def create(self, cr, uid, vals, context=None):
1995+ icops_order_id = vals['icops_order_id']
1996+ info = self.pool['icops.purchase.order'].read(cr, uid,
1997+ [icops_order_id],
1998+ ['openerp_id'],
1999+ context=context)
2000+ order_id = info[0]['openerp_id']
2001+ vals['order_id'] = order_id[0]
2002+ return super(icops_purchase_order_line, self).create(cr, uid, vals,
2003+ context=context)
2004+
2005+
2006+@icops
2007+class PurchaseOrderAdapter(ICOPSAdapter):
2008+ _model_name = 'icops.purchase.order'
2009+
2010+ def _get_pool(self):
2011+ sess = self.session
2012+ return sess.pool.get('sale.order')
2013+
2014+ def confirm(self, id):
2015+ sess = self.session
2016+ pool = self._get_pool()
2017+ context = {'icops': True}
2018+ pool.write(
2019+ sess.cr, self._backend_to.icops_uid.id, [id],
2020+ {'temp_unlock': True}, context=context)
2021+ pool.action_wait(
2022+ sess.cr, self._backend_to.icops_uid.id, [id])
2023+ pool.write(
2024+ sess.cr, self._backend_to.icops_uid.id, [id],
2025+ {'temp_unlock': False}, context=context)
2026+
2027+ def cancel(self, id):
2028+ sess = self.session
2029+ pool = self._get_pool()
2030+ context = {'icops': True}
2031+ pool.write(
2032+ sess.cr, self._backend_to.icops_uid.id, [id],
2033+ {'temp_unlock': True}, context=context)
2034+ pool.action_cancel(sess.cr, self._backend_to.icops_uid.id, [id])
2035+ pool.write(
2036+ sess.cr, self._backend_to.icops_uid.id, [id],
2037+ {'temp_unlock': False}, context=context)
2038+
2039+
2040+@icops
2041+class PurchaseOrderExport(ICOPSExporter):
2042+ _model_name = ['icops.purchase.order']
2043+ _concepts = ['po2so']
2044+
2045+ def _custom_routing(self, id, record, fields=None):
2046+ if 'state' in fields:
2047+ state = record.pop('state')
2048+ if state == 'cancel':
2049+ self._cancel(id)
2050+ elif state == 'progress':
2051+ self._confirm(id)
2052+ elif fields:
2053+ self._write(id, record)
2054+
2055+
2056+@icops
2057+class PurchaseOrderExportMapper(ICOPSExportMapper):
2058+ _model_name = 'icops.purchase.order'
2059+
2060+ children = [
2061+ ('order_line', 'order_line', 'icops.purchase.order.line')
2062+ ]
2063+
2064+ @mapping
2065+ def origin(self, record):
2066+ return {'origin': 'ICOPS: %s' % record.name}
2067+
2068+ @mapping
2069+ def state(self, record):
2070+ state = record.state
2071+ if record.state == 'approved':
2072+ state = 'progress'
2073+ return {'state': state}
2074+
2075+ @mapping
2076+ def address(self, record):
2077+ sess = self.session
2078+ partner = record.company_id.partner_id
2079+ partner_pool = sess.pool.get('res.partner')
2080+ addr = partner_pool.address_get(sess.cr, sess.uid, [partner.id],
2081+ ['delivery', 'invoice', 'contact'])
2082+ return {
2083+ 'partner_invoice_id': addr['invoice'],
2084+ 'partner_shipping_id': addr['delivery']
2085+ }
2086+
2087+ @mapping
2088+ def icops(self, record):
2089+ if not self._backend_to:
2090+ raise MappingError("Could not find an ICOPS backend")
2091+ sess = self.session
2092+ backend = self._backend_to
2093+ ic_uid = backend.icops_uid.id
2094+ company = backend.company_id
2095+ partner_pool = sess.pool.get('res.partner')
2096+ partner_id = record.company_id.partner_id.id
2097+ partner = partner_pool.browse(sess.cr, ic_uid, partner_id)
2098+ pricelist = (partner.property_product_pricelist.id
2099+ if partner.property_product_pricelist
2100+ else False)
2101+ fiscal_position = partner.property_account_position
2102+ payment_term = partner.property_payment_term
2103+ shop = self._backend_to.icops_shop_id
2104+ if company.partner_id.id != record.partner_id.id:
2105+ raise MappingError("Wrong partner")
2106+ return {
2107+ 'company_id': company.id,
2108+ 'partner_id': partner.id,
2109+ 'pricelist_id': pricelist,
2110+ 'fiscal_position': fiscal_position.id,
2111+ 'payment_term': payment_term.id,
2112+ 'user_id': ic_uid,
2113+ 'shop_id': shop.id
2114+ }
2115+
2116+
2117+@icops
2118+class PurchaseOrderLineExportMapper(ICOPSExportMapper):
2119+ _model_name = 'icops.purchase.order.line'
2120+
2121+ @mapping
2122+ def product(self, record):
2123+ return {
2124+ 'name': record.name,
2125+ 'product_id': record.product_id.id,
2126+ 'product_uom': record.product_uom.id,
2127+ 'product_uom_qty': record.product_qty
2128+ }
2129+
2130+ @mapping
2131+ def price(self, record):
2132+ if not record.product_id:
2133+ return {'price_unit': 0}
2134+ sess = self.session
2135+ backend = self._backend_to
2136+ ic_uid = backend.icops_uid.id
2137+ partner_pool = sess.pool.get('res.partner')
2138+ partner_id = record.order_id.company_id.partner_id.id
2139+ partner = partner_pool.browse(sess.cr, ic_uid, partner_id)
2140+ pricelist_id = (partner.property_product_pricelist.id
2141+ if partner.property_product_pricelist
2142+ else False)
2143+ pricelist_pool = sess.pool.get('product.pricelist')
2144+ price_unit = pricelist_pool.price_get(
2145+ sess.cr, ic_uid, [pricelist_id],
2146+ record.product_id.id, record.product_qty)
2147+ price = price_unit[int(pricelist_id)]
2148+ return {'price_unit': price}
2149
2150=== added file 'base_intercompany_sale/purchase_view.xml'
2151--- base_intercompany_sale/purchase_view.xml 1970-01-01 00:00:00 +0000
2152+++ base_intercompany_sale/purchase_view.xml 2014-02-23 01:10:59 +0000
2153@@ -0,0 +1,26 @@
2154+<?xml version="1.0" encoding="UTF-8"?>
2155+<openerp>
2156+ <data>
2157+ <record id="purchase_order_form" model="ir.ui.view">
2158+ <field name="name">purchase.order.form</field>
2159+ <field name="model">purchase.order</field>
2160+ <field name="inherit_id" ref="purchase.purchase_order_form" />
2161+ <field name="arch" type="xml">
2162+ <xpath expr="//header/button[last()]" position="after">
2163+ <button name="action_unlock" string="Unlock" type="object" attrs="{'invisible':[('locked', '=', False)]}" class="oe_highlight" />
2164+ </xpath>
2165+ <xpath expr="//page[last()]" position="after">
2166+ <page string="ICOPS">
2167+ <field name="locked" invisible="1" />
2168+ <field name="icops_bind_ids">
2169+ <tree string="ICOPS Backend" editable="bottom">
2170+ <field name="backend_id" />
2171+ <field name="icops_ids" />
2172+ </tree>
2173+ </field>
2174+ </page>
2175+ </xpath>
2176+ </field>
2177+ </record>
2178+ </data>
2179+</openerp>
2180\ No newline at end of file
2181
2182=== added file 'base_intercompany_sale/sale.py'
2183--- base_intercompany_sale/sale.py 1970-01-01 00:00:00 +0000
2184+++ base_intercompany_sale/sale.py 2014-02-23 01:10:59 +0000
2185@@ -0,0 +1,390 @@
2186+# -*- coding: utf-8 -*-
2187+##############################################################################
2188+#
2189+# OpenERP, Open Source Management Solution
2190+# Copyright (c) 2010-2014 Elico Corp. All Rights Reserved.
2191+# Augustin Cisterne-Kaas <augustin.cisterne-kaas@elico-corp.com>
2192+# Eric Caudal <eric.caudal@elico-corp.com>
2193+
2194+# This program is free software: you can redistribute it and/or modify
2195+# it under the terms of the GNU Affero General Public License as
2196+# published by the Free Software Foundation, either version 3 of the
2197+# License, or (at your option) any later version.
2198+#
2199+# This program is distributed in the hope that it will be useful,
2200+# but WITHOUT ANY WARRANTY; without even the implied warranty of
2201+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
2202+# GNU Affero General Public License for more details.
2203+#
2204+# You should have received a copy of the GNU Affero General Public License
2205+# along with this program. If not, see <http://www.gnu.org/licenses/>.
2206+#
2207+##############################################################################
2208+from openerp.osv import fields, orm
2209+from openerp.addons.base_intercompany.backend import icops
2210+from openerp.addons.base_intercompany.unit.export_synchronizer import (
2211+ ICOPSExporter)
2212+from openerp.addons.base_intercompany.unit.mapper import ICOPSExportMapper
2213+from openerp.addons.connector.unit.mapper import mapping
2214+from openerp.addons.connector.exception import MappingError
2215+from openerp.addons.base_intercompany.unit.backend_adapter import ICOPSAdapter
2216+
2217+
2218+class sale_order(orm.Model):
2219+ _name = 'sale.order'
2220+ _inherit = ['sale.order', 'icops.model']
2221+
2222+ _columns = {
2223+ 'openerp_id': fields.many2one('sale.order',
2224+ string='Sale Order',
2225+ required=True,
2226+ ondelete='cascade'),
2227+ 'icops_bind_ids': fields.one2many(
2228+ 'icops.sale.order', 'openerp_id',
2229+ string="ICOPS Bindings"),
2230+ }
2231+
2232+ def copy_data(self, cr, uid, id, default=None, context=None):
2233+ if default is None:
2234+ default = {}
2235+ default['icops_bind_ids'] = False
2236+ return super(sale_order, self).copy_data(cr, uid, id,
2237+ default=default,
2238+ context=context)
2239+
2240+ def create(self, cr, uid, data, context=None):
2241+ data['icops_bind_ids'] = self.pool.get(
2242+ 'icops.backend').prepare_binding(cr, uid, data, context)
2243+ return super(sale_order, self).create(cr, uid, data, context)
2244+
2245+ def write(self, cr, uid, ids, data, context=None):
2246+ self._check_icops(cr, uid, ids, context=context)
2247+ return super(sale_order, self).write(cr, uid, ids, data, context)
2248+
2249+ def unlink(self, cr, uid, ids, context=None):
2250+ self._check_icops(cr, uid, ids, context=context)
2251+ return super(sale_order, self).unlink(cr, uid, ids, context)
2252+
2253+
2254+class icops_sale_order(orm.Model):
2255+ _name = 'icops.sale.order'
2256+ _inherit = 'icops.binding'
2257+ _inherits = {'sale.order': 'openerp_id'}
2258+ _description = 'ICOPS Sale Order'
2259+
2260+ _columns = {
2261+ 'openerp_id': fields.many2one('sale.order',
2262+ string='SaleOrder',
2263+ required=True,
2264+ ondelete='cascade'),
2265+ 'backend_id': fields.many2one('icops.backend',
2266+ string='ICOPS Backend'),
2267+ 'icops_order_line_ids': fields.one2many('icops.sale.order.line',
2268+ 'icops_order_id',
2269+ 'ICOPS Order Lines'),
2270+
2271+ }
2272+
2273+
2274+class sale_order_line(orm.Model):
2275+ _inherit = 'sale.order.line'
2276+ _columns = {
2277+ 'icops_bind_ids': fields.one2many(
2278+ 'icops.sale.order.line', 'openerp_id',
2279+ string="ICOPS Bindings"),
2280+ }
2281+
2282+ def copy_data(self, cr, uid, id, default=None, context=None):
2283+ if default is None:
2284+ default = {}
2285+ default['icops_bind_ids'] = False
2286+ return super(sale_order_line, self).copy_data(cr, uid, id,
2287+ default=default,
2288+ context=context)
2289+
2290+
2291+class icops_sale_order_line(orm.Model):
2292+ _name = 'icops.sale.order.line'
2293+ _inherit = 'icops.binding'
2294+ _description = 'ICOPS Sale Order Line'
2295+ _inherits = {'sale.order.line': 'openerp_id'}
2296+
2297+ def _get_lines_from_order(self, cr, uid, ids, context=None):
2298+ line_obj = self.pool.get('icops.sale.order.line')
2299+ return line_obj.search(cr, uid,
2300+ [('icops_order_id', 'in', ids)],
2301+ context=context)
2302+ _columns = {
2303+ 'icops_order_id': fields.many2one('icops.sale.order',
2304+ 'ICOPS Sale Order',
2305+ required=True,
2306+ ondelete='cascade',
2307+ select=True),
2308+ 'openerp_id': fields.many2one('sale.order.line',
2309+ string='Sale Order Line',
2310+ required=True,
2311+ ondelete='cascade'),
2312+ 'backend_id': fields.related(
2313+ 'icops_order_id', 'backend_id',
2314+ type='many2one',
2315+ relation='icops.backend',
2316+ string='ICOPS Backend',
2317+ store={'icops.sale.order.line':
2318+ (lambda self, cr, uid, ids, c=None: ids,
2319+ ['icops_order_id'],
2320+ 10),
2321+ 'icops.sale.order':
2322+ (_get_lines_from_order, ['backend_id'], 20),
2323+ },
2324+ readonly=True)
2325+ }
2326+
2327+ def create(self, cr, uid, vals, context=None):
2328+ icops_order_id = vals['icops_order_id']
2329+ info = self.pool['icops.sale.order'].read(cr, uid,
2330+ [icops_order_id],
2331+ ['openerp_id'],
2332+ context=context)
2333+ order_id = info[0]['openerp_id']
2334+ vals['order_id'] = order_id[0]
2335+ return super(icops_sale_order_line, self).create(cr, uid, vals,
2336+ context=context)
2337+
2338+
2339+@icops
2340+class SaleOrderAdapter(ICOPSAdapter):
2341+ _model_name = 'icops.sale.order'
2342+
2343+ def _get_pool(self):
2344+ sess = self.session
2345+ name = ('purchase.order'
2346+ if self._icops.concept == 'so2po' else 'sale.order')
2347+ return sess.pool.get(name)
2348+
2349+ def confirm(self, id):
2350+ sess = self.session
2351+ pool = self._get_pool()
2352+ context = {'icops': True}
2353+ pool.write(
2354+ sess.cr, self._backend_to.icops_uid.id, [id],
2355+ {'temp_unlock': True}, context)
2356+ if hasattr(pool, 'wkf_confirm_order'):
2357+ pool.wkf_confirm_order(
2358+ sess.cr, self._backend_to.icops_uid.id, [id])
2359+ else:
2360+ pool.action_wait(
2361+ sess.cr, self._backend_to.icops_uid.id, [id])
2362+ pool.write(
2363+ sess.cr, self._backend_to.icops_uid.id, [id],
2364+ {'temp_unlock': False}, context)
2365+
2366+ def cancel(self, id):
2367+ sess = self.session
2368+ pool = self._get_pool()
2369+ context = {'icops': True}
2370+ pool.write(
2371+ sess.cr, self._backend_to.icops_uid.id, [id],
2372+ {'temp_unlock': True}, context)
2373+ pool.action_cancel(sess.cr, self._backend_to.icops_uid.id, [id])
2374+ pool.write(
2375+ sess.cr, self._backend_to.icops_uid.id, [id],
2376+ {'temp_unlock': False}, context)
2377+
2378+
2379+@icops
2380+class SaleOrderExport(ICOPSExporter):
2381+ _model_name = ['icops.sale.order']
2382+ _concepts = ['so2po', 'so2so']
2383+
2384+ def _custom_routing(self, id, record, fields=None):
2385+ if 'state' in fields:
2386+ state = record.pop('state')
2387+ if state == 'cancel':
2388+ self._cancel(id)
2389+ elif state in ('approved', 'progress', 'manual'):
2390+ self._confirm(id)
2391+ elif fields:
2392+ self._write(id, record)
2393+
2394+
2395+@icops
2396+class SaleOrderExportMapper(ICOPSExportMapper):
2397+ _model_name = 'icops.sale.order'
2398+
2399+ children = [
2400+ ('order_line', 'order_line', 'icops.sale.order.line')
2401+ ]
2402+
2403+ def _partner(self, record, is_po=False):
2404+ sess = self.session
2405+ backend = self._backend_to
2406+ ic_uid = backend.icops_uid.id
2407+ partner_pool = sess.pool.get('res.partner')
2408+ partner_id = record.company_id.partner_id.id
2409+ partner = partner_pool.browse(sess.cr, ic_uid, partner_id)
2410+ pricelist_id = None
2411+ payment_term_id = None
2412+ if is_po:
2413+ pricelist_id = (partner.property_product_pricelist_purchase.id
2414+ if partner.property_product_pricelist_purchase
2415+ else False)
2416+ payment_term_id = (partner.property_supplier_payment_term.id
2417+ if partner.property_supplier_payment_term
2418+ else False)
2419+ else:
2420+ pricelist_id = (partner.property_product_pricelist.id
2421+ if partner.property_product_pricelist
2422+ else False)
2423+ payment_term_id = (partner.property_payment_term.id
2424+ if partner.property_payment_term
2425+ else False)
2426+ fiscal_position_id = (partner.property_account_position.id
2427+ if partner.property_account_position
2428+ else False)
2429+ return {'partner_id': partner.id,
2430+ 'pricelist_id': pricelist_id,
2431+ 'payment_term_id': payment_term_id,
2432+ 'fiscal_position': fiscal_position_id}
2433+
2434+ @mapping
2435+ def origin(self, record):
2436+ return {'origin': 'ICOPS: %s' % record.name}
2437+
2438+ @mapping
2439+ def map_all(self, record):
2440+ assert self._icops
2441+ return self._get_mapping(self._icops.concept, record)
2442+
2443+ def so2so_icops(self, record):
2444+ if not self._backend_to:
2445+ raise MappingError("Could not find an ICOPS backend")
2446+ backend = self._backend_to
2447+ icops_uid = backend.icops_uid.id
2448+ company = backend.company_id
2449+ partner = company.partner_id
2450+ pricelist = partner.property_product_pricelist
2451+ fiscal_position = partner.property_account_position
2452+ payment_term = partner.property_payment_term
2453+ shop = self._backend_to.icops_shop_id
2454+
2455+ return {
2456+ 'company_id': company.id,
2457+ 'partner_id': partner.id,
2458+ 'pricelist_id': pricelist.id,
2459+ 'fiscal_position': fiscal_position.id,
2460+ 'payment_term': payment_term.id,
2461+ 'user_id': icops_uid,
2462+ 'shop_id': shop.id
2463+ }
2464+
2465+ def so2so_partner(self, record):
2466+ return self._partner(record)
2467+
2468+ def so2so_address(self, record):
2469+ sess = self.session
2470+ partner = record.company_id.partner_id
2471+ partner_pool = sess.pool.get('res.partner')
2472+ addr = partner_pool.address_get(sess.cr, sess.uid, [partner.id],
2473+ ['delivery', 'invoice', 'contact'])
2474+ return {
2475+ 'partner_invoice_id': addr['invoice'],
2476+ 'partner_shipping_id': addr['delivery']
2477+ }
2478+
2479+ def so2so_policy(self, record):
2480+ return {'order_policy': record.order_policy}
2481+
2482+ def so2so_state(self, record):
2483+ return {'state': record.state}
2484+
2485+ def so2po_partner(self, record):
2486+ return self._partner(record, True)
2487+
2488+ def so2po_icops(self, record):
2489+ if not self._backend_to:
2490+ raise MappingError("Could not find an ICOPS backend")
2491+ backend = self._backend_to
2492+ company = backend.company_id
2493+ shop = backend.icops_shop_id
2494+ warehouse = shop.warehouse_id
2495+ location = warehouse.lot_stock_id
2496+
2497+ if company.partner_id.id != record.partner_id.id:
2498+ raise MappingError("Wrong partner")
2499+ return {
2500+ 'company_id': company.id,
2501+ 'location_id': location.id
2502+ }
2503+
2504+ def so2po_state(self, record):
2505+ state = record.state
2506+ if record.state in ('progress', 'manual'):
2507+ state = 'approved'
2508+ return {'state': state}
2509+
2510+
2511+@icops
2512+class SaleOrderLineExportMapper(ICOPSExportMapper):
2513+ _model_name = 'icops.sale.order.line'
2514+
2515+ @mapping
2516+ def name(self, record):
2517+ return {'name': record.name}
2518+
2519+ def _price(self, record, is_po=False):
2520+ if not record.product_id:
2521+ return {'price_unit': 0}
2522+ sess = self.session
2523+ backend = self._backend_to
2524+ ic_uid = backend.icops_uid.id
2525+ partner_pool = sess.pool.get('res.partner')
2526+ partner_id = record.order_id.company_id.partner_id.id
2527+ partner = partner_pool.browse(sess.cr, ic_uid, partner_id)
2528+ pricelist_id = None
2529+ if is_po:
2530+ pricelist_id = (partner.property_product_pricelist_purchase.id
2531+ if partner.property_product_pricelist_purchase
2532+ else False)
2533+ else:
2534+ pricelist_id = (partner.property_product_pricelist.id
2535+ if partner.property_product_pricelist
2536+ else False)
2537+ pricelist_pool = sess.pool.get('product.pricelist')
2538+ price_unit = pricelist_pool.price_get(
2539+ sess.cr, ic_uid, [pricelist_id],
2540+ record.product_id.id, record.product_uom_qty)
2541+ price = price_unit[int(pricelist_id)]
2542+ return {'price_unit': price}
2543+
2544+ @mapping
2545+ def map_all(self, record):
2546+ assert self._icops
2547+ return self._get_mapping(self._icops.concept, record)
2548+
2549+ def so2so_price(self, record):
2550+ return self._price(record)
2551+
2552+ def so2so_product(self, record):
2553+ return {
2554+ 'product_id': record.product_id.id,
2555+ 'product_uom': record.product_uom.id,
2556+ 'product_uom_qty': record.product_uom_qty
2557+ }
2558+
2559+ def so2po_price(self, record):
2560+ return self._price(record, True)
2561+
2562+ def so2po_product(self, record):
2563+ return {
2564+ 'product_id': record.product_id.id,
2565+ 'product_uom': record.product_uom.id,
2566+ 'product_qty': record.product_uom_qty
2567+ }
2568+
2569+ def so2po_date_planned(self, record):
2570+ sess = self.session
2571+ order = record.order_id
2572+ order_pool = sess.pool.get(order._name)
2573+ date_planned = order_pool._get_date_planned(
2574+ sess.cr, sess.uid, order, record, order.date_order)
2575+ return {'date_planned': date_planned}
2576
2577=== added file 'base_intercompany_sale/sale_view.xml'
2578--- base_intercompany_sale/sale_view.xml 1970-01-01 00:00:00 +0000
2579+++ base_intercompany_sale/sale_view.xml 2014-02-23 01:10:59 +0000
2580@@ -0,0 +1,26 @@
2581+<?xml version="1.0" encoding="UTF-8"?>
2582+<openerp>
2583+ <data>
2584+ <record id="view_order_form" model="ir.ui.view">
2585+ <field name="name">sale.order</field>
2586+ <field name="model">sale.order</field>
2587+ <field name="inherit_id" ref="sale.view_order_form" />
2588+ <field name="arch" type="xml">
2589+ <xpath expr="//header/button[last()]" position="after">
2590+ <button name="action_unlock" string="Unlock" type="object" attrs="{'invisible':[('locked', '=', False)]}" class="oe_highlight" />
2591+ </xpath>
2592+ <xpath expr="//page[last()]" position="after">
2593+ <page string="ICOPS">
2594+ <field name="locked" invisible="1" />
2595+ <field name="icops_bind_ids">
2596+ <tree string="ICOPS Backend" editable="bottom">
2597+ <field name="backend_id" />
2598+ <field name="icops_ids" />
2599+ </tree>
2600+ </field>
2601+ </page>
2602+ </xpath>
2603+ </field>
2604+ </record>
2605+ </data>
2606+</openerp>
2607\ No newline at end of file
2608
2609=== added directory 'base_intercompany_sale/security'
2610=== added file 'base_intercompany_sale/security/ir.model.access.csv'
2611--- base_intercompany_sale/security/ir.model.access.csv 1970-01-01 00:00:00 +0000
2612+++ base_intercompany_sale/security/ir.model.access.csv 2014-02-23 01:10:59 +0000
2613@@ -0,0 +1,21 @@
2614+id,name,model_id:id,group_id:id,perm_read,perm_write,perm_create,perm_unlink
2615+access_icops_sale_order_manager,icops sale order manager,base_intercompany_sale.model_icops_sale_order,base.group_sale_manager,1,1,1,1
2616+access_icops_sale_order_user,icops sale order user,base_intercompany_sale.model_icops_sale_order,base.group_sale_salesman,1,1,1,1
2617+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
2618+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
2619+access_icops_backend_manager,icops backend manager,base_intercompany.model_icops_backend,base.group_sale_manager,1,1,1,1
2620+access_icops_backend_user,icops backend user,base_intercompany.model_icops_backend,base.group_sale_salesman,1,1,1,1
2621+access_res_intercompany_manager,res intercompany manager,base_intercompany.model_res_intercompany,base.group_sale_manager,1,1,1,1
2622+access_res_intercompany_user,res intercompany user,base_intercompany.model_res_intercompany,base.group_sale_salesman,1,1,1,1
2623+access_icops_record_manager,icops record manager,base_intercompany.model_icops_record,base.group_sale_manager,1,1,1,1
2624+access_icops_record_user,icops record user,base_intercompany.model_icops_record,base.group_sale_salesman,1,1,1,1
2625+access_icops_purchase_order_manager,icops purchase order manager,base_intercompany_sale.model_icops_purchase_order,purchase.group_purchase_manager,1,1,1,1
2626+access_icops_purchase_order_user,icops purchase order user,base_intercompany_sale.model_icops_purchase_order,purchase.group_purchase_user,1,1,1,1
2627+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
2628+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
2629+access_icops_backend_manager,icops backend manager,base_intercompany.model_icops_backend,purchase.group_purchase_manager,1,1,1,1
2630+access_icops_backend_user,icops backend user,base_intercompany.model_icops_backend,purchase.group_purchase_user,1,1,1,1
2631+access_res_intercompany_manager,res intercompany manager,base_intercompany.model_res_intercompany,purchase.group_purchase_manager,1,1,1,1
2632+access_res_intercompany_user,res intercompany user,base_intercompany.model_res_intercompany,purchase.group_purchase_user,1,1,1,1
2633+access_icops_record_manager,icops record manager,base_intercompany.model_icops_record,purchase.group_purchase_manager,1,1,1,1
2634+access_icops_record_user,icops record user,base_intercompany.model_icops_record,purchase.group_purchase_user,1,1,1,1
2635\ No newline at end of file
2636
2637=== added directory 'base_intercompany_sale/static'
2638=== added directory 'base_intercompany_sale/static/description'
2639=== added file 'base_intercompany_sale/static/description/Intercompany_setup.png'
2640Binary 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-02-23 01:10:59 +0000 differ
2641=== added file 'base_intercompany_sale/static/description/index.html'
2642--- base_intercompany_sale/static/description/index.html 1970-01-01 00:00:00 +0000
2643+++ base_intercompany_sale/static/description/index.html 2014-02-23 01:10:59 +0000
2644@@ -0,0 +1,26 @@
2645+<section class="oe_container">
2646+ <div class="oe_row">
2647+ <h2 class="oe_slogan">base_intercompany_sale</h2>
2648+ <h3 class="oe_slogan">Sale Inter-Company Processing (ICOPS) <br /></h3>
2649+ <h4 class="oe_slogan"><a href="http://www.elico-corp.com">By Elico Corp</a></h4>
2650+ <p>
2651+ 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.
2652+ </p>
2653+ <ul>
2654+ <li>Sale Order to Purchase Order (so2po)</li>
2655+ <li>Sale Order to Sale Order (so2so)</li>
2656+ <li>Purchase Order to Sale Order (po2so)</li>
2657+ <li>Handles the following events: Create, Update, Delete, Confirm and Cancel</li>
2658+ </ul>
2659+ <p>
2660+ TODO: demo data to be improved.<br />
2661+ Blueprint: <a href="https://blueprints.launchpad.net/multi-company/+spec/icops">https://blueprints.launchpad.net/multi-company/+spec/icops</a><br />
2662+ </p>
2663+ <div class="oe_row_img oe_centered oe_mt32">
2664+ <img class="oe_picture oe_screenshot" src="Intercompany_setup.png" />
2665+ </div>
2666+ </div>
2667+ <div class="oe_row oe_centeralign oe_more_space">
2668+ <a href="http://www.elico-corp.com/saas/" class="oe_button oe_big">Start your <span class="oe_emph">free</span> trial</a>
2669+ </div>
2670+</section>
2671\ No newline at end of file
2672
2673=== added directory 'base_intercompany_sale/static/src'
2674=== added directory 'base_intercompany_sale/static/src/img'
2675=== added file 'base_intercompany_sale/static/src/img/icon.png'
2676Binary 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-02-23 01:10:59 +0000 differ
2677=== added directory 'base_intercompany_sale/tests'
2678=== added file 'base_intercompany_sale/tests/__init__.py'
2679--- base_intercompany_sale/tests/__init__.py 1970-01-01 00:00:00 +0000
2680+++ base_intercompany_sale/tests/__init__.py 2014-02-23 01:10:59 +0000
2681@@ -0,0 +1,30 @@
2682+# -*- coding: utf-8 -*-
2683+##############################################################################
2684+#
2685+# OpenERP, Open Source Management Solution
2686+# Copyright (c) 2010-2014 Elico Corp. All Rights Reserved.
2687+# Augustin Cisterne-Kaas <augustin.cisterne-kaas@elico-corp.com>
2688+# Eric Caudal <eric.caudal@elico-corp.com>
2689+#
2690+# This program is free software: you can redistribute it and/or modify
2691+# it under the terms of the GNU Affero General Public License as
2692+# published by the Free Software Foundation, either version 3 of the
2693+# License, or (at your option) any later version.
2694+#
2695+# This program is distributed in the hope that it will be useful,
2696+# but WITHOUT ANY WARRANTY; without even the implied warranty of
2697+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
2698+# GNU Affero General Public License for more details.
2699+#
2700+# You should have received a copy of the GNU Affero General Public License
2701+# along with this program. If not, see <http://www.gnu.org/licenses/>.
2702+#
2703+##############################################################################
2704+import test_sale
2705+
2706+fast_suite = [
2707+]
2708+
2709+checks = [
2710+ test_sale
2711+]
2712
2713=== added file 'base_intercompany_sale/tests/test_sale.py'
2714--- base_intercompany_sale/tests/test_sale.py 1970-01-01 00:00:00 +0000
2715+++ base_intercompany_sale/tests/test_sale.py 2014-02-23 01:10:59 +0000
2716@@ -0,0 +1,181 @@
2717+# -*- coding: utf-8 -*-
2718+##############################################################################
2719+#
2720+# OpenERP, Open Source Management Solution
2721+# Copyright (c) 2010-2014 Elico Corp. All Rights Reserved.
2722+# Augustin Cisterne-Kaas <augustin.cisterne-kaas@elico-corp.com>
2723+# Eric Caudal <eric.caudal@elico-corp.com>
2724+#
2725+# This program is free software: you can redistribute it and/or modify
2726+# it under the terms of the GNU Affero General Public License as
2727+# published by the Free Software Foundation, either version 3 of the
2728+# License, or (at your option) any later version.
2729+#
2730+# This program is distributed in the hope that it will be useful,
2731+# but WITHOUT ANY WARRANTY; without even the implied warranty of
2732+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
2733+# GNU Affero General Public License for more details.
2734+#
2735+# You should have received a copy of the GNU Affero General Public License
2736+# along with this program. If not, see <http://www.gnu.org/licenses/>.
2737+#
2738+##############################################################################
2739+import unittest2
2740+from datetime import datetime, timedelta
2741+import openerp
2742+import openerp.tests.common as common
2743+from openerp.osv import osv
2744+
2745+DB = common.DB
2746+ADMIN_USER_ID = common.ADMIN_USER_ID
2747+
2748+
2749+class test_sale(object):
2750+
2751+ def configure(self):
2752+ # get super user
2753+ self.m = self.registry('ir.model.data')
2754+ # get companies
2755+ self.company_origin = self.m.get_object(
2756+ self.cr, self.uid, 'base_intercompany', 'company_origin')
2757+
2758+ self.company_destination = self.m.get_object(
2759+ self.cr, self.uid, 'base_intercompany', 'company_destination')
2760+
2761+ self.user_origin = self.m.get_object(
2762+ self.cr, self.uid, 'base_intercompany', 'user_origin')
2763+
2764+ self.user_destination = self.m.get_object(
2765+ self.cr, self.uid, 'base_intercompany', 'user_destination')
2766+
2767+ self.shop_origin = self.m.get_object(
2768+ self.cr, self.uid, 'base_intercompany_sale', 'shop_origin')
2769+
2770+ self.backend_origin = self.m.get_object(
2771+ self.cr, self.uid, 'base_intercompany', 'backend_origin')
2772+
2773+ self.backend_destination = self.m.get_object(
2774+ self.cr, self.uid, 'base_intercompany', 'backend_destination')
2775+
2776+ def set_backend(self, on_create=True, on_write=True,
2777+ on_unlink=True, on_confirm=True, on_cancel=True):
2778+ assert hasattr(self, 'concept')
2779+ backend_pool = self.registry('icops.backend')
2780+ backend_pool.write(self.cr, ADMIN_USER_ID, self.backend_origin.id, {
2781+ 'icops_ids': [(5, 0), (0, 0, {
2782+ 'backend_id': self.backend_origin.id,
2783+ 'backend_to': self.backend_destination.id,
2784+ 'concept': self.concept,
2785+ 'on_create': on_create,
2786+ 'on_write': on_write,
2787+ 'on_unlink': on_unlink,
2788+ 'on_confirm': on_confirm,
2789+ 'on_cancel': on_cancel
2790+ })]
2791+ })
2792+
2793+ def create_object(self):
2794+ pool_origin = self.registry(self.model_origin)
2795+ uid = self.user_origin.id
2796+ obj_id = pool_origin.create(
2797+ self.cr, self.user_origin.id, self.data_obj)
2798+ obj = pool_origin.browse(self.cr, uid, obj_id)
2799+ bind_ids = obj.icops_bind_ids
2800+ self.assertEqual(len(bind_ids), 1)
2801+ bind_id = bind_ids[0]
2802+ icops_ids = bind_id.icops_ids
2803+ self.assertEqual(len(icops_ids), 1)
2804+ icops = icops_ids[0]
2805+ ic_uid = icops.backend_id.icops_uid
2806+ self.assertEqual(icops.model, self.model_destination)
2807+ pool_destination = self.registry(icops.model)
2808+ obj_dest = pool_destination.browse(
2809+ self.cr, ic_uid, icops.record_id)
2810+ self.assertIsNotNone(obj_dest)
2811+ return obj, obj_dest
2812+
2813+ def write_object(self, obj, obj_dest):
2814+ pool_origin = self.registry(self.model_origin)
2815+ uid = self.user_origin.id
2816+ self.assertEqual(len(obj.order_line), 0)
2817+ self.assertEqual(len(obj_dest.order_line), 0)
2818+ pool_origin.write(self.cr, uid, obj.id, {
2819+ 'order_line': [(5, 0), (0, 0, self.data_line)]
2820+ })
2821+ self.assertEqual(len(obj.order_line), 2)
2822+ self.assertEqual(len(obj_dest.order_line), 2)
2823+
2824+
2825+ def test_01_creation(self):
2826+ self.set_backend()
2827+ self.create_object()
2828+
2829+ def test_02_creation_raise_exception(self):
2830+ self.set_backend(on_create=False)
2831+ self.assertRaises(
2832+ osv.except_osv, self.create_object)
2833+
2834+ def test_03_creation_without_write_permission(self):
2835+ self.set_backend(on_write=False)
2836+ self.create_object()
2837+
2838+ # def test_04_write(self):
2839+ # self.set_backend()
2840+ # pool_origin = self.registry(self.model_origin)
2841+ # obj, obj_dest = self.create_object()
2842+ # self.write_object(obj, obj_dest)
2843+
2844+
2845+class test_sale_so2po(common.TransactionCase, test_sale):
2846+
2847+ def setUp(self):
2848+ super(test_sale_so2po, self).setUp()
2849+ self.configure()
2850+ self.concept = 'so2po'
2851+ self.model_origin = 'sale.order'
2852+ self.model_destination = 'purchase.order'
2853+ self.data_obj = {
2854+ 'partner_id': self.company_destination.partner_id.id,
2855+ 'partner_invoice_id': self.company_destination.partner_id.id,
2856+ 'partner_shipping_id': self.company_destination.partner_id.id,
2857+ 'shop_id': self.shop_origin.id,
2858+ 'date_order': datetime.now().strftime('%Y-%m-%d'),
2859+ 'pricelist_id': self.user_origin.property_product_pricelist.id}
2860+
2861+ self.data_line = {
2862+ 'name': 'Test product',
2863+ 'product_uom': 1,
2864+ 'product_uom_qty': 1,
2865+ 'price_unit': 100
2866+ }
2867+
2868+
2869+class test_sale_so2so(common.TransactionCase, test_sale):
2870+
2871+ def setUp(self):
2872+ super(test_sale_so2so, self).setUp()
2873+ self.configure()
2874+ self.concept = 'so2so'
2875+ self.model_origin = 'sale.order'
2876+ self.model_destination = 'sale.order'
2877+ self.data_obj = {
2878+ 'partner_id': self.company_destination.partner_id.id,
2879+ 'partner_invoice_id': self.company_destination.partner_id.id,
2880+ 'partner_shipping_id': self.company_destination.partner_id.id,
2881+ 'shop_id': self.shop_origin.id,
2882+ 'date_order': datetime.now().strftime('%Y-%m-%d'),
2883+ 'pricelist_id': self.user_origin.property_product_pricelist.id}
2884+
2885+ self.data_line = {
2886+ 'name': 'Test product',
2887+ 'product_uom': 1,
2888+ 'product_uom_qty': 1,
2889+ 'price_unit': 100
2890+ }
2891+# class test_sale_po2so(common.TransactionCase, test_sale):
2892+
2893+# def setUp(self):
2894+# super(test_sale_po2so, self).setUp()
2895+# self.configure()
2896+# self.model_origin = 'purchase.order'
2897+# self.model_destination = 'sale.order'

Subscribers

People subscribed via source and target branches