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
=== added directory 'base_intercompany'
=== added file 'base_intercompany/__init__.py'
--- base_intercompany/__init__.py 1970-01-01 00:00:00 +0000
+++ base_intercompany/__init__.py 2014-02-23 01:10:59 +0000
@@ -0,0 +1,27 @@
1# -*- coding: utf-8 -*-
2##############################################################################
3#
4# OpenERP, Open Source Management Solution
5# Copyright (c) 2010-2014 Elico Corp. All Rights Reserved.
6# Augustin Cisterne-Kaas <augustin.cisterne-kaas@elico-corp.com>
7# Eric Caudal <eric.caudal@elico-corp.com>
8
9# This program is free software: you can redistribute it and/or modify
10# it under the terms of the GNU Affero General Public License as
11# published by the Free Software Foundation, either version 3 of the
12# License, or (at your option) any later version.
13#
14# This program is distributed in the hope that it will be useful,
15# but WITHOUT ANY WARRANTY; without even the implied warranty of
16# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17# GNU Affero General Public License for more details.
18#
19# You should have received a copy of the GNU Affero General Public License
20# along with this program. If not, see <http://www.gnu.org/licenses/>.
21#
22##############################################################################
23import connector
24import consumer
25import backend
26import company
27import icops_model
028
=== added file 'base_intercompany/__openerp__.py'
--- base_intercompany/__openerp__.py 1970-01-01 00:00:00 +0000
+++ base_intercompany/__openerp__.py 2014-02-23 01:10:59 +0000
@@ -0,0 +1,49 @@
1# -*- coding: utf-8 -*-
2##############################################################################
3#
4# OpenERP, Open Source Management Solution
5# Copyright (c) 2010-2014 Elico Corp. All Rights Reserved.
6# Augustin Cisterne-Kaas <augustin.cisterne-kaas@elico-corp.com>
7# Eric Caudal <eric.caudal@elico-corp.com>
8
9# This program is free software: you can redistribute it and/or modify
10# it under the terms of the GNU Affero General Public License as
11# published by the Free Software Foundation, either version 3 of the
12# License, or (at your option) any later version.
13#
14# This program is distributed in the hope that it will be useful,
15# but WITHOUT ANY WARRANTY; without even the implied warranty of
16# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17# GNU Affero General Public License for more details.
18#
19# You should have received a copy of the GNU Affero General Public License
20# along with this program. If not, see <http://www.gnu.org/licenses/>.
21#
22##############################################################################
23
24{'name': 'Base Intercompany',
25 'version': '0.4',
26 'category': 'Generic Modules',
27 'depends': ['connector'],
28 'author': 'Elico Corp',
29 'license': 'AGPL-3',
30 'website': 'https://www.elico-corp.com',
31 'description': """
32This module is the structure designed to manage Inter-company Process (ICOPS)
33and allows 2 companies to create objects in each other.
34This module needs to be installed with one of the following modules:
35 - Base Intercompany Sale
36 - Base Intercompany Stock (in development)
37 - Any module which extends the Base Intercompany module
38
39TODO: demo data to be improved.\n
40Blueprint: https://blueprints.launchpad.net/multi-company/+spec/icops
41""",
42 'images': [],
43 'demo': ['base_intercompany_demo.xml'],
44 'data': ['security/ir.model.access.csv',
45 'icops_model_view.xml',
46 'base_intercompany_menu.xml'],
47 'installable': True,
48 'application': False,
49 }
050
=== added file 'base_intercompany/backend.py'
--- base_intercompany/backend.py 1970-01-01 00:00:00 +0000
+++ base_intercompany/backend.py 2014-02-23 01:10:59 +0000
@@ -0,0 +1,30 @@
1# -*- coding: utf-8 -*-
2##############################################################################
3#
4# OpenERP, Open Source Management Solution
5# Copyright (c) 2010-2014 Elico Corp. All Rights Reserved.
6# Augustin Cisterne-Kaas <augustin.cisterne-kaas@elico-corp.com>
7# Eric Caudal <eric.caudal@elico-corp.com>
8
9# This program is free software: you can redistribute it and/or modify
10# it under the terms of the GNU Affero General Public License as
11# published by the Free Software Foundation, either version 3 of the
12# License, or (at your option) any later version.
13#
14# This program is distributed in the hope that it will be useful,
15# but WITHOUT ANY WARRANTY; without even the implied warranty of
16# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17# GNU Affero General Public License for more details.
18#
19# You should have received a copy of the GNU Affero General Public License
20# along with this program. If not, see <http://www.gnu.org/licenses/>.
21#
22##############################################################################
23import openerp.addons.connector.backend as backend
24
25
26icops = backend.Backend('icops')
27""" Generic ICOPS Backend """
28
29icops7 = backend.Backend(parent=icops, version='7.0')
30""" ICOPS Backend for OpenERP 7 """
031
=== added file 'base_intercompany/base_intercompany_demo.xml'
--- base_intercompany/base_intercompany_demo.xml 1970-01-01 00:00:00 +0000
+++ base_intercompany/base_intercompany_demo.xml 2014-02-23 01:10:59 +0000
@@ -0,0 +1,76 @@
1<?xml version="1.0" encoding="utf-8"?>
2<openerp>
3 <data noupdate="1">
4 <record id="customer_origin" model="res.partner">
5 <field name="name">Company Origin</field>
6 </record>
7
8 <record id="supplier_destination" model="res.partner">
9 <field name="name">Company Destination</field>
10 <field name="supplier" eval="True" />
11 </record>
12
13 <record id="company_origin" model="res.company">
14 <field name="name">Company Origin</field>
15 <field name="parent_id" ref="base.main_company"/>
16 <field name="partner_id" ref="customer_origin" />
17 </record>
18
19 <record id="company_destination" model="res.company">
20 <field name="name">Company Destination</field>
21 <field name="parent_id" ref="base.main_company"/>
22 <field name="partner_id" ref="supplier_destination" />
23 </record>
24
25 <record id="customer_origin" model="res.partner">
26 <field name="company_id"></field>
27 </record>
28
29 <record id="supplier_destination" model="res.partner">
30 <field name="company_id"></field>
31 </record>
32
33 <record id="partner_origin" model="res.partner">
34 <field name="name">User Origin</field>
35 <field name="company_id" ref="company_origin"/>
36 <field name="customer" eval="False"/>
37 </record>
38
39 <record id="user_origin" model="res.users">
40 <field name="partner_id" ref="partner_origin"/>
41 <field name="login">user.origin</field>
42 <field name="password">user.origin</field>
43 <field name="company_id" ref="company_origin"/>
44 <field name="company_ids" eval="[(6,0,[ref('company_origin')])]" />
45 <field name="groups_id" eval="[(6,0,[ref('base.group_user'), ref('base.group_partner_manager')])]"/>
46 </record>
47
48 <record id="partner_destination" model="res.partner">
49 <field name="name">User Destination</field>
50 <field name="company_id" ref="company_destination"/>
51 <field name="customer" eval="False"/>
52 </record>
53
54 <record id="user_destination" model="res.users">
55 <field name="partner_id" ref="partner_destination"/>
56 <field name="login">user.destination</field>
57 <field name="password">user.destination</field>
58 <field name="company_id" ref="company_destination"/>
59 <field name="company_ids" eval="[(6,0,[ref('company_destination')])]" />
60 <field name="groups_id" eval="[(6,0,[ref('base.group_user'), ref('base.group_partner_manager')])]"/>
61 </record>
62
63 <!-- ICOPS Setup -->
64 <record id="backend_origin" model="icops.backend">
65 <field name="name">Backend Origin</field>
66 <field name="company_id" ref="company_origin" />
67 <field name="icops_uid" ref="user_origin" />
68 </record>
69
70 <record id="backend_destination" model="icops.backend">
71 <field name="name">Backend Destination</field>
72 <field name="company_id" ref="company_destination" />
73 <field name="icops_uid" ref="user_destination" />
74 </record>
75 </data>
76</openerp>
0\ No newline at end of file77\ No newline at end of file
178
=== added file 'base_intercompany/base_intercompany_menu.xml'
--- base_intercompany/base_intercompany_menu.xml 1970-01-01 00:00:00 +0000
+++ base_intercompany/base_intercompany_menu.xml 2014-02-23 01:10:59 +0000
@@ -0,0 +1,22 @@
1<?xml version="1.0" encoding="utf-8"?>
2<openerp>
3 <data>
4 <menuitem id="menu_icops_root"
5 parent="connector.menu_connector_root"
6 name="ICOPS"
7 sequence="10"
8 groups="connector.group_connector_manager"/>
9
10 <menuitem id="menu_icops_backend"
11 name="Backends"
12 parent="menu_icops_root"
13 action="action_icops_backend"/>
14
15 <menuitem id="menu_server_root"
16 parent="connector.menu_connector_root"
17 name="Server"
18 sequence="20"
19 groups="connector.group_connector_manager"/>
20
21 </data>
22</openerp>
023
=== added file 'base_intercompany/company.py'
--- base_intercompany/company.py 1970-01-01 00:00:00 +0000
+++ base_intercompany/company.py 2014-02-23 01:10:59 +0000
@@ -0,0 +1,142 @@
1# -*- coding: utf-8 -*-
2##############################################################################
3#
4# OpenERP, Open Source Management Solution
5# Copyright (c) 2010-2014 Elico Corp. All Rights Reserved.
6# Augustin Cisterne-Kaas <augustin.cisterne-kaas@elico-corp.com>
7# Eric Caudal <eric.caudal@elico-corp.com>
8
9# This program is free software: you can redistribute it and/or modify
10# it under the terms of the GNU Affero General Public License as
11# published by the Free Software Foundation, either version 3 of the
12# License, or (at your option) any later version.
13#
14# This program is distributed in the hope that it will be useful,
15# but WITHOUT ANY WARRANTY; without even the implied warranty of
16# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17# GNU Affero General Public License for more details.
18#
19# You should have received a copy of the GNU Affero General Public License
20# along with this program. If not, see <http://www.gnu.org/licenses/>.
21#
22##############################################################################
23
24from openerp.osv import orm, fields
25
26
27class res_intercompany(orm.Model):
28 _name = 'res.intercompany'
29 _description = 'Inter-Company'
30
31 def _select_concepts(self, cr, uid, context=None):
32 """ Available concepts
33
34 Can be inherited to add custom versions.
35 """
36 return []
37
38 def _select_models(self, cr, uid, context=None):
39 """ Available Object names
40
41 Can be inherited to add custom versions.
42 """
43 return {}
44
45 def _get_model(self, cr, uid, ids, name, arg, context=None):
46 """ Available versions
47
48 Can be inherited to add custom versions.
49 """
50 res = {}
51 for intercompany in self.browse(cr, uid, ids, context):
52 model = None
53 if intercompany.concept:
54 model = self._select_models(
55 cr, uid)[intercompany.concept]
56 res[intercompany.id] = model
57 return res
58
59 def get_intercompany(self, cr, uid, obj_id,
60 obj_class_name, obj_name, context=None):
61 """
62 get company from and to
63 """
64 if isinstance(obj_id, list):
65 obj_id = obj_id[0]
66 assert isinstance(obj_id, int) or isinstance(obj_id, long)
67 obj = self.pool.get(obj_class_name).browse(
68 cr,
69 uid,
70 obj_id,
71 context=None)
72 company = obj.company_id
73 ic_uid_ids = None
74 company_to_ids = []
75 for ic in company.intercompany_ids:
76 if ic.concept == obj_name:
77 company_to_ids.append(ic.company_to.id)
78 ic_uid_ids = ic.icops_uid.id
79 if company.intercompany_ids:
80 ic_uid_ids = company.intercompany_ids[0].icops_uid.id
81 return obj.company_id.id, company_to_ids, ic_uid_ids
82
83 def _check_intercompany_user(self, cr, uid, ids, context=None):
84 for ic in self.browse(cr, uid, ids, context=context):
85 if not ic.icops_uid:
86 return False
87 return True
88
89 _columns = {
90 'backend_id': fields.many2one('icops.backend', 'Original Backend',
91 required=True, ondelete='cascade'),
92 'backend_to': fields.many2one('icops.backend', 'Destination Backend',
93 required=True, ondelete='cascade'),
94 'concept': fields.selection(_select_concepts, string="Concept",
95 required=True),
96 'model': fields.function(_get_model, type='char',
97 string='Object', store=False),
98 'icops_uid': fields.related(
99 'backend_to', 'icops_uid', type='many2one',
100 relation='res.users', readonly=True, string='IC User'),
101 'on_create': fields.boolean('Create'),
102 'on_write': fields.boolean('Update'),
103 'on_unlink': fields.boolean('Delete'),
104 'on_confirm': fields.boolean('Confirm'),
105 'on_cancel': fields.boolean('Cancel')
106 }
107
108 _constraints = [
109 (_check_intercompany_user, 'Please set IC user for the Company first',
110 ['icops_uid'])
111 ]
112
113 _defaults = {
114 'backend_id': lambda self, cr, uid, c: c.get('active_id', False),
115 }
116 # _sql_constraints = [(
117 # 'company_from_company_to_unique', 'unique(company_from, company_to)',
118 # 'A setup for that company already exists')]
119
120 def check_need_create_intercompany_object(
121 self, cr, uid, company_from, company_to, concept,
122 event, return_list_type=False, regular=False):
123 """ @company_from
124 @company_to
125 @o2o_field_name file name.Example, so2po po2so ,,,,
126 @node value of o2o_field_name , draft, confirm
127 @return_list_type, the return type is list or boolean
128 """
129
130 if type(company_to) != list:
131 company_to = [company_to]
132 request = [('company_from', '=', company_from),
133 ('concept', '=', concept),
134 (event, '=', True)]
135 if not regular:
136 request.append(('company_to', 'in', company_to))
137 intercompany_ids = self.search(
138 cr, uid, request)
139 if return_list_type:
140 return intercompany_ids
141 else:
142 return intercompany_ids and True or False
0143
=== added file 'base_intercompany/connector.py'
--- base_intercompany/connector.py 1970-01-01 00:00:00 +0000
+++ base_intercompany/connector.py 2014-02-23 01:10:59 +0000
@@ -0,0 +1,136 @@
1# -*- coding: utf-8 -*-
2##############################################################################
3#
4# OpenERP, Open Source Management Solution
5# Copyright (c) 2010-2014 Elico Corp. All Rights Reserved.
6# Augustin Cisterne-Kaas <augustin.cisterne-kaas@elico-corp.com>
7# Eric Caudal <eric.caudal@elico-corp.com>
8
9# This program is free software: you can redistribute it and/or modify
10# it under the terms of the GNU Affero General Public License as
11# published by the Free Software Foundation, either version 3 of the
12# License, or (at your option) any later version.
13#
14# This program is distributed in the hope that it will be useful,
15# but WITHOUT ANY WARRANTY; without even the implied warranty of
16# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17# GNU Affero General Public License for more details.
18#
19# You should have received a copy of the GNU Affero General Public License
20# along with this program. If not, see <http://www.gnu.org/licenses/>.
21#
22##############################################################################
23
24from openerp.osv import orm, osv, fields
25from openerp.addons.connector.connector import Environment
26from openerp.addons.connector.checkpoint import checkpoint
27
28
29class base_intercompany_installed(orm.AbstractModel):
30 """Empty model used to know if the module is installed on the
31 database.
32
33 If the model is in the registry, the module is installed.
34 """
35 _name = 'base_intercompany.installed'
36
37
38def get_environment(session, model_name, backend_id):
39 """ Create an environment to work with. """
40 backend_record = session.browse('icops.backend', backend_id)
41 env = Environment(backend_record, session, model_name)
42 lang_code = 'en_US'
43 env.set_lang(code=lang_code)
44 return env
45
46
47class icops_binding(orm.AbstractModel):
48 _name = 'icops.binding'
49 _inherit = 'external.binding'
50 _description = 'Coswin Binding (abstract)'
51
52 _columns = {
53 'backend_id': fields.many2one(
54 'icops.backend',
55 'ICOPS Backend',
56 required=True,
57 ondelete='restrict'),
58 'icops_ids': fields.one2many(
59 'icops.record', 'binding_id',
60 string="ICOPS Record"),
61 }
62
63
64class icops_record(orm.Model):
65 _name = 'icops.record'
66
67 _columns = {
68 'binding_id': fields.integer('ICOPS Binding'),
69 'record_id': fields.integer('ICOPS ID'),
70 'model': fields.char('Model'),
71 'concept': fields.char('Concept'),
72 'backend_id': fields.many2one(
73 'icops.backend', 'ICOPS Backends'),
74 }
75
76
77class icops_model(orm.AbstractModel):
78 _name = 'icops.model'
79
80 def _is_locked(self, cr, uid, ids, name, arg, context=None):
81 res = {}
82 for obj in self.browse(cr, uid, ids, context=context):
83 pool = self.pool.get('icops.record')
84 record_ids = pool.search(
85 cr, uid, [('record_id', '=', obj.id),
86 ('model', '=', self._name)])
87
88 res[obj.id] = True if record_ids else False
89 return res
90
91 def _check_icops(self, cr, uid, ids, context=None):
92 context = context or {}
93 if isinstance(ids, int):
94 ids = [ids]
95 if 'icops' in context:
96 return
97 fields = ['locked', 'temp_unlock']
98 for obj in self.read(cr, uid, ids, fields, context=context):
99 if obj['temp_unlock']:
100 return
101 elif obj['locked']:
102 raise osv.except_osv(
103 'ICOPS Error',
104 'This object is locked by an intercompany process')
105
106 _columns = {
107 'locked': fields.function(
108 _is_locked, type='boolean', string='Is Locked', store=False),
109 # To handle special workflow
110 'temp_unlock': fields.boolean('Temporary Unlock')
111 }
112
113 def action_unlock(self, cr, uid, ids, context):
114 pool = self.pool.get('icops.record')
115 for obj in self.browse(cr, uid, ids, context=context):
116 record_ids = pool.search(
117 cr, uid, [('record_id', '=', obj.id),
118 ('model', '=', self._name)])
119 pool.unlink(cr, uid, record_ids, context=context)
120
121
122def add_checkpoint(session, model_name, record_id, backend_id):
123 """ Add a row in the model ``connector.checkpoint`` for a record,
124 meaning it has to be reviewed by a user.
125
126 :param session: current session
127 :type session: :py:class:openerp.addons.connector.session.ConnectorSession
128 :param model_name: name of the model of the record to be reviewed
129 :type model_name: str
130 :param record_id: ID of the record to be reviewed
131 :type record_id: int
132 :param backend_id: ID of the Coswin Backend
133 :type backend_id: int
134 """
135 return checkpoint.add_checkpoint(session, model_name, record_id,
136 'icops.backend', backend_id)
0137
=== added file 'base_intercompany/consumer.py'
--- base_intercompany/consumer.py 1970-01-01 00:00:00 +0000
+++ base_intercompany/consumer.py 2014-02-23 01:10:59 +0000
@@ -0,0 +1,77 @@
1# -*- coding: utf-8 -*-
2##############################################################################
3#
4# OpenERP, Open Source Management Solution
5# Copyright (c) 2010-2014 Elico Corp. All Rights Reserved.
6# Augustin Cisterne-Kaas <augustin.cisterne-kaas@elico-corp.com>
7# Eric Caudal <eric.caudal@elico-corp.com>
8
9# This program is free software: you can redistribute it and/or modify
10# it under the terms of the GNU Affero General Public License as
11# published by the Free Software Foundation, either version 3 of the
12# License, or (at your option) any later version.
13#
14# This program is distributed in the hope that it will be useful,
15# but WITHOUT ANY WARRANTY; without even the implied warranty of
16# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17# GNU Affero General Public License for more details.
18#
19# You should have received a copy of the GNU Affero General Public License
20# along with this program. If not, see <http://www.gnu.org/licenses/>.
21#
22##############################################################################
23from openerp.addons.connector.event import (on_record_write,
24 on_record_create,
25 on_record_unlink
26 )
27from .unit.export_synchronizer import (
28 export_record)
29
30_MODEL_NAMES = ()
31_BIND_MODEL_NAMES = ()
32_UNLINK_MODEL_NAMES = ()
33_UNLINK_BIND_MODEL_NAMES = ()
34
35
36@on_record_create(model_names=_BIND_MODEL_NAMES)
37@on_record_write(model_names=_BIND_MODEL_NAMES)
38def delay_export(session, model_name, record_id, fields=None):
39 """ Delay a job which export a binding record.
40
41 (A binding record being a ``icops.res.partner``,
42 ``icops.sale.order``, ...)
43 """
44 export_record(session, model_name, record_id, fields=fields)
45
46
47@on_record_write(model_names=_MODEL_NAMES)
48def delay_export_all_bindings(session, model_name, record_id, fields=None):
49 """ Delay a job which export all the bindings of a record.
50
51 In this case, it is called on records of normal models and will delay
52 the export for all the bindings.
53 """
54 model = session.pool.get(model_name)
55 record = model.browse(session.cr, session.uid,
56 record_id, context=session.context)
57 for binding in record.icops_bind_ids:
58 export_record(session, binding._model._name, binding.id,
59 fields=fields)
60
61
62@on_record_unlink(model_names=_UNLINK_MODEL_NAMES)
63def delay_unlink(session, model_name, record_id):
64 """ Delay a job which delete a record on Magento.
65
66 Called on binding records."""
67 fields = {'icops_delete': True}
68 delay_export_all_bindings(session, model_name, record_id, fields)
69
70
71@on_record_unlink(model_names=_UNLINK_BIND_MODEL_NAMES)
72def delay_unlink_binding(session, model_name, record_id):
73 """ Delay a job which delete a record on Magento.
74
75 Called on binding records."""
76 fields = {'icops_delete': True}
77 delay_export(session, model_name, record_id, fields)
078
=== added file 'base_intercompany/icops_model.py'
--- base_intercompany/icops_model.py 1970-01-01 00:00:00 +0000
+++ base_intercompany/icops_model.py 2014-02-23 01:10:59 +0000
@@ -0,0 +1,92 @@
1# -*- coding: utf-8 -*-
2##############################################################################
3#
4# OpenERP, Open Source Management Solution
5# Copyright (c) 2010-2014 Elico Corp. All Rights Reserved.
6# Augustin Cisterne-Kaas <augustin.cisterne-kaas@elico-corp.com>
7# Eric Caudal <eric.caudal@elico-corp.com>
8
9# This program is free software: you can redistribute it and/or modify
10# it under the terms of the GNU Affero General Public License as
11# published by the Free Software Foundation, either version 3 of the
12# License, or (at your option) any later version.
13#
14# This program is distributed in the hope that it will be useful,
15# but WITHOUT ANY WARRANTY; without even the implied warranty of
16# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17# GNU Affero General Public License for more details.
18#
19# You should have received a copy of the GNU Affero General Public License
20# along with this program. If not, see <http://www.gnu.org/licenses/>.
21#
22##############################################################################
23
24import logging
25from openerp.osv import fields, orm
26
27_logger = logging.getLogger(__name__)
28
29
30class icops_backend(orm.Model):
31 _name = 'icops.backend'
32 _description = 'ICOPS Backend'
33 _inherit = 'connector.backend'
34
35 _backend_type = 'icops'
36
37 def _select_versions(self, cr, uid, context=None):
38 """ Available versions
39
40 Can be inherited to add custom versions.
41 """
42 return [('7.0', '7.0')]
43
44 _columns = {
45 'version': fields.selection(
46 _select_versions,
47 string='Version',
48 required=True),
49 'company_id': fields.many2one(
50 'res.company', string="Company", required=True),
51 'icops_ids': fields.one2many(
52 'res.intercompany', 'backend_id', 'Inter-Company Setup'),
53 'icops_uid': fields.many2one(
54 'res.users', 'IC User',
55 required=True,
56 domain="[('company_id', '=', company_id)]",
57 help="User to create update unlink IC records"),
58 'model': fields.related(
59 'backend_to', 'icops_uid', type='many2one',
60 relation='res.users', readonly=True, string='IC User'),
61 }
62
63 _defaults = {
64 'version': '7.0',
65 }
66
67 def _icops_backend(self, cr, uid, callback, domain=None,
68 context=None):
69 if domain is None:
70 domain = []
71 ids = self.search(cr, uid, domain, context=context)
72 if ids:
73 callback(cr, uid, ids, context=context)
74
75 def prepare_binding(self, cr, uid, data, context=None):
76 context = context or {}
77 icops_bind_ids = []
78 if not 'icops_bind_ids' in data:
79 data['icops_bind_ids'] = None
80 if not 'icops' in context and not data['icops_bind_ids']:
81 user = self.pool.get('res.users').browse(
82 cr, uid, uid, context)
83 backend_pool = self.pool.get('icops.backend')
84 backend_ids = backend_pool.search(
85 cr, uid, [('company_id', '=', user.company_id.id)])
86 if backend_ids:
87 backends = backend_pool.browse(
88 cr, uid, backend_ids, context)
89 for backend in backends:
90 icops_bind_ids.append(
91 (0, 0, {'backend_id': backend.id}))
92 return icops_bind_ids
093
=== added file 'base_intercompany/icops_model_view.xml'
--- base_intercompany/icops_model_view.xml 1970-01-01 00:00:00 +0000
+++ base_intercompany/icops_model_view.xml 2014-02-23 01:10:59 +0000
@@ -0,0 +1,59 @@
1<?xml version="1.0" encoding="utf-8"?>
2<openerp>
3 <data>
4 <record id="view_icops_backend_form" model="ir.ui.view">
5 <field name="name">icops.backend.form</field>
6 <field name="model">icops.backend</field>
7 <field name="arch" type="xml">
8 <form string="ICOPS Backend" version="7.0">
9 <sheet>
10 <label for="name" class="oe_edit_only"/>
11 <h1>
12 <field name="name" class="oe_inline" />
13 </h1>
14 <group name="icops" string="ICOPS Configuration">
15 <group colspan="4" col="4">
16 <field name="version" cols="4" />
17 <field name="company_id" cols="2" />
18 <field name="icops_uid" cols="2" />
19 </group>
20 </group>
21 <field name="icops_ids" context="{'active_id': active_id}">
22 <tree editable="bottom">
23 <field name="concept"/>
24 <field name="model" invisible="1" />
25 <field name="backend_id" invisible="1" />
26 <field name="backend_to" />
27 <field name='icops_uid' invisible="1" />
28 <field name="on_create" />
29 <field name="on_write" />
30 <field name="on_unlink" />
31 <field name="on_confirm" />
32 <field name="on_cancel" />
33 </tree>
34 </field>
35 </sheet>
36 </form>
37 </field>
38 </record>
39
40 <record id="view_icops_backend_tree" model="ir.ui.view">
41 <field name="name">icops.backend.tree</field>
42 <field name="model">icops.backend</field>
43 <field name="arch" type="xml">
44 <tree string="ICOPS Backend" version="7.0">
45 <field name="name"/>
46 <field name="company_id" />
47 </tree>
48 </field>
49 </record>
50
51 <record id="action_icops_backend" model="ir.actions.act_window">
52 <field name="name">ICOPS Backends</field>
53 <field name="res_model">icops.backend</field>
54 <field name="view_type">form</field>
55 <field name="view_mode">tree,form</field>
56 <field name="view_id" ref="view_icops_backend_tree"/>
57 </record>
58 </data>
59</openerp>
060
=== added directory 'base_intercompany/security'
=== added file 'base_intercompany/security/ir.model.access.csv'
--- base_intercompany/security/ir.model.access.csv 1970-01-01 00:00:00 +0000
+++ base_intercompany/security/ir.model.access.csv 2014-02-23 01:10:59 +0000
@@ -0,0 +1,3 @@
1id,name,model_id:id,group_id:id,perm_read,perm_write,perm_create,perm_unlink
2access_res_intercompany_manager,res intercompany manager,base_intercompany.model_res_intercompany,connector.group_connector_manager,1,1,1,1
3access_icops_backend_manager,icops backend manager,base_intercompany.model_icops_backend,connector.group_connector_manager,1,1,1,1
0\ No newline at end of file4\ No newline at end of file
15
=== added directory 'base_intercompany/static'
=== added directory 'base_intercompany/static/description'
=== added file 'base_intercompany/static/description/Intercompany_setup.png'
2Binary 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 differ6Binary 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
=== added file 'base_intercompany/static/description/index.html'
--- base_intercompany/static/description/index.html 1970-01-01 00:00:00 +0000
+++ base_intercompany/static/description/index.html 2014-02-23 01:10:59 +0000
@@ -0,0 +1,25 @@
1<section class="oe_container">
2 <div class="oe_row">
3 <h2 class="oe_slogan">base_intercompany</h2>
4 <h3 class="oe_slogan">Inter-Company Processing (ICOPS) <br /></h3>
5 <h4 class="oe_slogan"><a href="http://www.elico-corp.com">By Elico Corp</a></h4>
6 <p>
7 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:
8 </p>
9 <ul>
10 <li>Base Intercompany Sale</li>
11 <li>Base Intercompany Stock (in development)</li>
12 <li>Any module which extends the Base Intercompany module</li>
13 </ul>
14 <p>
15 TODO: demo data to be improved<br />
16 Blueprint: <a href="https://blueprints.launchpad.net/multi-company/+spec/icops">https://blueprints.launchpad.net/multi-company/+spec/icops</a><br />
17 </p>
18 <div class="oe_row_img oe_centered oe_mt32">
19 <img class="oe_picture oe_screenshot" src="Intercompany_setup.png" />
20 </div>
21 </div>
22 <div class="oe_row oe_centeralign oe_more_space">
23 <a href="http://www.elico-corp.com/saas/" class="oe_button oe_big">Start your <span class="oe_emph">free</span> trial</a>
24 </div>
25</section>
026
=== added directory 'base_intercompany/static/src'
=== added directory 'base_intercompany/static/src/img'
=== added file 'base_intercompany/static/src/img/icon.png'
1Binary 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 differ27Binary 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
=== added directory 'base_intercompany/unit'
=== added file 'base_intercompany/unit/__init__.py'
--- base_intercompany/unit/__init__.py 1970-01-01 00:00:00 +0000
+++ base_intercompany/unit/__init__.py 2014-02-23 01:10:59 +0000
@@ -0,0 +1,26 @@
1# -*- coding: utf-8 -*-
2##############################################################################
3#
4# OpenERP, Open Source Management Solution
5# Copyright (c) 2010-2014 Elico Corp. All Rights Reserved.
6# Augustin Cisterne-Kaas <augustin.cisterne-kaas@elico-corp.com>
7# Eric Caudal <eric.caudal@elico-corp.com>
8#
9# This program is free software: you can redistribute it and/or modify
10# it under the terms of the GNU Affero General Public License as
11# published by the Free Software Foundation, either version 3 of the
12# License, or (at your option) any later version.
13#
14# This program is distributed in the hope that it will be useful,
15# but WITHOUT ANY WARRANTY; without even the implied warranty of
16# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17# GNU Affero General Public License for more details.
18#
19# You should have received a copy of the GNU Affero General Public License
20# along with this program. If not, see <http://www.gnu.org/licenses/>.
21#
22##############################################################################
23
24import binder
25import export_synchronizer
26import backend_adapter
027
=== added file 'base_intercompany/unit/backend_adapter.py'
--- base_intercompany/unit/backend_adapter.py 1970-01-01 00:00:00 +0000
+++ base_intercompany/unit/backend_adapter.py 2014-02-23 01:10:59 +0000
@@ -0,0 +1,151 @@
1# -*- coding: utf-8 -*-
2##############################################################################
3#
4# OpenERP, Open Source Management Solution
5# Copyright (c) 2010-2014 Elico Corp. All Rights Reserved.
6# Augustin Cisterne-Kaas <augustin.cisterne-kaas@elico-corp.com>
7# Eric Caudal <eric.caudal@elico-corp.com>
8
9# This program is free software: you can redistribute it and/or modify
10# it under the terms of the GNU Affero General Public License as
11# published by the Free Software Foundation, either version 3 of the
12# License, or (at your option) any later version.
13#
14# This program is distributed in the hope that it will be useful,
15# but WITHOUT ANY WARRANTY; without even the implied warranty of
16# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17# GNU Affero General Public License for more details.
18#
19# You should have received a copy of the GNU Affero General Public License
20# along with this program. If not, see <http://www.gnu.org/licenses/>.
21#
22##############################################################################
23
24import logging
25from openerp.addons.connector.unit.backend_adapter import CRUDAdapter
26
27_logger = logging.getLogger(__name__)
28recorder = {}
29
30
31def call_to_key(method, arguments):
32 """ Used to 'freeze' the method and arguments of a call to Coswin
33 so they can be hashable; they will be stored in a dict.
34
35 Used in both the recorder and the tests.
36 """
37 def freeze(arg):
38 if isinstance(arg, dict):
39 items = dict((key, freeze(value)) for key, value
40 in arg.iteritems())
41 return frozenset(items.iteritems())
42 elif isinstance(arg, list):
43 return tuple([freeze(item) for item in arg])
44 else:
45 return arg
46
47 new_args = []
48 for arg in arguments:
49 new_args.append(freeze(arg))
50 return (method, tuple(new_args))
51
52
53def record(method, arguments, result):
54 """ Utility function which can be used to record test data
55 during synchronisations. Call it from CoswinOracleAdapter._call
56
57 Then ``output_recorder`` can be used to write the data recorded
58 to a file.
59 """
60 recorder[call_to_key(method, arguments)] = result
61
62
63def output_recorder(filename):
64 import pprint
65 with open(filename, 'w') as f:
66 pprint.pprint(recorder, f)
67 _logger.debug('recorder written to file %s', filename)
68
69
70# class ICOPSLocation(object):
71
72# def __init__(self, location, db_name, username, password):
73# self.location = location
74# self.db_name = db_name
75# self.username = username
76# self.password = password
77
78
79class GenericAdapter(CRUDAdapter):
80
81 """ External Records Adapter for Coswin """
82
83 def __init__(self, environment):
84 """
85
86 :param environment: current environment (backend, session, ...)
87 :type environment: :py:class:`connector.connector.Environment`
88 """
89 super(GenericAdapter, self).__init__(environment)
90 # self.icops = ICOPSLocation(self.backend_record.icops_server,
91 # self.backend_record.backup_server)
92
93 def search(self, filters=None):
94 """ Search records according to some criterias
95 and returns a list of ids """
96 raise NotImplementedError
97
98 def read(self, id, attributes=None):
99 """ Returns the information of a record """
100 raise NotImplementedError
101
102 def search_read(self, filters=None):
103 """ Search records according to some criterias
104 and returns their information"""
105 raise NotImplementedError
106
107 def create(self, data):
108 """ Create a record on the external system """
109 raise NotImplementedError
110
111 def write(self, id, data):
112 """ Update records on the external system """
113 raise NotImplementedError
114
115 def delete(self, id):
116 """ Delete a record on the external system """
117 raise NotImplementedError
118
119 def _write(self, text):
120 return True
121
122
123class ICOPSAdapter(GenericAdapter):
124
125 _model_name = None
126
127 def __init__(self, environment):
128 super(GenericAdapter, self).__init__(environment)
129 self._icops = None
130 self._backend_to = None
131
132 def _get_pool(self):
133 raise NotImplementedError
134
135 def create(self, data):
136 sess = self.session
137 pool = self._get_pool()
138 return pool.create(
139 sess.cr, self._backend_to.icops_uid.id, data, {'icops': True})
140
141 def write(self, id, data):
142 sess = self.session
143 pool = self._get_pool()
144 pool.write(sess.cr, self._backend_to.icops_uid.id, id, data,
145 {'icops': True})
146
147 def delete(self, id):
148 sess = self.session
149 pool = self._get_pool()
150 pool.unlink(sess.cr, self._backend_to.icops_uid.id, [id],
151 {'icops': True})
0152
=== added file 'base_intercompany/unit/binder.py'
--- base_intercompany/unit/binder.py 1970-01-01 00:00:00 +0000
+++ base_intercompany/unit/binder.py 2014-02-23 01:10:59 +0000
@@ -0,0 +1,122 @@
1# -*- coding: utf-8 -*-
2##############################################################################
3#
4# OpenERP, Open Source Management Solution
5# Copyright (c) 2010-2014 Elico Corp. All Rights Reserved.
6# Augustin Cisterne-Kaas <augustin.cisterne-kaas@elico-corp.com>
7# Eric Caudal <eric.caudal@elico-corp.com>
8
9# This program is free software: you can redistribute it and/or modify
10# it under the terms of the GNU Affero General Public License as
11# published by the Free Software Foundation, either version 3 of the
12# License, or (at your option) any later version.
13#
14# This program is distributed in the hope that it will be useful,
15# but WITHOUT ANY WARRANTY; without even the implied warranty of
16# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17# GNU Affero General Public License for more details.
18#
19# You should have received a copy of the GNU Affero General Public License
20# along with this program. If not, see <http://www.gnu.org/licenses/>.
21#
22##############################################################################
23
24from datetime import datetime
25from openerp.tools import DEFAULT_SERVER_DATETIME_FORMAT
26from openerp.addons.connector.connector import Binder
27from ..backend import icops
28
29
30class ICOPSBinder(Binder):
31 """ Generic Binder for ICOPS """
32
33
34@icops
35class ICOPSModelBinder(ICOPSBinder):
36 """
37 Bindings are done directly on the binding model.
38
39 Binding models are models called ``icops.{normal_model}``,
40 like ``icops.res.partner`` or ``icops.sale.order``.
41 They are ``_inherits`` of the normal models and contains
42 the ICOPS ID, the ID of the ICOPS Backend and the additional
43 fields belonging to the ICOPS instance.
44 """
45 _model_name = [
46 'icops.sale.order',
47 'icops.sale.order.line',
48 'icops.purchase.order',
49 'icops.purchase.order.line'
50 ]
51
52 def to_openerp(self, external_id, unwrap=False):
53 """ Give the OpenERP ID for an external ID
54
55 :param external_id: external ID for which we want the OpenERP ID
56 :param unwrap: if True, returns the openerp_id of the icops_xxxx
57 record, else return the id (binding id) of that record
58 :return: a record ID, depending on the value of unwrap,
59 or None if the external_id is not mapped
60 :rtype: int
61 """
62 binding_ids = self.session.search(
63 self.model._name,
64 [('icops_id', '=', external_id),
65 ('backend_id', '=', self.backend_record.id)])
66 if not binding_ids:
67 return None
68 assert len(binding_ids) == 1, "Several records found: %s" % binding_ids
69 binding_id = binding_ids[0]
70 if unwrap:
71 return self.session.read(self.model._name,
72 binding_id,
73 ['openerp_id'])['openerp_id'][0]
74 else:
75 return binding_id
76
77 def to_backend(self, binding_id):
78 """ Give the external ID for an OpenERP ID
79
80 :param binding_id: OpenERP ID for which we want the external id
81 :return: backend identifier of the record
82 """
83 record = self.session.browse(self.model._name,
84 binding_id)
85 assert record
86 res = {}
87 for icops in record.icops_ids:
88 key = '%s_%s' % (icops.backend_id.id, icops.concept)
89 res[key] = {'id': icops.record_id}
90 return res
91
92 def bind(self, records, binding_id):
93 """ Create the link between an external ID and an OpenERP ID and
94 update the last synchronization date.
95
96 :param external_ids: External ID to bind
97 :param binding_id: OpenERP ID to bind
98 :type binding_id: int
99 """
100 # avoid to trigger the export when we modify the `icops_id`
101 if not records:
102 return
103 context = self.session.context.copy()
104 context['icops'] = True
105 now_fmt = datetime.now().strftime(DEFAULT_SERVER_DATETIME_FORMAT)
106 icops_ids = []
107 for record, record_id in records.items():
108 backend_id, concept = record.split('_')
109
110 icops_id = (0, 0, {'record_id': record_id['id'],
111 'backend_id': backend_id,
112 'concept': concept,
113 'binding_id': binding_id,
114 'model': record_id['model']})
115 icops_ids.append(icops_id)
116
117 self.environment.model.write(
118 self.session.cr,
119 self.session.uid,
120 binding_id,
121 {'icops_ids': icops_ids, 'sync_date': now_fmt},
122 context=context)
0123
=== added file 'base_intercompany/unit/export_synchronizer.py'
--- base_intercompany/unit/export_synchronizer.py 1970-01-01 00:00:00 +0000
+++ base_intercompany/unit/export_synchronizer.py 2014-02-23 01:10:59 +0000
@@ -0,0 +1,258 @@
1# -*- coding: utf-8 -*-
2##############################################################################
3#
4# OpenERP, Open Source Management Solution
5# Copyright (c) 2010-2014 Elico Corp. All Rights Reserved.
6# Augustin Cisterne-Kaas <augustin.cisterne-kaas@elico-corp.com>
7# Eric Caudal <eric.caudal@elico-corp.com>
8
9# This program is free software: you can redistribute it and/or modify
10# it under the terms of the GNU Affero General Public License as
11# published by the Free Software Foundation, either version 3 of the
12# License, or (at your option) any later version.
13#
14# This program is distributed in the hope that it will be useful,
15# but WITHOUT ANY WARRANTY; without even the implied warranty of
16# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17# GNU Affero General Public License for more details.
18#
19# You should have received a copy of the GNU Affero General Public License
20# along with this program. If not, see <http://www.gnu.org/licenses/>.
21#
22##############################################################################
23
24import logging
25from openerp.tools.translate import _
26from openerp.addons.connector.queue.job import job
27from openerp.addons.connector.unit.synchronizer import ExportSynchronizer
28from ..connector import get_environment
29from openerp.addons.connector.exception import MappingError
30from osv import osv
31
32_logger = logging.getLogger(__name__)
33
34
35"""
36
37Exporters for ICOPS.
38
39In addition to its export job, an exporter has to:
40
41* check in ICOPS if the record has been updated more recently than the
42 last sync date and if yes, delay an import
43* call the ``bind`` method of the binder to update the last sync date
44
45"""
46
47
48class ICOPSBaseExporter(ExportSynchronizer):
49
50 """ Base exporter for ICOPS """
51
52 def __init__(self, environment):
53 """
54 :param environment: current environment (backend, session, ...)
55 :type environment: :py:class:`connector.connector.Environment`
56 """
57 super(ICOPSBaseExporter, self).__init__(environment)
58 self.binding_id = None
59 self.icops_ids = {}
60
61 def _get_openerp_data(self):
62 """ Return the raw OpenERP data for ``self.binding_id`` """
63 return self.session.browse(self.model._name, self.binding_id)
64
65 def run(self, binding_id, *args, **kwargs):
66 """ Run the synchronization
67
68 :param binding_id: identifier of the binding record to export
69 """
70 self.binding_id = binding_id
71 self.binding_record = self._get_openerp_data()
72
73 self.icops_ids = self.binder.to_backend(self.binding_id)
74 result = self._run(*args, **kwargs)
75 self.binder.bind(self.icops_ids, self.binding_id)
76 return result
77
78 def _run(self):
79 """ Flow of the synchronization, implemented in inherited classes"""
80 raise NotImplementedError
81
82
83class ICOPSExporter(ICOPSBaseExporter):
84
85 _concepts = None
86 """ A common flow for the exports to ICOPS """
87 def __init__(self, environment):
88 """
89 :param environment: current environment (backend, session, ...)
90 :type environment: :py:class:`connector.connector.Environment`
91 """
92 super(ICOPSExporter, self).__init__(environment)
93 self.binding_record = None
94
95 def _has_to_skip(self):
96 """ Return True if the export can be skipped """
97 return False
98
99 def _routing(self, record, fields=None):
100 fields = fields or []
101 icops = self.mapper._icops
102 icops_id = self._get_icops_id(
103 icops.backend_to.id, icops.concept)
104 if not icops_id:
105 return
106 if 'icops_delete' in fields:
107 self._delete(icops_id)
108 else:
109 self._custom_routing(icops_id, record, fields)
110
111 def _custom_routing(self, id, record, fields=None):
112 self._write(id, record)
113
114 def _run(self, fields=None):
115 """ Flow of the synchronization, implemented in inherited classes"""
116 assert self.binding_id
117 assert self.binding_record
118
119 if not self.icops_ids:
120 fields = None # should be created with all the fields
121
122 if self._has_to_skip():
123 return
124
125 nb_records = 0
126 icops_ids = {}
127
128 for icops in self._get_icops():
129 backend = self._get_backend_with_permission(icops)
130 self._set_icops(icops, backend)
131 map_record = self._map_data(fields=fields)
132 if self.icops_ids:
133 record = None
134 try:
135 record = self._update_data(map_record, fields=fields)
136 except MappingError as e:
137 continue
138 if not record:
139 continue
140 nb_records += 1
141 self._validate_data(record)
142 self._routing(record, fields)
143 else:
144 record = None
145 try:
146 record = self._create_data(map_record, fields=fields)
147 except MappingError as e:
148 continue
149 if not record:
150 continue
151 nb_records += 1
152 key = '%s_%s' % (icops.backend_to.id, icops.concept)
153 icops_ids[key] = {
154 'id': self._create(record),
155 'model': self.backend_adapter._get_pool()._name
156 }
157
158 self.icops_ids = icops_ids
159
160 if nb_records == 0:
161 return _('Nothing to export.')
162 return _('Record exported.')
163
164 def _get_backend_with_permission(self, icops):
165 sess = self.session
166 backend_pool = sess.pool.get('icops.backend')
167 return backend_pool.browse(
168 sess.cr, icops.icops_uid.id, icops.backend_to.id)
169
170 def _get_icops(self):
171 res = []
172 sess = self.session
173 user_pool = sess.pool.get('res.users')
174 user = user_pool.browse(sess.cr, sess.uid, sess.uid)
175 backend_pool = sess.pool.get('icops.backend')
176 backend_ids = backend_pool.search(
177 sess.cr, sess.uid, [('company_id', '=', user.company_id.id)])
178 if not backend_ids:
179 return res
180 intercompany_pool = sess.pool.get('res.intercompany')
181 intercompany_ids = intercompany_pool.search(
182 sess.cr, sess.uid,
183 [('backend_id', '=', backend_ids[0]),
184 ('concept', 'in', self._concepts),
185 ('model', '=', self.binding_record.openerp_id._name)])
186 res = intercompany_pool.browse(sess.cr, sess.uid, intercompany_ids)
187 return res
188
189 def _set_icops(self, icops, backend):
190 self.mapper._icops = icops
191 self.mapper._backend_to = backend
192 self.backend_adapter._icops = icops
193 self.backend_adapter._backend_to = backend
194
195 def _create(self, data):
196 if not self.backend_adapter._icops.on_create:
197 raise osv.except_osv('ICOPS Error', 'Can\'t create')
198 self._validate_data(data)
199 return self.backend_adapter.create(data)
200
201 def _write(self, id, data):
202 context = self.session.context or {}
203 if not self.backend_adapter._icops.on_write and not 'icops' in context:
204 raise osv.except_osv('ICOPS Error', 'Can\'t write')
205 self.backend_adapter.write(id, data)
206
207 def _confirm(self, id):
208 if not self.backend_adapter._icops.on_confirm:
209 raise osv.except_osv('ICOPS Error', 'Can\'t confirm')
210 self.backend_adapter.confirm(id)
211
212 def _cancel(self, id):
213 if not self.backend_adapter._icops.on_cancel:
214 raise osv.except_osv('ICOPS Error', 'Can\'t cancel')
215 self.backend_adapter.cancel(id)
216
217 def _delete(self, id):
218 if not self.backend_adapter._icops.on_unlink:
219 raise osv.except_osv('ICOPS Error', 'Can\'t delete')
220 self.backend_adapter.delete(id)
221
222 def _map_data(self, fields=None):
223 """ Convert the external record to OpenERP """
224 return self.mapper.map_record(self.binding_record)
225
226 def _create_data(self, map_record, fields=None, **kwargs):
227 """ Get the data to pass to :py:meth:`_create` """
228 return map_record.values(for_create=True, fields=fields, **kwargs)
229
230 def _update_data(self, map_record, fields=None, **kwargs):
231 """ Get the data to pass to :py:meth:`_update` """
232 return map_record.values(fields=fields, **kwargs)
233
234 def _validate_data(self, data):
235 """ Check if the values to export are correct
236
237 Pro-actively check before the ``Model.create`` or
238 ``Model.update`` if some fields are missing
239
240 Raise `InvalidDataError`
241 """
242 return
243
244 def _get_icops_id(self, backend_id, concept):
245 key = '%s_%s' % (backend_id, concept)
246 try:
247 return self.icops_ids[key]['id']
248 except:
249 return None
250
251
252@job
253def export_record(session, model_name, binding_id, fields=None):
254 """ Export a record on ICOPS """
255 record = session.browse(model_name, binding_id)
256 env = get_environment(session, model_name, record.backend_id.id)
257 exporter = env.get_connector_unit(ICOPSExporter)
258 return exporter.run(binding_id, fields=fields)
0259
=== added file 'base_intercompany/unit/mapper.py'
--- base_intercompany/unit/mapper.py 1970-01-01 00:00:00 +0000
+++ base_intercompany/unit/mapper.py 2014-02-23 01:10:59 +0000
@@ -0,0 +1,66 @@
1# -*- coding: utf-8 -*-
2##############################################################################
3#
4# OpenERP, Open Source Management Solution
5# Copyright (c) 2010-2014 Elico Corp. All Rights Reserved.
6# Augustin Cisterne-Kaas <augustin.cisterne-kaas@elico-corp.com>
7# Eric Caudal <eric.caudal@elico-corp.com>
8
9# This program is free software: you can redistribute it and/or modify
10# it under the terms of the GNU Affero General Public License as
11# published by the Free Software Foundation, either version 3 of the
12# License, or (at your option) any later version.
13#
14# This program is distributed in the hope that it will be useful,
15# but WITHOUT ANY WARRANTY; without even the implied warranty of
16# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17# GNU Affero General Public License for more details.
18#
19# You should have received a copy of the GNU Affero General Public License
20# along with this program. If not, see <http://www.gnu.org/licenses/>.
21#
22##############################################################################
23from openerp.addons.connector.unit.mapper import (ExportMapper, ExportMapChild)
24
25
26class ICOPSExportMapChild(ExportMapChild):
27 """ :py:class:`MapChild` for the Exports """
28
29 def _child_mapper(self):
30 mapper = self.get_connector_unit_for_model(
31 ExportMapper, self.model._name)
32 mapper._icops = self._icops
33 mapper._backend_to = self._backend_to
34 return mapper
35
36 def format_items(self, items_values):
37 items = super(ICOPSExportMapChild, self).format_items(items_values)
38 return [(5, 0)] + [(0, 0, data) for data in items]
39
40
41class ICOPSExportMapper(ExportMapper):
42 _map_child_class = ICOPSExportMapChild
43
44 def __init__(self, environment):
45 """
46
47 :param environment: current environment (backend, session, ...)
48 :type environment: :py:class:`connector.connector.Environment`
49 """
50 super(ICOPSExportMapper, self).__init__(environment)
51 self._icops = None
52 self._backend_to = None
53
54 def _get_map_child_unit(self, model_name):
55 mapper = super(ICOPSExportMapper, self)._get_map_child_unit(model_name)
56 mapper._icops = self._icops
57 mapper._backend_to = self._backend_to
58 return mapper
59
60 def _get_mapping(self, name, record):
61 res = {}
62 for method in dir(self):
63 if method.startswith('%s_' % name):
64 new_dict = getattr(self, method)(record)
65 res = dict(res.items() + new_dict.items())
66 return res
067
=== added directory 'base_intercompany_sale'
=== added file 'base_intercompany_sale/__init__.py'
--- base_intercompany_sale/__init__.py 1970-01-01 00:00:00 +0000
+++ base_intercompany_sale/__init__.py 2014-02-23 01:10:59 +0000
@@ -0,0 +1,28 @@
1# -*- coding: utf-8 -*-
2##############################################################################
3#
4# OpenERP, Open Source Management Solution
5# Copyright (c) 2010-2014 Elico Corp. All Rights Reserved.
6# Augustin Cisterne-Kaas <augustin.cisterne-kaas@elico-corp.com>
7# Eric Caudal <eric.caudal@elico-corp.com>
8
9# This program is free software: you can redistribute it and/or modify
10# it under the terms of the GNU Affero General Public License as
11# published by the Free Software Foundation, either version 3 of the
12# License, or (at your option) any later version.
13#
14# This program is distributed in the hope that it will be useful,
15# but WITHOUT ANY WARRANTY; without even the implied warranty of
16# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17# GNU Affero General Public License for more details.
18#
19# You should have received a copy of the GNU Affero General Public License
20# along with this program. If not, see <http://www.gnu.org/licenses/>.
21#
22##############################################################################
23import connector
24import consumer
25import company
26import icops_model
27import sale
28import purchase
029
=== added file 'base_intercompany_sale/__openerp__.py'
--- base_intercompany_sale/__openerp__.py 1970-01-01 00:00:00 +0000
+++ base_intercompany_sale/__openerp__.py 2014-02-23 01:10:59 +0000
@@ -0,0 +1,50 @@
1# -*- coding: utf-8 -*-
2##############################################################################
3#
4# OpenERP, Open Source Management Solution
5# Copyright (c) 2010-2014 Elico Corp. All Rights Reserved.
6# Augustin Cisterne-Kaas <augustin.cisterne-kaas@elico-corp.com>
7# Eric Caudal <eric.caudal@elico-corp.com>
8
9# This program is free software: you can redistribute it and/or modify
10# it under the terms of the GNU Affero General Public License as
11# published by the Free Software Foundation, either version 3 of the
12# License, or (at your option) any later version.
13#
14# This program is distributed in the hope that it will be useful,
15# but WITHOUT ANY WARRANTY; without even the implied warranty of
16# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17# GNU Affero General Public License for more details.
18#
19# You should have received a copy of the GNU Affero General Public License
20# along with this program. If not, see <http://www.gnu.org/licenses/>.
21#
22##############################################################################
23
24{'name': 'Base Intercompany Sale',
25 'version': '0.4',
26 'category': 'Sales Management',
27 'depends': ['base_intercompany', 'sale', 'purchase'],
28 'author': 'Elico Corp',
29 'license': 'AGPL-3',
30 'website': 'https://www.elico-corp.com',
31 'description': """
32This module is an extension designed to manage Inter-company Process (ICOPS)
33and allows 2 companies to create Sale Orders and Purchase Order in each other.
34 - Sale Order to Purchase Order (so2po)
35 - Sale Order to Sale Order (so2so)
36 - Purchase Order to Sale Order (po2so)
37 - Handles the following events: Create, Update, Delete, Confirm and Cancel
38
39TODO: demo data to be improved.\n
40Blueprint: https://blueprints.launchpad.net/multi-company/+spec/icops
41""",
42 'images': [],
43 'demo': ['base_intercompany_sale_demo.xml'],
44 'data': ['security/ir.model.access.csv',
45 'icops_model_view.xml',
46 'sale_view.xml',
47 'purchase_view.xml'],
48 'installable': True,
49 'application': False,
50 }
051
=== added file 'base_intercompany_sale/base_intercompany_sale_demo.xml'
--- base_intercompany_sale/base_intercompany_sale_demo.xml 1970-01-01 00:00:00 +0000
+++ base_intercompany_sale/base_intercompany_sale_demo.xml 2014-02-23 01:10:59 +0000
@@ -0,0 +1,53 @@
1<?xml version="1.0" encoding="utf-8"?>
2<openerp>
3 <data noupdate="1">
4
5 <record id="base_intercompany.user_origin" model="res.users">
6 <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'))]"/>
7 </record>
8
9 <record id="base_intercompany.user_destination" model="res.users">
10 <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'))]"/>
11 </record>
12
13 <record id="warehouse_origin" model="stock.warehouse">
14 <field name="name">Warehouse Origin</field>
15 <field name="company_id" ref="base_intercompany.company_origin" />
16 </record>
17
18 <record id="warehouse_destination" model="stock.warehouse">
19 <field name="name">Warehouse Destination</field>
20 <field name="company_id" ref="base_intercompany.company_destination" />
21 </record>
22
23 <record id="shop_origin" model="sale.shop">
24 <field name="name">Shop Origin</field>
25 <field name="warehouse_id" ref="warehouse_origin" />
26 <field name="payment_default_id" ref="account.account_payment_term_immediate" />
27 <field name="company_id" ref="base_intercompany.company_origin" />
28 </record>
29
30 <record id="shop_destination" model="sale.shop">
31 <field name="name">Shop Destination</field>
32 <field name="warehouse_id" ref="warehouse_destination" />
33 <field name="payment_default_id" ref="account.account_payment_term_immediate" />
34 <field name="company_id" ref="base_intercompany.company_destination" />
35 </record>
36
37 <!-- ICOPS Setup -->
38 <record id="base_intercompany.backend_origin" model="icops.backend">
39 <field name="icops_shop_id" ref="shop_origin" />
40 <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})]" />
41 </record>
42
43 <record id="base_intercompany.backend_destination" model="icops.backend">
44 <field name="icops_shop_id" ref="shop_destination" />
45 </record>
46
47 <!-- Remove company_id from stock -->
48 <function model="stock.location" name="write">
49 <function eval="[[]]" model="stock.location" name="search"/>
50 <value eval="{'company_id': None}" />
51 </function>
52 </data>
53</openerp>
0\ No newline at end of file54\ No newline at end of file
155
=== added file 'base_intercompany_sale/company.py'
--- base_intercompany_sale/company.py 1970-01-01 00:00:00 +0000
+++ base_intercompany_sale/company.py 2014-02-23 01:10:59 +0000
@@ -0,0 +1,56 @@
1 # -*- coding: utf-8 -*-
2##############################################################################
3#
4# OpenERP, Open Source Management Solution
5# Copyright (c) 2010-2014 Elico Corp. All Rights Reserved.
6# Augustin Cisterne-Kaas <augustin.cisterne-kaas@elico-corp.com>
7# Eric Caudal <eric.caudal@elico-corp.com>
8
9# This program is free software: you can redistribute it and/or modify
10# it under the terms of the GNU Affero General Public License as
11# published by the Free Software Foundation, either version 3 of the
12# License, or (at your option) any later version.
13#
14# This program is distributed in the hope that it will be useful,
15# but WITHOUT ANY WARRANTY; without even the implied warranty of
16# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17# GNU Affero General Public License for more details.
18#
19# You should have received a copy of the GNU Affero General Public License
20# along with this program. If not, see <http://www.gnu.org/licenses/>.
21#
22##############################################################################
23from openerp.osv import fields, orm
24
25
26class res_intercompany(orm.Model):
27 _inherit = 'res.intercompany'
28
29 def _select_concepts(self, cr, uid, context=None):
30 """ Available concepts
31
32 Can be inherited to add custom versions.
33 """
34 res = super(res_intercompany, self)._select_concepts(cr, uid, context)
35 res += [('so2po', 'SO to PO'),
36 ('so2so', 'SO to SO'),
37 ('po2so', 'PO to SO')]
38 return res
39
40 def _select_models(self, cr, uid, context=None):
41 """ Available Object names
42
43 Can be inherited to add custom versions.
44 """
45 res = super(res_intercompany, self)._select_models(
46 cr, uid, context)
47 new_dict = {'so2po': 'sale.order',
48 'so2so': 'sale.order',
49 'po2so': 'purchase.order'}
50 res = dict(res.items() + new_dict.items())
51 return res
52
53 _columns = {
54 'concept': fields.selection(_select_concepts, string="Concept",
55 required=True),
56 }
057
=== added file 'base_intercompany_sale/connector.py'
--- base_intercompany_sale/connector.py 1970-01-01 00:00:00 +0000
+++ base_intercompany_sale/connector.py 2014-02-23 01:10:59 +0000
@@ -0,0 +1,32 @@
1# -*- coding: utf-8 -*-
2##############################################################################
3#
4# OpenERP, Open Source Management Solution
5# Copyright (c) 2010-2014 Elico Corp. All Rights Reserved.
6# Augustin Cisterne-Kaas <augustin.cisterne-kaas@elico-corp.com>
7# Eric Caudal <eric.caudal@elico-corp.com>
8
9# This program is free software: you can redistribute it and/or modify
10# it under the terms of the GNU Affero General Public License as
11# published by the Free Software Foundation, either version 3 of the
12# License, or (at your option) any later version.
13#
14# This program is distributed in the hope that it will be useful,
15# but WITHOUT ANY WARRANTY; without even the implied warranty of
16# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17# GNU Affero General Public License for more details.
18#
19# You should have received a copy of the GNU Affero General Public License
20# along with this program. If not, see <http://www.gnu.org/licenses/>.
21#
22##############################################################################
23from openerp.osv import orm
24
25
26class base_intercompany_sale_installed(orm.AbstractModel):
27 """Empty model used to know if the module is installed on the
28 database.
29
30 If the model is in the registry, the module is installed.
31 """
32 _name = 'base_intercompany_sale.installed'
033
=== added file 'base_intercompany_sale/consumer.py'
--- base_intercompany_sale/consumer.py 1970-01-01 00:00:00 +0000
+++ base_intercompany_sale/consumer.py 2014-02-23 01:10:59 +0000
@@ -0,0 +1,79 @@
1# -*- coding: utf-8 -*-
2##############################################################################
3#
4# OpenERP, Open Source Management Solution
5# Copyright (c) 2010-2014 Elico Corp. All Rights Reserved.
6# Augustin Cisterne-Kaas <augustin.cisterne-kaas@elico-corp.com>
7# Eric Caudal <eric.caudal@elico-corp.com>
8
9# This program is free software: you can redistribute it and/or modify
10# it under the terms of the GNU Affero General Public License as
11# published by the Free Software Foundation, either version 3 of the
12# License, or (at your option) any later version.
13#
14# This program is distributed in the hope that it will be useful,
15# but WITHOUT ANY WARRANTY; without even the implied warranty of
16# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17# GNU Affero General Public License for more details.
18#
19# You should have received a copy of the GNU Affero General Public License
20# along with this program. If not, see <http://www.gnu.org/licenses/>.
21#
22##############################################################################
23from openerp.addons.connector.event import (on_record_write,
24 on_record_create,
25 on_record_unlink
26 )
27from openerp.addons.base_intercompany.unit.export_synchronizer import (
28 export_record)
29
30_MODEL_NAMES = ('sale.order', 'sale.order.line',
31 'purchase.order', 'purchase.order.line')
32_BIND_MODEL_NAMES = ('icops.sale.order', 'icops.sale.order.line',
33 'icops.purchase.order', 'icops.purchase.order.line')
34_UNLINK_MODEL_NAMES = ('sale.order', 'purchase.order')
35_UNLINK_BIND_MODEL_NAMES = ('icops.sale.order', 'icops.purchase.order')
36
37
38@on_record_create(model_names=_BIND_MODEL_NAMES)
39@on_record_write(model_names=_BIND_MODEL_NAMES)
40def delay_export(session, model_name, record_id, fields=None):
41 """ Delay a job which export a binding record.
42
43 (A binding record being a ``icops.res.partner``,
44 ``icops.sale.order``, ...)
45 """
46 export_record(session, model_name, record_id, fields=fields)
47
48
49@on_record_write(model_names=_MODEL_NAMES)
50def delay_export_all_bindings(session, model_name, record_id, fields=None):
51 """ Delay a job which export all the bindings of a record.
52
53 In this case, it is called on records of normal models and will delay
54 the export for all the bindings.
55 """
56 model = session.pool.get(model_name)
57 record = model.browse(session.cr, session.uid,
58 record_id, context=session.context)
59 for binding in record.icops_bind_ids:
60 export_record(session, binding._model._name, binding.id,
61 fields=fields)
62
63
64@on_record_unlink(model_names=_UNLINK_MODEL_NAMES)
65def delay_unlink(session, model_name, record_id):
66 """ Delay a job which delete a record on Magento.
67
68 Called on binding records."""
69 fields = {'icops_delete': True}
70 delay_export_all_bindings(session, model_name, record_id, fields)
71
72
73@on_record_unlink(model_names=_UNLINK_BIND_MODEL_NAMES)
74def delay_unlink_binding(session, model_name, record_id):
75 """ Delay a job which delete a record on Magento.
76
77 Called on binding records."""
78 fields = {'icops_delete': True}
79 delay_export(session, model_name, record_id, fields)
080
=== added file 'base_intercompany_sale/icops_model.py'
--- base_intercompany_sale/icops_model.py 1970-01-01 00:00:00 +0000
+++ base_intercompany_sale/icops_model.py 2014-02-23 01:10:59 +0000
@@ -0,0 +1,33 @@
1# -*- coding: utf-8 -*-
2##############################################################################
3#
4# OpenERP, Open Source Management Solution
5# Copyright (c) 2010-2014 Elico Corp. All Rights Reserved.
6# Augustin Cisterne-Kaas <augustin.cisterne-kaas@elico-corp.com>
7# Eric Caudal <eric.caudal@elico-corp.com>
8
9# This program is free software: you can redistribute it and/or modify
10# it under the terms of the GNU Affero General Public License as
11# published by the Free Software Foundation, either version 3 of the
12# License, or (at your option) any later version.
13#
14# This program is distributed in the hope that it will be useful,
15# but WITHOUT ANY WARRANTY; without even the implied warranty of
16# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17# GNU Affero General Public License for more details.
18#
19# You should have received a copy of the GNU Affero General Public License
20# along with this program. If not, see <http://www.gnu.org/licenses/>.
21#
22##############################################################################
23from openerp.osv import fields, orm
24
25
26class icops_backend(orm.Model):
27 _inherit = 'icops.backend'
28
29 _columns = {
30 'icops_shop_id': fields.many2one(
31 'sale.shop', 'IC Default location',
32 required=True),
33 }
034
=== added file 'base_intercompany_sale/icops_model_view.xml'
--- base_intercompany_sale/icops_model_view.xml 1970-01-01 00:00:00 +0000
+++ base_intercompany_sale/icops_model_view.xml 2014-02-23 01:10:59 +0000
@@ -0,0 +1,15 @@
1<?xml version="1.0" encoding="UTF-8"?>
2<openerp>
3 <data>
4 <record id="view_icops_backend_form" model="ir.ui.view">
5 <field name="name">icops.backend.form</field>
6 <field name="model">icops.backend</field>
7 <field name="inherit_id" ref="base_intercompany.view_icops_backend_form" />
8 <field name="arch" type="xml">
9 <field name="icops_uid" position="after">
10 <field name="icops_shop_id" cols="2" />
11 </field>
12 </field>
13 </record>
14 </data>
15</openerp>
0\ No newline at end of file16\ No newline at end of file
117
=== added file 'base_intercompany_sale/purchase.py'
--- base_intercompany_sale/purchase.py 1970-01-01 00:00:00 +0000
+++ base_intercompany_sale/purchase.py 2014-02-23 01:10:59 +0000
@@ -0,0 +1,296 @@
1# -*- coding: utf-8 -*-
2##############################################################################
3#
4# OpenERP, Open Source Management Solution
5# Copyright (c) 2010-2014 Elico Corp. All Rights Reserved.
6# Augustin Cisterne-Kaas <augustin.cisterne-kaas@elico-corp.com>
7# Eric Caudal <eric.caudal@elico-corp.com>
8
9# This program is free software: you can redistribute it and/or modify
10# it under the terms of the GNU Affero General Public License as
11# published by the Free Software Foundation, either version 3 of the
12# License, or (at your option) any later version.
13#
14# This program is distributed in the hope that it will be useful,
15# but WITHOUT ANY WARRANTY; without even the implied warranty of
16# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17# GNU Affero General Public License for more details.
18#
19# You should have received a copy of the GNU Affero General Public License
20# along with this program. If not, see <http://www.gnu.org/licenses/>.
21#
22##############################################################################
23from openerp.osv import fields, orm
24from openerp.addons.base_intercompany.backend import icops
25from openerp.addons.base_intercompany.unit.export_synchronizer import (
26 ICOPSExporter)
27from openerp.addons.base_intercompany.unit.mapper import ICOPSExportMapper
28from openerp.addons.connector.unit.mapper import mapping
29from openerp.addons.base_intercompany.unit.backend_adapter import (
30 ICOPSAdapter)
31from openerp.addons.connector.exception import MappingError
32
33
34class purchase_order(orm.Model):
35 _name = 'purchase.order'
36 _inherit = ['purchase.order', 'icops.model']
37
38 _columns = {
39 'openerp_id': fields.many2one('purchase.order',
40 string='Sale Order',
41 required=True,
42 ondelete='cascade'),
43 'icops_bind_ids': fields.one2many(
44 'icops.purchase.order', 'openerp_id',
45 string="ICOPS Bindings"),
46 }
47
48 def copy_data(self, cr, uid, id, default=None, context=None):
49 if default is None:
50 default = {}
51 default['icops_bind_ids'] = False
52 return super(purchase_order, self).copy_data(cr, uid, id,
53 default=default,
54 context=context)
55
56 def create(self, cr, uid, data, context=None):
57 data['icops_bind_ids'] = self.pool.get(
58 'icops.backend').prepare_binding(cr, uid, data, context)
59 return super(purchase_order, self).create(cr, uid, data, context)
60
61 def write(self, cr, uid, ids, data, context=None):
62 self._check_icops(cr, uid, ids, context=context)
63 return super(purchase_order, self).write(cr, uid, ids, data, context)
64
65 def unlink(self, cr, uid, ids, context=None):
66 self._check_icops(cr, uid, ids, context=context)
67 return super(purchase_order, self).unlink(cr, uid, ids, context)
68
69
70class icops_purchase_order(orm.Model):
71 _name = 'icops.purchase.order'
72 _inherit = 'icops.binding'
73 _inherits = {'purchase.order': 'openerp_id'}
74 _description = 'ICOPS Purchase Order'
75
76 _columns = {
77 'openerp_id': fields.many2one('purchase.order',
78 string='Product',
79 required=True,
80 ondelete='cascade'),
81 'backend_id': fields.many2one('icops.backend',
82 string='ICOPS Backend'),
83 'icops_order_line_ids': fields.one2many('icops.purchase.order.line',
84 'icops_order_id',
85 'ICOPS Order Lines'),
86 }
87
88
89class purchase_order_line(orm.Model):
90 _inherit = 'purchase.order.line'
91 _columns = {
92 'icops_bind_ids': fields.one2many(
93 'icops.purchase.order.line', 'openerp_id',
94 string="ICOPS Bindings"),
95 }
96
97 def copy_data(self, cr, uid, id, default=None, context=None):
98 if default is None:
99 default = {}
100 default['icops_bind_ids'] = False
101 return super(purchase_order_line, self).copy_data(cr, uid, id,
102 default=default,
103 context=context)
104
105
106class icops_purchase_order_line(orm.Model):
107 _name = 'icops.purchase.order.line'
108 _inherit = 'icops.binding'
109 _description = 'ICOPS Sale Order Line'
110 _inherits = {'purchase.order.line': 'openerp_id'}
111
112 def _get_lines_from_order(self, cr, uid, ids, context=None):
113 line_obj = self.pool.get('icops.purchase.order.line')
114 return line_obj.search(cr, uid,
115 [('icops_order_id', 'in', ids)],
116 context=context)
117 _columns = {
118 'icops_order_id': fields.many2one('icops.purchase.order',
119 'ICOPS Sale Order',
120 required=True,
121 ondelete='cascade',
122 select=True),
123 'openerp_id': fields.many2one('purchase.order.line',
124 string='Sale Order Line',
125 required=True,
126 ondelete='cascade'),
127 'backend_id': fields.related(
128 'icops_order_id', 'backend_id',
129 type='many2one',
130 relation='icops.backend',
131 string='ICOPS Backend',
132 store={'icops.purchase.order.line':
133 (lambda self, cr, uid, ids, c=None: ids,
134 ['icops_order_id'],
135 10),
136 'icops.purchase.order':
137 (_get_lines_from_order, ['backend_id'], 20),
138 },
139 readonly=True)
140 }
141
142 def create(self, cr, uid, vals, context=None):
143 icops_order_id = vals['icops_order_id']
144 info = self.pool['icops.purchase.order'].read(cr, uid,
145 [icops_order_id],
146 ['openerp_id'],
147 context=context)
148 order_id = info[0]['openerp_id']
149 vals['order_id'] = order_id[0]
150 return super(icops_purchase_order_line, self).create(cr, uid, vals,
151 context=context)
152
153
154@icops
155class PurchaseOrderAdapter(ICOPSAdapter):
156 _model_name = 'icops.purchase.order'
157
158 def _get_pool(self):
159 sess = self.session
160 return sess.pool.get('sale.order')
161
162 def confirm(self, id):
163 sess = self.session
164 pool = self._get_pool()
165 context = {'icops': True}
166 pool.write(
167 sess.cr, self._backend_to.icops_uid.id, [id],
168 {'temp_unlock': True}, context=context)
169 pool.action_wait(
170 sess.cr, self._backend_to.icops_uid.id, [id])
171 pool.write(
172 sess.cr, self._backend_to.icops_uid.id, [id],
173 {'temp_unlock': False}, context=context)
174
175 def cancel(self, id):
176 sess = self.session
177 pool = self._get_pool()
178 context = {'icops': True}
179 pool.write(
180 sess.cr, self._backend_to.icops_uid.id, [id],
181 {'temp_unlock': True}, context=context)
182 pool.action_cancel(sess.cr, self._backend_to.icops_uid.id, [id])
183 pool.write(
184 sess.cr, self._backend_to.icops_uid.id, [id],
185 {'temp_unlock': False}, context=context)
186
187
188@icops
189class PurchaseOrderExport(ICOPSExporter):
190 _model_name = ['icops.purchase.order']
191 _concepts = ['po2so']
192
193 def _custom_routing(self, id, record, fields=None):
194 if 'state' in fields:
195 state = record.pop('state')
196 if state == 'cancel':
197 self._cancel(id)
198 elif state == 'progress':
199 self._confirm(id)
200 elif fields:
201 self._write(id, record)
202
203
204@icops
205class PurchaseOrderExportMapper(ICOPSExportMapper):
206 _model_name = 'icops.purchase.order'
207
208 children = [
209 ('order_line', 'order_line', 'icops.purchase.order.line')
210 ]
211
212 @mapping
213 def origin(self, record):
214 return {'origin': 'ICOPS: %s' % record.name}
215
216 @mapping
217 def state(self, record):
218 state = record.state
219 if record.state == 'approved':
220 state = 'progress'
221 return {'state': state}
222
223 @mapping
224 def address(self, record):
225 sess = self.session
226 partner = record.company_id.partner_id
227 partner_pool = sess.pool.get('res.partner')
228 addr = partner_pool.address_get(sess.cr, sess.uid, [partner.id],
229 ['delivery', 'invoice', 'contact'])
230 return {
231 'partner_invoice_id': addr['invoice'],
232 'partner_shipping_id': addr['delivery']
233 }
234
235 @mapping
236 def icops(self, record):
237 if not self._backend_to:
238 raise MappingError("Could not find an ICOPS backend")
239 sess = self.session
240 backend = self._backend_to
241 ic_uid = backend.icops_uid.id
242 company = backend.company_id
243 partner_pool = sess.pool.get('res.partner')
244 partner_id = record.company_id.partner_id.id
245 partner = partner_pool.browse(sess.cr, ic_uid, partner_id)
246 pricelist = (partner.property_product_pricelist.id
247 if partner.property_product_pricelist
248 else False)
249 fiscal_position = partner.property_account_position
250 payment_term = partner.property_payment_term
251 shop = self._backend_to.icops_shop_id
252 if company.partner_id.id != record.partner_id.id:
253 raise MappingError("Wrong partner")
254 return {
255 'company_id': company.id,
256 'partner_id': partner.id,
257 'pricelist_id': pricelist,
258 'fiscal_position': fiscal_position.id,
259 'payment_term': payment_term.id,
260 'user_id': ic_uid,
261 'shop_id': shop.id
262 }
263
264
265@icops
266class PurchaseOrderLineExportMapper(ICOPSExportMapper):
267 _model_name = 'icops.purchase.order.line'
268
269 @mapping
270 def product(self, record):
271 return {
272 'name': record.name,
273 'product_id': record.product_id.id,
274 'product_uom': record.product_uom.id,
275 'product_uom_qty': record.product_qty
276 }
277
278 @mapping
279 def price(self, record):
280 if not record.product_id:
281 return {'price_unit': 0}
282 sess = self.session
283 backend = self._backend_to
284 ic_uid = backend.icops_uid.id
285 partner_pool = sess.pool.get('res.partner')
286 partner_id = record.order_id.company_id.partner_id.id
287 partner = partner_pool.browse(sess.cr, ic_uid, partner_id)
288 pricelist_id = (partner.property_product_pricelist.id
289 if partner.property_product_pricelist
290 else False)
291 pricelist_pool = sess.pool.get('product.pricelist')
292 price_unit = pricelist_pool.price_get(
293 sess.cr, ic_uid, [pricelist_id],
294 record.product_id.id, record.product_qty)
295 price = price_unit[int(pricelist_id)]
296 return {'price_unit': price}
0297
=== added file 'base_intercompany_sale/purchase_view.xml'
--- base_intercompany_sale/purchase_view.xml 1970-01-01 00:00:00 +0000
+++ base_intercompany_sale/purchase_view.xml 2014-02-23 01:10:59 +0000
@@ -0,0 +1,26 @@
1<?xml version="1.0" encoding="UTF-8"?>
2<openerp>
3 <data>
4 <record id="purchase_order_form" model="ir.ui.view">
5 <field name="name">purchase.order.form</field>
6 <field name="model">purchase.order</field>
7 <field name="inherit_id" ref="purchase.purchase_order_form" />
8 <field name="arch" type="xml">
9 <xpath expr="//header/button[last()]" position="after">
10 <button name="action_unlock" string="Unlock" type="object" attrs="{'invisible':[('locked', '=', False)]}" class="oe_highlight" />
11 </xpath>
12 <xpath expr="//page[last()]" position="after">
13 <page string="ICOPS">
14 <field name="locked" invisible="1" />
15 <field name="icops_bind_ids">
16 <tree string="ICOPS Backend" editable="bottom">
17 <field name="backend_id" />
18 <field name="icops_ids" />
19 </tree>
20 </field>
21 </page>
22 </xpath>
23 </field>
24 </record>
25 </data>
26</openerp>
0\ No newline at end of file27\ No newline at end of file
128
=== added file 'base_intercompany_sale/sale.py'
--- base_intercompany_sale/sale.py 1970-01-01 00:00:00 +0000
+++ base_intercompany_sale/sale.py 2014-02-23 01:10:59 +0000
@@ -0,0 +1,390 @@
1# -*- coding: utf-8 -*-
2##############################################################################
3#
4# OpenERP, Open Source Management Solution
5# Copyright (c) 2010-2014 Elico Corp. All Rights Reserved.
6# Augustin Cisterne-Kaas <augustin.cisterne-kaas@elico-corp.com>
7# Eric Caudal <eric.caudal@elico-corp.com>
8
9# This program is free software: you can redistribute it and/or modify
10# it under the terms of the GNU Affero General Public License as
11# published by the Free Software Foundation, either version 3 of the
12# License, or (at your option) any later version.
13#
14# This program is distributed in the hope that it will be useful,
15# but WITHOUT ANY WARRANTY; without even the implied warranty of
16# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17# GNU Affero General Public License for more details.
18#
19# You should have received a copy of the GNU Affero General Public License
20# along with this program. If not, see <http://www.gnu.org/licenses/>.
21#
22##############################################################################
23from openerp.osv import fields, orm
24from openerp.addons.base_intercompany.backend import icops
25from openerp.addons.base_intercompany.unit.export_synchronizer import (
26 ICOPSExporter)
27from openerp.addons.base_intercompany.unit.mapper import ICOPSExportMapper
28from openerp.addons.connector.unit.mapper import mapping
29from openerp.addons.connector.exception import MappingError
30from openerp.addons.base_intercompany.unit.backend_adapter import ICOPSAdapter
31
32
33class sale_order(orm.Model):
34 _name = 'sale.order'
35 _inherit = ['sale.order', 'icops.model']
36
37 _columns = {
38 'openerp_id': fields.many2one('sale.order',
39 string='Sale Order',
40 required=True,
41 ondelete='cascade'),
42 'icops_bind_ids': fields.one2many(
43 'icops.sale.order', 'openerp_id',
44 string="ICOPS Bindings"),
45 }
46
47 def copy_data(self, cr, uid, id, default=None, context=None):
48 if default is None:
49 default = {}
50 default['icops_bind_ids'] = False
51 return super(sale_order, self).copy_data(cr, uid, id,
52 default=default,
53 context=context)
54
55 def create(self, cr, uid, data, context=None):
56 data['icops_bind_ids'] = self.pool.get(
57 'icops.backend').prepare_binding(cr, uid, data, context)
58 return super(sale_order, self).create(cr, uid, data, context)
59
60 def write(self, cr, uid, ids, data, context=None):
61 self._check_icops(cr, uid, ids, context=context)
62 return super(sale_order, self).write(cr, uid, ids, data, context)
63
64 def unlink(self, cr, uid, ids, context=None):
65 self._check_icops(cr, uid, ids, context=context)
66 return super(sale_order, self).unlink(cr, uid, ids, context)
67
68
69class icops_sale_order(orm.Model):
70 _name = 'icops.sale.order'
71 _inherit = 'icops.binding'
72 _inherits = {'sale.order': 'openerp_id'}
73 _description = 'ICOPS Sale Order'
74
75 _columns = {
76 'openerp_id': fields.many2one('sale.order',
77 string='SaleOrder',
78 required=True,
79 ondelete='cascade'),
80 'backend_id': fields.many2one('icops.backend',
81 string='ICOPS Backend'),
82 'icops_order_line_ids': fields.one2many('icops.sale.order.line',
83 'icops_order_id',
84 'ICOPS Order Lines'),
85
86 }
87
88
89class sale_order_line(orm.Model):
90 _inherit = 'sale.order.line'
91 _columns = {
92 'icops_bind_ids': fields.one2many(
93 'icops.sale.order.line', 'openerp_id',
94 string="ICOPS Bindings"),
95 }
96
97 def copy_data(self, cr, uid, id, default=None, context=None):
98 if default is None:
99 default = {}
100 default['icops_bind_ids'] = False
101 return super(sale_order_line, self).copy_data(cr, uid, id,
102 default=default,
103 context=context)
104
105
106class icops_sale_order_line(orm.Model):
107 _name = 'icops.sale.order.line'
108 _inherit = 'icops.binding'
109 _description = 'ICOPS Sale Order Line'
110 _inherits = {'sale.order.line': 'openerp_id'}
111
112 def _get_lines_from_order(self, cr, uid, ids, context=None):
113 line_obj = self.pool.get('icops.sale.order.line')
114 return line_obj.search(cr, uid,
115 [('icops_order_id', 'in', ids)],
116 context=context)
117 _columns = {
118 'icops_order_id': fields.many2one('icops.sale.order',
119 'ICOPS Sale Order',
120 required=True,
121 ondelete='cascade',
122 select=True),
123 'openerp_id': fields.many2one('sale.order.line',
124 string='Sale Order Line',
125 required=True,
126 ondelete='cascade'),
127 'backend_id': fields.related(
128 'icops_order_id', 'backend_id',
129 type='many2one',
130 relation='icops.backend',
131 string='ICOPS Backend',
132 store={'icops.sale.order.line':
133 (lambda self, cr, uid, ids, c=None: ids,
134 ['icops_order_id'],
135 10),
136 'icops.sale.order':
137 (_get_lines_from_order, ['backend_id'], 20),
138 },
139 readonly=True)
140 }
141
142 def create(self, cr, uid, vals, context=None):
143 icops_order_id = vals['icops_order_id']
144 info = self.pool['icops.sale.order'].read(cr, uid,
145 [icops_order_id],
146 ['openerp_id'],
147 context=context)
148 order_id = info[0]['openerp_id']
149 vals['order_id'] = order_id[0]
150 return super(icops_sale_order_line, self).create(cr, uid, vals,
151 context=context)
152
153
154@icops
155class SaleOrderAdapter(ICOPSAdapter):
156 _model_name = 'icops.sale.order'
157
158 def _get_pool(self):
159 sess = self.session
160 name = ('purchase.order'
161 if self._icops.concept == 'so2po' else 'sale.order')
162 return sess.pool.get(name)
163
164 def confirm(self, id):
165 sess = self.session
166 pool = self._get_pool()
167 context = {'icops': True}
168 pool.write(
169 sess.cr, self._backend_to.icops_uid.id, [id],
170 {'temp_unlock': True}, context)
171 if hasattr(pool, 'wkf_confirm_order'):
172 pool.wkf_confirm_order(
173 sess.cr, self._backend_to.icops_uid.id, [id])
174 else:
175 pool.action_wait(
176 sess.cr, self._backend_to.icops_uid.id, [id])
177 pool.write(
178 sess.cr, self._backend_to.icops_uid.id, [id],
179 {'temp_unlock': False}, context)
180
181 def cancel(self, id):
182 sess = self.session
183 pool = self._get_pool()
184 context = {'icops': True}
185 pool.write(
186 sess.cr, self._backend_to.icops_uid.id, [id],
187 {'temp_unlock': True}, context)
188 pool.action_cancel(sess.cr, self._backend_to.icops_uid.id, [id])
189 pool.write(
190 sess.cr, self._backend_to.icops_uid.id, [id],
191 {'temp_unlock': False}, context)
192
193
194@icops
195class SaleOrderExport(ICOPSExporter):
196 _model_name = ['icops.sale.order']
197 _concepts = ['so2po', 'so2so']
198
199 def _custom_routing(self, id, record, fields=None):
200 if 'state' in fields:
201 state = record.pop('state')
202 if state == 'cancel':
203 self._cancel(id)
204 elif state in ('approved', 'progress', 'manual'):
205 self._confirm(id)
206 elif fields:
207 self._write(id, record)
208
209
210@icops
211class SaleOrderExportMapper(ICOPSExportMapper):
212 _model_name = 'icops.sale.order'
213
214 children = [
215 ('order_line', 'order_line', 'icops.sale.order.line')
216 ]
217
218 def _partner(self, record, is_po=False):
219 sess = self.session
220 backend = self._backend_to
221 ic_uid = backend.icops_uid.id
222 partner_pool = sess.pool.get('res.partner')
223 partner_id = record.company_id.partner_id.id
224 partner = partner_pool.browse(sess.cr, ic_uid, partner_id)
225 pricelist_id = None
226 payment_term_id = None
227 if is_po:
228 pricelist_id = (partner.property_product_pricelist_purchase.id
229 if partner.property_product_pricelist_purchase
230 else False)
231 payment_term_id = (partner.property_supplier_payment_term.id
232 if partner.property_supplier_payment_term
233 else False)
234 else:
235 pricelist_id = (partner.property_product_pricelist.id
236 if partner.property_product_pricelist
237 else False)
238 payment_term_id = (partner.property_payment_term.id
239 if partner.property_payment_term
240 else False)
241 fiscal_position_id = (partner.property_account_position.id
242 if partner.property_account_position
243 else False)
244 return {'partner_id': partner.id,
245 'pricelist_id': pricelist_id,
246 'payment_term_id': payment_term_id,
247 'fiscal_position': fiscal_position_id}
248
249 @mapping
250 def origin(self, record):
251 return {'origin': 'ICOPS: %s' % record.name}
252
253 @mapping
254 def map_all(self, record):
255 assert self._icops
256 return self._get_mapping(self._icops.concept, record)
257
258 def so2so_icops(self, record):
259 if not self._backend_to:
260 raise MappingError("Could not find an ICOPS backend")
261 backend = self._backend_to
262 icops_uid = backend.icops_uid.id
263 company = backend.company_id
264 partner = company.partner_id
265 pricelist = partner.property_product_pricelist
266 fiscal_position = partner.property_account_position
267 payment_term = partner.property_payment_term
268 shop = self._backend_to.icops_shop_id
269
270 return {
271 'company_id': company.id,
272 'partner_id': partner.id,
273 'pricelist_id': pricelist.id,
274 'fiscal_position': fiscal_position.id,
275 'payment_term': payment_term.id,
276 'user_id': icops_uid,
277 'shop_id': shop.id
278 }
279
280 def so2so_partner(self, record):
281 return self._partner(record)
282
283 def so2so_address(self, record):
284 sess = self.session
285 partner = record.company_id.partner_id
286 partner_pool = sess.pool.get('res.partner')
287 addr = partner_pool.address_get(sess.cr, sess.uid, [partner.id],
288 ['delivery', 'invoice', 'contact'])
289 return {
290 'partner_invoice_id': addr['invoice'],
291 'partner_shipping_id': addr['delivery']
292 }
293
294 def so2so_policy(self, record):
295 return {'order_policy': record.order_policy}
296
297 def so2so_state(self, record):
298 return {'state': record.state}
299
300 def so2po_partner(self, record):
301 return self._partner(record, True)
302
303 def so2po_icops(self, record):
304 if not self._backend_to:
305 raise MappingError("Could not find an ICOPS backend")
306 backend = self._backend_to
307 company = backend.company_id
308 shop = backend.icops_shop_id
309 warehouse = shop.warehouse_id
310 location = warehouse.lot_stock_id
311
312 if company.partner_id.id != record.partner_id.id:
313 raise MappingError("Wrong partner")
314 return {
315 'company_id': company.id,
316 'location_id': location.id
317 }
318
319 def so2po_state(self, record):
320 state = record.state
321 if record.state in ('progress', 'manual'):
322 state = 'approved'
323 return {'state': state}
324
325
326@icops
327class SaleOrderLineExportMapper(ICOPSExportMapper):
328 _model_name = 'icops.sale.order.line'
329
330 @mapping
331 def name(self, record):
332 return {'name': record.name}
333
334 def _price(self, record, is_po=False):
335 if not record.product_id:
336 return {'price_unit': 0}
337 sess = self.session
338 backend = self._backend_to
339 ic_uid = backend.icops_uid.id
340 partner_pool = sess.pool.get('res.partner')
341 partner_id = record.order_id.company_id.partner_id.id
342 partner = partner_pool.browse(sess.cr, ic_uid, partner_id)
343 pricelist_id = None
344 if is_po:
345 pricelist_id = (partner.property_product_pricelist_purchase.id
346 if partner.property_product_pricelist_purchase
347 else False)
348 else:
349 pricelist_id = (partner.property_product_pricelist.id
350 if partner.property_product_pricelist
351 else False)
352 pricelist_pool = sess.pool.get('product.pricelist')
353 price_unit = pricelist_pool.price_get(
354 sess.cr, ic_uid, [pricelist_id],
355 record.product_id.id, record.product_uom_qty)
356 price = price_unit[int(pricelist_id)]
357 return {'price_unit': price}
358
359 @mapping
360 def map_all(self, record):
361 assert self._icops
362 return self._get_mapping(self._icops.concept, record)
363
364 def so2so_price(self, record):
365 return self._price(record)
366
367 def so2so_product(self, record):
368 return {
369 'product_id': record.product_id.id,
370 'product_uom': record.product_uom.id,
371 'product_uom_qty': record.product_uom_qty
372 }
373
374 def so2po_price(self, record):
375 return self._price(record, True)
376
377 def so2po_product(self, record):
378 return {
379 'product_id': record.product_id.id,
380 'product_uom': record.product_uom.id,
381 'product_qty': record.product_uom_qty
382 }
383
384 def so2po_date_planned(self, record):
385 sess = self.session
386 order = record.order_id
387 order_pool = sess.pool.get(order._name)
388 date_planned = order_pool._get_date_planned(
389 sess.cr, sess.uid, order, record, order.date_order)
390 return {'date_planned': date_planned}
0391
=== added file 'base_intercompany_sale/sale_view.xml'
--- base_intercompany_sale/sale_view.xml 1970-01-01 00:00:00 +0000
+++ base_intercompany_sale/sale_view.xml 2014-02-23 01:10:59 +0000
@@ -0,0 +1,26 @@
1<?xml version="1.0" encoding="UTF-8"?>
2<openerp>
3 <data>
4 <record id="view_order_form" model="ir.ui.view">
5 <field name="name">sale.order</field>
6 <field name="model">sale.order</field>
7 <field name="inherit_id" ref="sale.view_order_form" />
8 <field name="arch" type="xml">
9 <xpath expr="//header/button[last()]" position="after">
10 <button name="action_unlock" string="Unlock" type="object" attrs="{'invisible':[('locked', '=', False)]}" class="oe_highlight" />
11 </xpath>
12 <xpath expr="//page[last()]" position="after">
13 <page string="ICOPS">
14 <field name="locked" invisible="1" />
15 <field name="icops_bind_ids">
16 <tree string="ICOPS Backend" editable="bottom">
17 <field name="backend_id" />
18 <field name="icops_ids" />
19 </tree>
20 </field>
21 </page>
22 </xpath>
23 </field>
24 </record>
25 </data>
26</openerp>
0\ No newline at end of file27\ No newline at end of file
128
=== added directory 'base_intercompany_sale/security'
=== added file 'base_intercompany_sale/security/ir.model.access.csv'
--- base_intercompany_sale/security/ir.model.access.csv 1970-01-01 00:00:00 +0000
+++ base_intercompany_sale/security/ir.model.access.csv 2014-02-23 01:10:59 +0000
@@ -0,0 +1,21 @@
1id,name,model_id:id,group_id:id,perm_read,perm_write,perm_create,perm_unlink
2access_icops_sale_order_manager,icops sale order manager,base_intercompany_sale.model_icops_sale_order,base.group_sale_manager,1,1,1,1
3access_icops_sale_order_user,icops sale order user,base_intercompany_sale.model_icops_sale_order,base.group_sale_salesman,1,1,1,1
4access_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
5access_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
6access_icops_backend_manager,icops backend manager,base_intercompany.model_icops_backend,base.group_sale_manager,1,1,1,1
7access_icops_backend_user,icops backend user,base_intercompany.model_icops_backend,base.group_sale_salesman,1,1,1,1
8access_res_intercompany_manager,res intercompany manager,base_intercompany.model_res_intercompany,base.group_sale_manager,1,1,1,1
9access_res_intercompany_user,res intercompany user,base_intercompany.model_res_intercompany,base.group_sale_salesman,1,1,1,1
10access_icops_record_manager,icops record manager,base_intercompany.model_icops_record,base.group_sale_manager,1,1,1,1
11access_icops_record_user,icops record user,base_intercompany.model_icops_record,base.group_sale_salesman,1,1,1,1
12access_icops_purchase_order_manager,icops purchase order manager,base_intercompany_sale.model_icops_purchase_order,purchase.group_purchase_manager,1,1,1,1
13access_icops_purchase_order_user,icops purchase order user,base_intercompany_sale.model_icops_purchase_order,purchase.group_purchase_user,1,1,1,1
14access_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
15access_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
16access_icops_backend_manager,icops backend manager,base_intercompany.model_icops_backend,purchase.group_purchase_manager,1,1,1,1
17access_icops_backend_user,icops backend user,base_intercompany.model_icops_backend,purchase.group_purchase_user,1,1,1,1
18access_res_intercompany_manager,res intercompany manager,base_intercompany.model_res_intercompany,purchase.group_purchase_manager,1,1,1,1
19access_res_intercompany_user,res intercompany user,base_intercompany.model_res_intercompany,purchase.group_purchase_user,1,1,1,1
20access_icops_record_manager,icops record manager,base_intercompany.model_icops_record,purchase.group_purchase_manager,1,1,1,1
21access_icops_record_user,icops record user,base_intercompany.model_icops_record,purchase.group_purchase_user,1,1,1,1
0\ No newline at end of file22\ No newline at end of file
123
=== added directory 'base_intercompany_sale/static'
=== added directory 'base_intercompany_sale/static/description'
=== added file 'base_intercompany_sale/static/description/Intercompany_setup.png'
2Binary 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 differ24Binary 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
=== added file 'base_intercompany_sale/static/description/index.html'
--- base_intercompany_sale/static/description/index.html 1970-01-01 00:00:00 +0000
+++ base_intercompany_sale/static/description/index.html 2014-02-23 01:10:59 +0000
@@ -0,0 +1,26 @@
1<section class="oe_container">
2 <div class="oe_row">
3 <h2 class="oe_slogan">base_intercompany_sale</h2>
4 <h3 class="oe_slogan">Sale Inter-Company Processing (ICOPS) <br /></h3>
5 <h4 class="oe_slogan"><a href="http://www.elico-corp.com">By Elico Corp</a></h4>
6 <p>
7 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.
8 </p>
9 <ul>
10 <li>Sale Order to Purchase Order (so2po)</li>
11 <li>Sale Order to Sale Order (so2so)</li>
12 <li>Purchase Order to Sale Order (po2so)</li>
13 <li>Handles the following events: Create, Update, Delete, Confirm and Cancel</li>
14 </ul>
15 <p>
16 TODO: demo data to be improved.<br />
17 Blueprint: <a href="https://blueprints.launchpad.net/multi-company/+spec/icops">https://blueprints.launchpad.net/multi-company/+spec/icops</a><br />
18 </p>
19 <div class="oe_row_img oe_centered oe_mt32">
20 <img class="oe_picture oe_screenshot" src="Intercompany_setup.png" />
21 </div>
22 </div>
23 <div class="oe_row oe_centeralign oe_more_space">
24 <a href="http://www.elico-corp.com/saas/" class="oe_button oe_big">Start your <span class="oe_emph">free</span> trial</a>
25 </div>
26</section>
0\ No newline at end of file27\ No newline at end of file
128
=== added directory 'base_intercompany_sale/static/src'
=== added directory 'base_intercompany_sale/static/src/img'
=== added file 'base_intercompany_sale/static/src/img/icon.png'
2Binary 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 differ29Binary 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
=== added directory 'base_intercompany_sale/tests'
=== added file 'base_intercompany_sale/tests/__init__.py'
--- base_intercompany_sale/tests/__init__.py 1970-01-01 00:00:00 +0000
+++ base_intercompany_sale/tests/__init__.py 2014-02-23 01:10:59 +0000
@@ -0,0 +1,30 @@
1# -*- coding: utf-8 -*-
2##############################################################################
3#
4# OpenERP, Open Source Management Solution
5# Copyright (c) 2010-2014 Elico Corp. All Rights Reserved.
6# Augustin Cisterne-Kaas <augustin.cisterne-kaas@elico-corp.com>
7# Eric Caudal <eric.caudal@elico-corp.com>
8#
9# This program is free software: you can redistribute it and/or modify
10# it under the terms of the GNU Affero General Public License as
11# published by the Free Software Foundation, either version 3 of the
12# License, or (at your option) any later version.
13#
14# This program is distributed in the hope that it will be useful,
15# but WITHOUT ANY WARRANTY; without even the implied warranty of
16# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17# GNU Affero General Public License for more details.
18#
19# You should have received a copy of the GNU Affero General Public License
20# along with this program. If not, see <http://www.gnu.org/licenses/>.
21#
22##############################################################################
23import test_sale
24
25fast_suite = [
26]
27
28checks = [
29 test_sale
30]
031
=== added file 'base_intercompany_sale/tests/test_sale.py'
--- base_intercompany_sale/tests/test_sale.py 1970-01-01 00:00:00 +0000
+++ base_intercompany_sale/tests/test_sale.py 2014-02-23 01:10:59 +0000
@@ -0,0 +1,181 @@
1# -*- coding: utf-8 -*-
2##############################################################################
3#
4# OpenERP, Open Source Management Solution
5# Copyright (c) 2010-2014 Elico Corp. All Rights Reserved.
6# Augustin Cisterne-Kaas <augustin.cisterne-kaas@elico-corp.com>
7# Eric Caudal <eric.caudal@elico-corp.com>
8#
9# This program is free software: you can redistribute it and/or modify
10# it under the terms of the GNU Affero General Public License as
11# published by the Free Software Foundation, either version 3 of the
12# License, or (at your option) any later version.
13#
14# This program is distributed in the hope that it will be useful,
15# but WITHOUT ANY WARRANTY; without even the implied warranty of
16# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17# GNU Affero General Public License for more details.
18#
19# You should have received a copy of the GNU Affero General Public License
20# along with this program. If not, see <http://www.gnu.org/licenses/>.
21#
22##############################################################################
23import unittest2
24from datetime import datetime, timedelta
25import openerp
26import openerp.tests.common as common
27from openerp.osv import osv
28
29DB = common.DB
30ADMIN_USER_ID = common.ADMIN_USER_ID
31
32
33class test_sale(object):
34
35 def configure(self):
36 # get super user
37 self.m = self.registry('ir.model.data')
38 # get companies
39 self.company_origin = self.m.get_object(
40 self.cr, self.uid, 'base_intercompany', 'company_origin')
41
42 self.company_destination = self.m.get_object(
43 self.cr, self.uid, 'base_intercompany', 'company_destination')
44
45 self.user_origin = self.m.get_object(
46 self.cr, self.uid, 'base_intercompany', 'user_origin')
47
48 self.user_destination = self.m.get_object(
49 self.cr, self.uid, 'base_intercompany', 'user_destination')
50
51 self.shop_origin = self.m.get_object(
52 self.cr, self.uid, 'base_intercompany_sale', 'shop_origin')
53
54 self.backend_origin = self.m.get_object(
55 self.cr, self.uid, 'base_intercompany', 'backend_origin')
56
57 self.backend_destination = self.m.get_object(
58 self.cr, self.uid, 'base_intercompany', 'backend_destination')
59
60 def set_backend(self, on_create=True, on_write=True,
61 on_unlink=True, on_confirm=True, on_cancel=True):
62 assert hasattr(self, 'concept')
63 backend_pool = self.registry('icops.backend')
64 backend_pool.write(self.cr, ADMIN_USER_ID, self.backend_origin.id, {
65 'icops_ids': [(5, 0), (0, 0, {
66 'backend_id': self.backend_origin.id,
67 'backend_to': self.backend_destination.id,
68 'concept': self.concept,
69 'on_create': on_create,
70 'on_write': on_write,
71 'on_unlink': on_unlink,
72 'on_confirm': on_confirm,
73 'on_cancel': on_cancel
74 })]
75 })
76
77 def create_object(self):
78 pool_origin = self.registry(self.model_origin)
79 uid = self.user_origin.id
80 obj_id = pool_origin.create(
81 self.cr, self.user_origin.id, self.data_obj)
82 obj = pool_origin.browse(self.cr, uid, obj_id)
83 bind_ids = obj.icops_bind_ids
84 self.assertEqual(len(bind_ids), 1)
85 bind_id = bind_ids[0]
86 icops_ids = bind_id.icops_ids
87 self.assertEqual(len(icops_ids), 1)
88 icops = icops_ids[0]
89 ic_uid = icops.backend_id.icops_uid
90 self.assertEqual(icops.model, self.model_destination)
91 pool_destination = self.registry(icops.model)
92 obj_dest = pool_destination.browse(
93 self.cr, ic_uid, icops.record_id)
94 self.assertIsNotNone(obj_dest)
95 return obj, obj_dest
96
97 def write_object(self, obj, obj_dest):
98 pool_origin = self.registry(self.model_origin)
99 uid = self.user_origin.id
100 self.assertEqual(len(obj.order_line), 0)
101 self.assertEqual(len(obj_dest.order_line), 0)
102 pool_origin.write(self.cr, uid, obj.id, {
103 'order_line': [(5, 0), (0, 0, self.data_line)]
104 })
105 self.assertEqual(len(obj.order_line), 2)
106 self.assertEqual(len(obj_dest.order_line), 2)
107
108
109 def test_01_creation(self):
110 self.set_backend()
111 self.create_object()
112
113 def test_02_creation_raise_exception(self):
114 self.set_backend(on_create=False)
115 self.assertRaises(
116 osv.except_osv, self.create_object)
117
118 def test_03_creation_without_write_permission(self):
119 self.set_backend(on_write=False)
120 self.create_object()
121
122 # def test_04_write(self):
123 # self.set_backend()
124 # pool_origin = self.registry(self.model_origin)
125 # obj, obj_dest = self.create_object()
126 # self.write_object(obj, obj_dest)
127
128
129class test_sale_so2po(common.TransactionCase, test_sale):
130
131 def setUp(self):
132 super(test_sale_so2po, self).setUp()
133 self.configure()
134 self.concept = 'so2po'
135 self.model_origin = 'sale.order'
136 self.model_destination = 'purchase.order'
137 self.data_obj = {
138 'partner_id': self.company_destination.partner_id.id,
139 'partner_invoice_id': self.company_destination.partner_id.id,
140 'partner_shipping_id': self.company_destination.partner_id.id,
141 'shop_id': self.shop_origin.id,
142 'date_order': datetime.now().strftime('%Y-%m-%d'),
143 'pricelist_id': self.user_origin.property_product_pricelist.id}
144
145 self.data_line = {
146 'name': 'Test product',
147 'product_uom': 1,
148 'product_uom_qty': 1,
149 'price_unit': 100
150 }
151
152
153class test_sale_so2so(common.TransactionCase, test_sale):
154
155 def setUp(self):
156 super(test_sale_so2so, self).setUp()
157 self.configure()
158 self.concept = 'so2so'
159 self.model_origin = 'sale.order'
160 self.model_destination = 'sale.order'
161 self.data_obj = {
162 'partner_id': self.company_destination.partner_id.id,
163 'partner_invoice_id': self.company_destination.partner_id.id,
164 'partner_shipping_id': self.company_destination.partner_id.id,
165 'shop_id': self.shop_origin.id,
166 'date_order': datetime.now().strftime('%Y-%m-%d'),
167 'pricelist_id': self.user_origin.property_product_pricelist.id}
168
169 self.data_line = {
170 'name': 'Test product',
171 'product_uom': 1,
172 'product_uom_qty': 1,
173 'price_unit': 100
174 }
175# class test_sale_po2so(common.TransactionCase, test_sale):
176
177# def setUp(self):
178# super(test_sale_po2so, self).setUp()
179# self.configure()
180# self.model_origin = 'purchase.order'
181# self.model_destination = 'sale.order'

Subscribers

People subscribed via source and target branches