Merge lp:~openerp-dev/openobject-addons/7.0-base-contact-xal into lp:openobject-addons/7.0
- 7.0-base-contact-xal
- Merge into 7.0
Status: | Needs review |
---|---|
Proposed branch: | lp:~openerp-dev/openobject-addons/7.0-base-contact-xal |
Merge into: | lp:openobject-addons/7.0 |
Diff against target: |
691 lines (+653/-0) 7 files modified
base_contact/__init__.py (+23/-0) base_contact/__openerp__.py (+52/-0) base_contact/base_contact.py (+185/-0) base_contact/base_contact_demo.xml (+29/-0) base_contact/base_contact_view.xml (+202/-0) base_contact/tests/__init__.py (+26/-0) base_contact/tests/test_base_contact.py (+136/-0) |
To merge this branch: | bzr merge lp:~openerp-dev/openobject-addons/7.0-base-contact-xal |
Related bugs: |
Reviewer | Review Type | Date Requested | Status |
---|---|---|---|
Jacques-Etienne Baudoux (community) | Needs Fixing | ||
OpenERP Community Reviewer/Maintainer | Pending | ||
Martin Trigaux (OpenERP) | Pending | ||
Review via email: mp+192129@code.launchpad.net |
Commit message
Description of the change
Module base_contact porte de 6 -> 7
- 9535. By Xavier ALT
-
[FIX] base_contact: fix contact staying attached after settings it's type to standalone
- 9536. By Xavier ALT
-
[FIX] base_contact: missing .name
- 9537. By Xavier ALT
-
[IMP] base_contact: add contact fields (name + title) sync between partner attached to the same contact
Xavier ALT (dex-phx) wrote : | # |
Bonjour Martin,
l87: fixé
Pour ``search()``; oui pour le moment je suis obligé de faire deux recherches consécutives, c'est la seule manière de retourner un résultat correct, i.e retourner le contact "standalone" dont au moins un contact "attaché" match les critères de recherche.
Pour ``contact_type`` il est nécessaire dans l'interface, car il permet en alternance soit ``contact_id``, soit ``name``. (Ex depuis un partenaire de type "company", ajouter un contact => il te propose de choisir entre 'Standalone Contact' & 'Attached to an existing contact').
J'ai aussi fixé le bug rencontré par Olivier (olt) et ajouter la synchro du champs "Nom" & "Title".
Jacques-Etienne Baudoux (jbaudoux) wrote : | # |
Hello,
Thanks for making base contact available in v7.0.
Here are my comments:
*/ Customer -> Other Positions (o2m). At first sight I thought the purpose is to create a private contact and then consider 'other positions' as the list of job positions. But it appears you can start by creating a professional contact and then add other professional contacts or private contacts. Why not. But:
- in the list or kanban, you only see the first created contact. I would expect to see all contacts. When you filter a company, you see the company and all their contacts. I'd like the same when I filter a contact and see all the positions.
- you cannot access directly the other contacts from the customer view (because you don't see them)
- the name is confusing because "Other Positions" suggest it's only contacts that have a position in a company and it would mean you could only create professional contacts but the company field is not required.
- in the other contacts, the view is missing all the tabs you have on a contact. If I create in other positions a private contact, I need to manage e.g. the accounting for the sales to that private contact.
*/ From another view that has a m2o to res.partner and looking for a contact. I start typing the name and I see all the contacts (private and professional) and I decide to create a new contact (a new job position in another company). I am not able to link this contact to the other already existing contacts.
*/ I have to 2 professional contacts related to the same person. How can I create a link between those? This will always occur due to previous remark.
*/ I don't understand the purpose of the "Personal Information" tab for a professional contact (a contact linked to a company)
*/ When you filter a company, it's not the company contact that is listed but the 'main' contact that is linked to the company contact. That is weird.
*/ In Other Positions, when I create a new professional contact and use company address, why contact type is still displayed?
*/ In Other Positions, when I create a new professional contact and use company address, the company address should be displayed and editable like on the parent contact.
*/ In Other Positions, when I create a new professional contact and fill a new company, why it is not tagged as company?
*/ In Other Positions, the form is called "OtherS Positions"
*/ When you create or edit a contact from a company, you can choose between standalone and attached, but it's like it shouldn't visible/editable. I think contact_type should be readonly especially that there is no write method.
More to come when the concept will be clarified. Please also update description of the module explaining how it works.
kr,
J-E
Pierre Lamarche (www.savoirfairelinux.com) (pierre-lamarche) wrote : | # |
Hello,
First of all, thanks for this work.
Here are our review and comments about the functionnalities :
* One contact should have multiple relation with companies on the same level.
(why differenciate a main one and other positions ?)
We should be able to leave blank the parent company field on the top and only add multiple positions in the "Other Positions" tab below
* When you are creating a contact directly on the already opened partner form of a company, it only creates the database entry for the contact, company instead of create a one entry for the contact alone, and one entry for the contact, company.
* The idea is to manage a list of contact, a list of company and a list of relation between them.
one contact can have several relations with one or several companies, a company can have several relations with one or several contacts. Each relation should be based on a job position with professional addresses, telephone, etc...
I would be happy to discuss about that, Xavier.
BR,
Pierre
Olivier Dony (Odoo) (odo-openerp) wrote : | # |
Hi everyone,
Thanks for your feedback! Xavier has done a great job and provided a simple implementation that covers the main contact management feature that is not covered in the standard v7 distribution: the ability to easily link a single person to multiple companies, while maintaining the important semantics of the OpenERP v7 partner model.
This is the main goal of the `base_contact` port for v7 (the name is not very appropriate anymore, as contact management is now builtin without needing this module). The point is not to reproduce exactly the behavior of base_contact in v6.1, nor to implement new features related to the management of multiple positions for a given contact. The point is to establish clean groundwork for managing multiple positions for a single person under the OpenERP v7 model.
In this context the single most important aspect is the way we model these concepts at the database level - if that is correct, the rest are simple UI/UX details that can be fine-tuned easily.
We will complete the review of the functional and technical aspects of this new module very soon, and will attempt to answer your various remarks at the same time.
Do not hesitate to post further feedback here, but please be patient and keep the scope of this development in mind!
Rolf Wojtech (rolf-wojtech) wrote : | # |
I have to admit that my knowledge of the OpenERP module landscape is not vast.
We currently have a 6.1 installation using base_contact that we want to migrate from to 7.0.
I am now wondering if this module will take care of the migration as well, I did not see any migration code in the diffs.
I also found this non-merge openupgrade-
https:/
So basically I wonder if Xavier has covered migration as well or if I should resort to Sandy Carter's migration code.
(I know this is not a support forum but I feel like having this information here will help many others as well).
Fabrice (OpenERP) (fhe) wrote : | # |
Rolf, for migration, see https:/
Stefan Rijnhart (Opener) (stefan-opener) wrote : | # |
@rolf Yes, please refer to the OpenERP migration service if you have an OPW or consider getting one if you can. As for the OpenUpgrade branch that you have found, its code migrates an existing installation of 6.1 base_contact to regular partners in 7.0 because the status of this branch was still unclear at the time. That is probably not what you want.
Sandy Carter (http://www.savoirfairelinux.com) (sandy-carter) wrote : | # |
What is the current status of this MP?
Leonardo Pistone (lepistone) wrote : | # |
Thanks for your work Martin.
If this merge is not a priority for OpenERP SA and someone else needs it, maybe we could propose it for merging in OCB?
Sandy Carter (http://www.savoirfairelinux.com) (sandy-carter) wrote : | # |
I suggest moving this to lp:partner-contact-management
Here is the MP: https:/
Unmerged revisions
- 9537. By Xavier ALT
-
[IMP] base_contact: add contact fields (name + title) sync between partner attached to the same contact
- 9536. By Xavier ALT
-
[FIX] base_contact: missing .name
- 9535. By Xavier ALT
-
[FIX] base_contact: fix contact staying attached after settings it's type to standalone
- 9534. By Xavier ALT
-
[+] base_contact module
Preview Diff
1 | === added directory 'base_contact' | |||
2 | === added file 'base_contact/__init__.py' | |||
3 | --- base_contact/__init__.py 1970-01-01 00:00:00 +0000 | |||
4 | +++ base_contact/__init__.py 2013-10-23 13:04:00 +0000 | |||
5 | @@ -0,0 +1,23 @@ | |||
6 | 1 | # -*- coding: utf-8 -*- | ||
7 | 2 | ############################################################################## | ||
8 | 3 | # | ||
9 | 4 | # OpenERP, Open Source Management Solution | ||
10 | 5 | # Copyright (C) 2013-TODAY OpenERP SA (<http://www.openerp.com>). | ||
11 | 6 | # | ||
12 | 7 | # This program is free software: you can redistribute it and/or modify | ||
13 | 8 | # it under the terms of the GNU Affero General Public License as | ||
14 | 9 | # published by the Free Software Foundation, either version 3 of the | ||
15 | 10 | # License, or (at your option) any later version. | ||
16 | 11 | # | ||
17 | 12 | # This program is distributed in the hope that it will be useful, | ||
18 | 13 | # but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
19 | 14 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
20 | 15 | # GNU Affero General Public License for more details. | ||
21 | 16 | # | ||
22 | 17 | # You should have received a copy of the GNU Affero General Public License | ||
23 | 18 | # along with this program. If not, see <http://www.gnu.org/licenses/>. | ||
24 | 19 | # | ||
25 | 20 | ############################################################################## | ||
26 | 21 | |||
27 | 22 | import base_contact | ||
28 | 23 | |||
29 | 0 | 24 | ||
30 | === added file 'base_contact/__openerp__.py' | |||
31 | --- base_contact/__openerp__.py 1970-01-01 00:00:00 +0000 | |||
32 | +++ base_contact/__openerp__.py 2013-10-23 13:04:00 +0000 | |||
33 | @@ -0,0 +1,52 @@ | |||
34 | 1 | # -*- coding: utf-8 -*- | ||
35 | 2 | ############################################################################## | ||
36 | 3 | # | ||
37 | 4 | # OpenERP, Open Source Business Applications | ||
38 | 5 | # Copyright (C) 2013-TODAY OpenERP S.A. (<http://openerp.com>). | ||
39 | 6 | # | ||
40 | 7 | # This program is free software: you can redistribute it and/or modify | ||
41 | 8 | # it under the terms of the GNU Affero General Public License as | ||
42 | 9 | # published by the Free Software Foundation, either version 3 of the | ||
43 | 10 | # License, or (at your option) any later version. | ||
44 | 11 | # | ||
45 | 12 | # This program is distributed in the hope that it will be useful, | ||
46 | 13 | # but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
47 | 14 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
48 | 15 | # GNU Affero General Public License for more details. | ||
49 | 16 | # | ||
50 | 17 | # You should have received a copy of the GNU Affero General Public License | ||
51 | 18 | # along with this program. If not, see <http://www.gnu.org/licenses/>. | ||
52 | 19 | # | ||
53 | 20 | ############################################################################## | ||
54 | 21 | |||
55 | 22 | { | ||
56 | 23 | 'name': 'Contacts Management', | ||
57 | 24 | 'version': '1.0', | ||
58 | 25 | 'category': 'Customer Relationship Management', | ||
59 | 26 | 'complexity': "expert", | ||
60 | 27 | 'description': """ | ||
61 | 28 | This module allows you to manage your contacts | ||
62 | 29 | ============================================== | ||
63 | 30 | |||
64 | 31 | It lets you define groups of contacts sharing some common information, like: | ||
65 | 32 | * Birthdate | ||
66 | 33 | * Nationality | ||
67 | 34 | * Native Language | ||
68 | 35 | |||
69 | 36 | """, | ||
70 | 37 | 'author': 'OpenERP SA', | ||
71 | 38 | 'website': 'http://www.openerp.com', | ||
72 | 39 | 'depends': ['base', 'process', 'contacts'], | ||
73 | 40 | 'init_xml': [], | ||
74 | 41 | 'update_xml': [ | ||
75 | 42 | 'base_contact_view.xml', | ||
76 | 43 | ], | ||
77 | 44 | 'demo_xml': [ | ||
78 | 45 | 'base_contact_demo.xml', | ||
79 | 46 | ], | ||
80 | 47 | 'installable': True, | ||
81 | 48 | 'auto_install': False, | ||
82 | 49 | #'certificate': '0031287885469', | ||
83 | 50 | 'images': [], | ||
84 | 51 | } | ||
85 | 52 | # vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4: | ||
86 | 0 | 53 | ||
87 | === added file 'base_contact/base_contact.py' | |||
88 | --- base_contact/base_contact.py 1970-01-01 00:00:00 +0000 | |||
89 | +++ base_contact/base_contact.py 2013-10-23 13:04:00 +0000 | |||
90 | @@ -0,0 +1,185 @@ | |||
91 | 1 | # -*- coding: utf-8 -*- | ||
92 | 2 | ############################################################################## | ||
93 | 3 | # | ||
94 | 4 | # OpenERP, Open Source Management Solution | ||
95 | 5 | # Copyright (C) 2013-TODAY OpenERP SA (<http://www.openerp.com>). | ||
96 | 6 | # | ||
97 | 7 | # This program is free software: you can redistribute it and/or modify | ||
98 | 8 | # it under the terms of the GNU Affero General Public License as | ||
99 | 9 | # published by the Free Software Foundation, either version 3 of the | ||
100 | 10 | # License, or (at your option) any later version. | ||
101 | 11 | # | ||
102 | 12 | # This program is distributed in the hope that it will be useful, | ||
103 | 13 | # but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
104 | 14 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
105 | 15 | # GNU Affero General Public License for more details. | ||
106 | 16 | # | ||
107 | 17 | # You should have received a copy of the GNU Affero General Public License | ||
108 | 18 | # along with this program. If not, see <http://www.gnu.org/licenses/>. | ||
109 | 19 | # | ||
110 | 20 | ############################################################################## | ||
111 | 21 | |||
112 | 22 | from openerp.osv import fields, osv, expression | ||
113 | 23 | |||
114 | 24 | |||
115 | 25 | class res_partner(osv.osv): | ||
116 | 26 | _inherit = 'res.partner' | ||
117 | 27 | |||
118 | 28 | _contact_type = [ | ||
119 | 29 | ('standalone', 'Standalone Contact'), | ||
120 | 30 | ('attached', 'Attached to existing Contact'), | ||
121 | 31 | ] | ||
122 | 32 | |||
123 | 33 | def _get_contact_type(self, cr, uid, ids, field_name, args, context=None): | ||
124 | 34 | result = dict.fromkeys(ids, 'standalone') | ||
125 | 35 | for partner in self.browse(cr, uid, ids, context=context): | ||
126 | 36 | if partner.contact_id: | ||
127 | 37 | result[partner.id] = 'attached' | ||
128 | 38 | return result | ||
129 | 39 | |||
130 | 40 | _columns = { | ||
131 | 41 | 'contact_type': fields.function(_get_contact_type, type='selection', selection=_contact_type, | ||
132 | 42 | string='Contact Type', required=True, select=1, store=True), | ||
133 | 43 | 'contact_id': fields.many2one('res.partner', 'Main Contact', | ||
134 | 44 | domain=[('is_company','=',False),('contact_type','=','standalone')]), | ||
135 | 45 | 'other_contact_ids': fields.one2many('res.partner', 'contact_id', 'Others Positions'), | ||
136 | 46 | |||
137 | 47 | # Person specific fields | ||
138 | 48 | 'birthdate_date': fields.date('Birthdate'), # add a 'birthdate' as date field, i.e different from char 'birthdate' introduced v6.1! | ||
139 | 49 | 'nationality_id': fields.many2one('res.country', 'Nationality'), | ||
140 | 50 | } | ||
141 | 51 | |||
142 | 52 | _defaults = { | ||
143 | 53 | 'contact_type': 'standalone', | ||
144 | 54 | } | ||
145 | 55 | |||
146 | 56 | def _basecontact_check_context(self, cr, user, mode, context=None): | ||
147 | 57 | if context is None: | ||
148 | 58 | context = {} | ||
149 | 59 | # Remove 'search_show_all_positions' for non-search mode. | ||
150 | 60 | # Keeping it in context can result in unexpected behaviour (ex: reading | ||
151 | 61 | # one2many might return wrong result - i.e with "attached contact" removed | ||
152 | 62 | # even if it's directly linked to a company). | ||
153 | 63 | if mode != 'search': | ||
154 | 64 | context.pop('search_show_all_positions', None) | ||
155 | 65 | return context | ||
156 | 66 | |||
157 | 67 | def search(self, cr, user, args, offset=0, limit=None, order=None, context=None, count=False): | ||
158 | 68 | if context is None: | ||
159 | 69 | context = {} | ||
160 | 70 | if context.get('search_show_all_positions') is False: | ||
161 | 71 | # display only standalone contact matching ``args`` or having | ||
162 | 72 | # attached contact matching ``args`` | ||
163 | 73 | args = expression.normalize_domain(args) | ||
164 | 74 | attached_contact_args = expression.AND((args, [('contact_type', '=', 'attached')])) | ||
165 | 75 | attached_contact_ids = super(res_partner, self).search(cr, user, attached_contact_args, | ||
166 | 76 | context=context) | ||
167 | 77 | args = expression.OR(( | ||
168 | 78 | expression.AND(([('contact_type', '=', 'standalone')], args)), | ||
169 | 79 | [('other_contact_ids', 'in', attached_contact_ids)], | ||
170 | 80 | )) | ||
171 | 81 | return super(res_partner, self).search(cr, user, args, offset=offset, limit=limit, | ||
172 | 82 | order=order, context=context, count=count) | ||
173 | 83 | |||
174 | 84 | def create(self, cr, user, vals, context=None): | ||
175 | 85 | context = self._basecontact_check_context(cr, user, 'create', context) | ||
176 | 86 | if not vals.get('name') and vals.get('contact_id'): | ||
177 | 87 | vals['name'] = self.browse(cr, user, vals['contact_id'], context=context).name | ||
178 | 88 | return super(res_partner, self).create(cr, user, vals, context=context) | ||
179 | 89 | |||
180 | 90 | def read(self, cr, user, ids, fields=None, context=None, load='_classic_read'): | ||
181 | 91 | context = self._basecontact_check_context(cr, user, 'read', context) | ||
182 | 92 | return super(res_partner, self).read(cr, user, ids, fields=fields, context=context, load=load) | ||
183 | 93 | |||
184 | 94 | def write(self, cr, user, ids, vals, context=None): | ||
185 | 95 | context = self._basecontact_check_context(cr, user, 'write', context) | ||
186 | 96 | return super(res_partner, self).write(cr, user, ids, vals, context=context) | ||
187 | 97 | |||
188 | 98 | def unlink(self, cr, user, ids, context=None): | ||
189 | 99 | context = self._basecontact_check_context(cr, user, 'unlink', context) | ||
190 | 100 | return super(res_partner, self).unlink(cr, user, ids, context=context) | ||
191 | 101 | |||
192 | 102 | def _commercial_partner_compute(self, cr, uid, ids, name, args, context=None): | ||
193 | 103 | """ Returns the partner that is considered the commercial | ||
194 | 104 | entity of this partner. The commercial entity holds the master data | ||
195 | 105 | for all commercial fields (see :py:meth:`~_commercial_fields`) """ | ||
196 | 106 | result = super(res_partner, self)._commercial_partner_compute(cr, uid, ids, name, args, context=context) | ||
197 | 107 | for partner in self.browse(cr, uid, ids, context=context): | ||
198 | 108 | if partner.contact_type == 'attached' and not partner.parent_id: | ||
199 | 109 | result[partner.id] = partner.contact_id.id | ||
200 | 110 | return result | ||
201 | 111 | |||
202 | 112 | def _contact_fields(self, cr, uid, context=None): | ||
203 | 113 | """ Returns the list of contact fields that are synced from the parent | ||
204 | 114 | when a partner is attached to him. """ | ||
205 | 115 | return ['name', 'title'] | ||
206 | 116 | |||
207 | 117 | def _contact_sync_from_parent(self, cr, uid, partner, context=None): | ||
208 | 118 | """ Handle sync of contact fields when a new parent contact entity is set, | ||
209 | 119 | as if they were related fields """ | ||
210 | 120 | if partner.contact_id: | ||
211 | 121 | contact_fields = self._contact_fields(cr, uid, context=context) | ||
212 | 122 | sync_vals = self._update_fields_values(cr, uid, partner.contact_id, | ||
213 | 123 | contact_fields, context=context) | ||
214 | 124 | partner.write(sync_vals) | ||
215 | 125 | |||
216 | 126 | def update_contact(self, cr, uid, ids, vals, context=None): | ||
217 | 127 | if context is None: | ||
218 | 128 | context = {} | ||
219 | 129 | if context.get('__update_contact_lock'): | ||
220 | 130 | return | ||
221 | 131 | contact_fields = self._contact_fields(cr, uid, context=context) | ||
222 | 132 | contact_vals = dict((field, vals[field]) for field in contact_fields if field in vals) | ||
223 | 133 | if contact_vals: | ||
224 | 134 | ctx = dict(context, __update_contact_lock=True) | ||
225 | 135 | self.write(cr, uid, ids, contact_vals, context=ctx) | ||
226 | 136 | |||
227 | 137 | def _fields_sync(self, cr, uid, partner, update_values, context=None): | ||
228 | 138 | """ Sync commercial fields and address fields from company and to children, | ||
229 | 139 | contact fields from contact and to attached contact after create/update, | ||
230 | 140 | just as if those were all modeled as fields.related to the parent """ | ||
231 | 141 | super(res_partner, self)._fields_sync(cr, uid, partner, update_values, context=context) | ||
232 | 142 | contact_fields = self._contact_fields(cr, uid, context=context) | ||
233 | 143 | # 1. From UPSTREAM: sync from parent contact | ||
234 | 144 | if update_values.get('contact_id'): | ||
235 | 145 | self._contact_sync_from_parent(cr, uid, partner, context=context) | ||
236 | 146 | # 2. To DOWNSTREAM: sync contact fields to parent or related | ||
237 | 147 | elif any(field in contact_fields for field in update_values): | ||
238 | 148 | update_ids = [c.id for c in partner.other_contact_ids if not c.is_company] | ||
239 | 149 | if partner.contact_id: | ||
240 | 150 | update_ids.append(partner.contact_id.id) | ||
241 | 151 | self.update_contact(cr, uid, update_ids, update_values, context=context) | ||
242 | 152 | |||
243 | 153 | def onchange_contact_id(self, cr, uid, ids, contact_id, context=None): | ||
244 | 154 | values = {} | ||
245 | 155 | if contact_id: | ||
246 | 156 | values['name'] = self.browse(cr, uid, contact_id, context=context).name | ||
247 | 157 | return {'value': values} | ||
248 | 158 | |||
249 | 159 | def onchange_contact_type(self, cr, uid, ids, contact_type, context=None): | ||
250 | 160 | values = {} | ||
251 | 161 | if contact_type == 'standalone': | ||
252 | 162 | values['contact_id'] = False | ||
253 | 163 | return {'value': values} | ||
254 | 164 | |||
255 | 165 | |||
256 | 166 | class ir_actions_window(osv.osv): | ||
257 | 167 | _inherit = 'ir.actions.act_window' | ||
258 | 168 | |||
259 | 169 | def read(self, cr, user, ids, fields=None, context=None, load='_classic_read'): | ||
260 | 170 | action_ids = ids | ||
261 | 171 | if isinstance(ids, (int, long)): | ||
262 | 172 | action_ids = [ids] | ||
263 | 173 | actions = super(ir_actions_window, self).read(cr, user, action_ids, fields=fields, context=context, load=load) | ||
264 | 174 | for action in actions: | ||
265 | 175 | if action.get('res_model', '') == 'res.partner': | ||
266 | 176 | # By default, only show standalone contact | ||
267 | 177 | action_context = action.get('context', '{}') or '{}' | ||
268 | 178 | if 'search_show_all_positions' not in action_context: | ||
269 | 179 | action['context'] = action_context.replace('{', | ||
270 | 180 | "{'search_show_all_positions': False,", 1) | ||
271 | 181 | if isinstance(ids, (int, long)): | ||
272 | 182 | if actions: | ||
273 | 183 | return actions[0] | ||
274 | 184 | return False | ||
275 | 185 | return actions | ||
276 | 0 | 186 | ||
277 | === added file 'base_contact/base_contact_demo.xml' | |||
278 | --- base_contact/base_contact_demo.xml 1970-01-01 00:00:00 +0000 | |||
279 | +++ base_contact/base_contact_demo.xml 2013-10-23 13:04:00 +0000 | |||
280 | @@ -0,0 +1,29 @@ | |||
281 | 1 | <?xml version="1.0" encoding="UTF-8"?> | ||
282 | 2 | <openerp> | ||
283 | 3 | <data> | ||
284 | 4 | |||
285 | 5 | <record id="res_partner_main2_position_consultant" model="res.partner"> | ||
286 | 6 | <field name="name">Roger Scott</field> | ||
287 | 7 | <field name="function">Consultant</field> | ||
288 | 8 | <field name="parent_id" ref="base.res_partner_11"/> | ||
289 | 9 | <field name="contact_id" ref="base.res_partner_main2"/> | ||
290 | 10 | <field name="use_parent_address" eval="True"/> | ||
291 | 11 | </record> | ||
292 | 12 | |||
293 | 13 | <record id="res_partner_contact1" model="res.partner"> | ||
294 | 14 | <field name="name">Bob Egnops</field> | ||
295 | 15 | <field name="birthdate_date">1984-01-01</field> | ||
296 | 16 | <field name="email">bob@hillenburg-oceaninstitute.com</field> | ||
297 | 17 | </record> | ||
298 | 18 | |||
299 | 19 | <record id="res_partner_contact1_work_position1" model="res.partner"> | ||
300 | 20 | <field name="name">Bob Egnops</field> | ||
301 | 21 | <field name="function">Technician</field> | ||
302 | 22 | <field name="email">bob@yourcompany.com</field> | ||
303 | 23 | <field name="parent_id" ref="base.main_partner"/> | ||
304 | 24 | <field name="contact_id" ref="res_partner_contact1"/> | ||
305 | 25 | <field name="use_parent_address" eval="True"/> | ||
306 | 26 | </record> | ||
307 | 27 | |||
308 | 28 | </data> | ||
309 | 29 | </openerp> | ||
310 | 0 | \ No newline at end of file | 30 | \ No newline at end of file |
311 | 1 | 31 | ||
312 | === added file 'base_contact/base_contact_view.xml' | |||
313 | --- base_contact/base_contact_view.xml 1970-01-01 00:00:00 +0000 | |||
314 | +++ base_contact/base_contact_view.xml 2013-10-23 13:04:00 +0000 | |||
315 | @@ -0,0 +1,202 @@ | |||
316 | 1 | <?xml version="1.0" encoding="utf-8"?> | ||
317 | 2 | <openerp> | ||
318 | 3 | <data> | ||
319 | 4 | |||
320 | 5 | <record id="view_res_partner_filter_contact" model="ir.ui.view"> | ||
321 | 6 | <field name="name">res.partner.select.contact</field> | ||
322 | 7 | <field name="model">res.partner</field> | ||
323 | 8 | <field name="inherit_id" ref="base.view_res_partner_filter"/> | ||
324 | 9 | <field name="arch" type="xml"> | ||
325 | 10 | <filter name="type_company" position="after"> | ||
326 | 11 | <separator/> | ||
327 | 12 | <filter string="All positions" name="type_otherpositions" | ||
328 | 13 | context="{'search_show_all_positions': True}" | ||
329 | 14 | help="All partner positions"/> | ||
330 | 15 | </filter> | ||
331 | 16 | <xpath expr="/search/group/filter[@string='Company']" position="before"> | ||
332 | 17 | <filter string="Person" name="group_person" context="{'group_by': 'contact_id'}"/> | ||
333 | 18 | </xpath> | ||
334 | 19 | </field> | ||
335 | 20 | </record> | ||
336 | 21 | |||
337 | 22 | <record id="view_res_partner_tree_contact" model="ir.ui.view"> | ||
338 | 23 | <field name="name">res.partner.tree.contact</field> | ||
339 | 24 | <field name="model">res.partner</field> | ||
340 | 25 | <field name="inherit_id" ref="base.view_partner_tree"/> | ||
341 | 26 | <field name="arch" type="xml"> | ||
342 | 27 | <field name="parent_id" position="after"> | ||
343 | 28 | <field name="contact_id" invisible="1"/> | ||
344 | 29 | </field> | ||
345 | 30 | </field> | ||
346 | 31 | </record> | ||
347 | 32 | |||
348 | 33 | <record model="ir.ui.view" id="view_partner_form_inherit"> | ||
349 | 34 | <field name="name">res.partner.form.contact</field> | ||
350 | 35 | <field name="model">res.partner</field> | ||
351 | 36 | <field name="inherit_id" ref="base.view_partner_form"/> | ||
352 | 37 | <field name="type">form</field> | ||
353 | 38 | <field name="arch" type="xml"> | ||
354 | 39 | <field name="is_company" position="after"> | ||
355 | 40 | <field name="contact_type" invisible="1"/> | ||
356 | 41 | </field> | ||
357 | 42 | <page string="Contacts" position="after"> | ||
358 | 43 | <page string="Other Positions" attrs="{'invisible': ['|',('is_company','=',True),('contact_id','!=',False)]}"> | ||
359 | 44 | <field name="other_contact_ids" context="{'default_contact_id': active_id, 'default_name': name, 'default_street': street, 'default_street2': street2, 'default_city': city, 'default_state_id': state_id, 'default_zip': zip, 'default_country_id': country_id, 'default_supplier': supplier}}" mode="kanban"> | ||
360 | 45 | <kanban> | ||
361 | 46 | <field name="color"/> | ||
362 | 47 | <field name="name"/> | ||
363 | 48 | <field name="title"/> | ||
364 | 49 | <field name="email"/> | ||
365 | 50 | <field name="parent_id"/> | ||
366 | 51 | <field name="is_company"/> | ||
367 | 52 | <field name="function"/> | ||
368 | 53 | <field name="phone"/> | ||
369 | 54 | <field name="street"/> | ||
370 | 55 | <field name="street2"/> | ||
371 | 56 | <field name="zip"/> | ||
372 | 57 | <field name="city"/> | ||
373 | 58 | <field name="country_id"/> | ||
374 | 59 | <field name="mobile"/> | ||
375 | 60 | <field name="fax"/> | ||
376 | 61 | <field name="state_id"/> | ||
377 | 62 | <field name="has_image"/> | ||
378 | 63 | <templates> | ||
379 | 64 | <t t-name="kanban-box"> | ||
380 | 65 | <t t-set="color" t-value="kanban_color(record.color.raw_value)"/> | ||
381 | 66 | <div t-att-class="color + (record.title.raw_value == 1 ? ' oe_kanban_color_alert' : '')" style="position: relative"> | ||
382 | 67 | <a t-if="! read_only_mode" type="delete" style="position: absolute; right: 0; padding: 4px; diplay: inline-block">X</a> | ||
383 | 68 | <div class="oe_module_vignette"> | ||
384 | 69 | <a type="open"> | ||
385 | 70 | <t t-if="record.has_image.raw_value === true"> | ||
386 | 71 | <img t-att-src="kanban_image('res.partner', 'image', record.id.value, {'preview_image': 'image_small'})" class="oe_avatar oe_kanban_avatar_smallbox"/> | ||
387 | 72 | </t> | ||
388 | 73 | <t t-if="record.image and record.image.raw_value !== false"> | ||
389 | 74 | <img t-att-src="'data:image/png;base64,'+record.image.raw_value" class="oe_avatar oe_kanban_avatar_smallbox"/> | ||
390 | 75 | </t> | ||
391 | 76 | <t t-if="record.has_image.raw_value === false and (!record.image or record.image.raw_value === false)"> | ||
392 | 77 | <t t-if="record.is_company.raw_value === true"> | ||
393 | 78 | <img t-att-src='_s + "/base/static/src/img/company_image.png"' class="oe_kanban_image oe_kanban_avatar_smallbox"/> | ||
394 | 79 | </t> | ||
395 | 80 | <t t-if="record.is_company.raw_value === false"> | ||
396 | 81 | <img t-att-src='_s + "/base/static/src/img/avatar.png"' class="oe_kanban_image oe_kanban_avatar_smallbox"/> | ||
397 | 82 | </t> | ||
398 | 83 | </t> | ||
399 | 84 | </a> | ||
400 | 85 | <div class="oe_module_desc"> | ||
401 | 86 | <div class="oe_kanban_box_content oe_kanban_color_bglight oe_kanban_color_border"> | ||
402 | 87 | <table class="oe_kanban_table"> | ||
403 | 88 | <tr> | ||
404 | 89 | <td class="oe_kanban_title1" align="left" valign="middle"> | ||
405 | 90 | <h4><a type="open"><field name="name"/></a></h4> | ||
406 | 91 | <i> | ||
407 | 92 | <t t-if="record.parent_id.raw_value and !record.function.raw_value"><field name="parent_id"/></t> | ||
408 | 93 | <t t-if="!record.parent_id.raw_value and record.function.raw_value"><field name="function"/></t> | ||
409 | 94 | <t t-if="record.parent_id.raw_value and record.function.raw_value"><field name="function"/> at <field name="parent_id"/></t> | ||
410 | 95 | </i> | ||
411 | 96 | <div><a t-if="record.email.raw_value" title="Mail" t-att-href="'mailto:'+record.email.value"> | ||
412 | 97 | <field name="email"/> | ||
413 | 98 | </a></div> | ||
414 | 99 | <div t-if="record.phone.raw_value">Phone: <field name="phone"/></div> | ||
415 | 100 | <div t-if="record.mobile.raw_value">Mobile: <field name="mobile"/></div> | ||
416 | 101 | <div t-if="record.fax.raw_value">Fax: <field name="fax"/></div> | ||
417 | 102 | </td> | ||
418 | 103 | </tr> | ||
419 | 104 | </table> | ||
420 | 105 | </div> | ||
421 | 106 | </div> | ||
422 | 107 | </div> | ||
423 | 108 | </div> | ||
424 | 109 | </t> | ||
425 | 110 | </templates> | ||
426 | 111 | </kanban> | ||
427 | 112 | <form string="Contact" version="7.0"> | ||
428 | 113 | <sheet> | ||
429 | 114 | <field name="image" widget='image' class="oe_avatar oe_left" options='{"preview_image": "image_medium"}'/> | ||
430 | 115 | <div class="oe_title"> | ||
431 | 116 | <label for="name" class="oe_edit_only"/> | ||
432 | 117 | <h1><field name="name" style="width: 70%%"/></h1> | ||
433 | 118 | </div> | ||
434 | 119 | <group> | ||
435 | 120 | <!-- inherited part --> | ||
436 | 121 | <field name="category_id" widget="many2many_tags" placeholder="Tags..." style="width: 70%%"/> | ||
437 | 122 | <field name="parent_id" placeholder="Company" domain="[('is_company','=',True)]"/> | ||
438 | 123 | <!-- inherited part end --> | ||
439 | 124 | <field name="function" placeholder="e.g. Sales Director"/> | ||
440 | 125 | <field name="email"/> | ||
441 | 126 | <field name="phone"/> | ||
442 | 127 | <field name="mobile"/> | ||
443 | 128 | </group> | ||
444 | 129 | <div> | ||
445 | 130 | <field name="use_parent_address"/><label for="use_parent_address"/> | ||
446 | 131 | </div> | ||
447 | 132 | <group> | ||
448 | 133 | <label for="type"/> | ||
449 | 134 | <div name="div_type"> | ||
450 | 135 | <field class="oe_inline" name="type"/> | ||
451 | 136 | </div> | ||
452 | 137 | <label for="street" string="Address" attrs="{'invisible': [('use_parent_address','=', True)]}"/> | ||
453 | 138 | <div attrs="{'invisible': [('use_parent_address','=', True)]}" name="div_address"> | ||
454 | 139 | <field name="street" placeholder="Street..."/> | ||
455 | 140 | <field name="street2"/> | ||
456 | 141 | <div class="address_format"> | ||
457 | 142 | <field name="city" placeholder="City" style="width: 40%%"/> | ||
458 | 143 | <field name="state_id" class="oe_no_button" placeholder="State" style="width: 37%%" options='{"no_open": True}' on_change="onchange_state(state_id)"/> | ||
459 | 144 | <field name="zip" placeholder="ZIP" style="width: 20%%"/> | ||
460 | 145 | </div> | ||
461 | 146 | <field name="country_id" placeholder="Country" class="oe_no_button" options='{"no_open": True}'/> | ||
462 | 147 | </div> | ||
463 | 148 | </group> | ||
464 | 149 | <field name="supplier" invisible="True"/> | ||
465 | 150 | </sheet> | ||
466 | 151 | </form> | ||
467 | 152 | </field> | ||
468 | 153 | </page> | ||
469 | 154 | <page name="personal-info" string="Personal Information" attrs="{'invisible': ['|',('is_company','=',True)]}"> | ||
470 | 155 | <p attrs="{'invisible': [('contact_id','=',False)]}"> | ||
471 | 156 | To see personal information about this contact, please go to to the his person form: <field name="contact_id" class="oe_inline" domain="[('contact_type','!=','attached')]" context="{'show_address': 1}" | ||
472 | 157 | on_change="onchange_contact_id(contact_id)" options="{'always_reload': True}"/> | ||
473 | 158 | </p> | ||
474 | 159 | <group attrs="{'invisible': [('contact_id','!=',False)]}"> | ||
475 | 160 | <field name="birthdate_date"/> | ||
476 | 161 | <field name="nationality_id"/> | ||
477 | 162 | </group> | ||
478 | 163 | </page> | ||
479 | 164 | </page> | ||
480 | 165 | <xpath expr="//field[@name='child_ids']/form//field[@name='name']/.." position="before"> | ||
481 | 166 | <field name="contact_type" readonly="0" on_change="onchange_contact_type(contact_type)"/> | ||
482 | 167 | </xpath> | ||
483 | 168 | <xpath expr="//field[@name='child_ids']/form//field[@name='name']" position="after"> | ||
484 | 169 | <field name="contact_id" on_change="onchange_contact_id(contact_id)" string="Contact" | ||
485 | 170 | attrs="{'invisible': [('contact_type','!=','attached')], 'required': [('contact_type','=','attached')]}"/> | ||
486 | 171 | </xpath> | ||
487 | 172 | <xpath expr="//field[@name='child_ids']/form//field[@name='name']" position="attributes"> | ||
488 | 173 | <attribute name="attrs">{'invisible': [('contact_type','=','attached')]}</attribute> | ||
489 | 174 | </xpath> | ||
490 | 175 | </field> | ||
491 | 176 | </record> | ||
492 | 177 | |||
493 | 178 | <record model="ir.ui.view" id="view_res_partner_kanban_contact"> | ||
494 | 179 | <field name="name">res.partner.kanban.contact</field> | ||
495 | 180 | <field name="model">res.partner</field> | ||
496 | 181 | <field name="inherit_id" ref="base.res_partner_kanban_view"/> | ||
497 | 182 | <field name="arch" type="xml"> | ||
498 | 183 | <field name="is_company" position="after"> | ||
499 | 184 | <field name="other_contact_ids"> | ||
500 | 185 | <tree> | ||
501 | 186 | <field name="parent_id"/> | ||
502 | 187 | <field name="function"/> | ||
503 | 188 | </tree> | ||
504 | 189 | </field> | ||
505 | 190 | </field> | ||
506 | 191 | <xpath expr="//t[@t-name='kanban-box']//div[@class='oe_kanban_details']/ul/li[3]" position="after"> | ||
507 | 192 | <t t-if="record.other_contact_ids.raw_value.length > 0"> | ||
508 | 193 | <li>+<t t-esc="record.other_contact_ids.raw_value.length"/> | ||
509 | 194 | <t t-if="record.other_contact_ids.raw_value.length == 1">other position</t> | ||
510 | 195 | <t t-if="record.other_contact_ids.raw_value.length > 1">other positions</t></li> | ||
511 | 196 | </t> | ||
512 | 197 | </xpath> | ||
513 | 198 | </field> | ||
514 | 199 | </record> | ||
515 | 200 | |||
516 | 201 | </data> | ||
517 | 202 | </openerp> | ||
518 | 0 | 203 | ||
519 | === added directory 'base_contact/images' | |||
520 | === added directory 'base_contact/tests' | |||
521 | === added file 'base_contact/tests/__init__.py' | |||
522 | --- base_contact/tests/__init__.py 1970-01-01 00:00:00 +0000 | |||
523 | +++ base_contact/tests/__init__.py 2013-10-23 13:04:00 +0000 | |||
524 | @@ -0,0 +1,26 @@ | |||
525 | 1 | # -*- coding: utf-8 ⁻*- | ||
526 | 2 | ############################################################################## | ||
527 | 3 | # | ||
528 | 4 | # OpenERP, Open Source Business Applications | ||
529 | 5 | # Copyright (C) 2013-TODAY OpenERP S.A. (<http://openerp.com>). | ||
530 | 6 | # | ||
531 | 7 | # This program is free software: you can redistribute it and/or modify | ||
532 | 8 | # it under the terms of the GNU Affero General Public License as | ||
533 | 9 | # published by the Free Software Foundation, either version 3 of the | ||
534 | 10 | # License, or (at your option) any later version. | ||
535 | 11 | # | ||
536 | 12 | # This program is distributed in the hope that it will be useful, | ||
537 | 13 | # but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
538 | 14 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
539 | 15 | # GNU Affero General Public License for more details. | ||
540 | 16 | # | ||
541 | 17 | # You should have received a copy of the GNU Affero General Public License | ||
542 | 18 | # along with this program. If not, see <http://www.gnu.org/licenses/>. | ||
543 | 19 | # | ||
544 | 20 | ############################################################################## | ||
545 | 21 | |||
546 | 22 | from . import test_base_contact | ||
547 | 23 | |||
548 | 24 | checks = [ | ||
549 | 25 | test_base_contact, | ||
550 | 26 | ] | ||
551 | 0 | 27 | ||
552 | === added file 'base_contact/tests/test_base_contact.py' | |||
553 | --- base_contact/tests/test_base_contact.py 1970-01-01 00:00:00 +0000 | |||
554 | +++ base_contact/tests/test_base_contact.py 2013-10-23 13:04:00 +0000 | |||
555 | @@ -0,0 +1,136 @@ | |||
556 | 1 | # -*- coding: utf-8 ⁻*- | ||
557 | 2 | ############################################################################## | ||
558 | 3 | # | ||
559 | 4 | # OpenERP, Open Source Business Applications | ||
560 | 5 | # Copyright (C) 2013-TODAY OpenERP S.A. (<http://openerp.com>). | ||
561 | 6 | # | ||
562 | 7 | # This program is free software: you can redistribute it and/or modify | ||
563 | 8 | # it under the terms of the GNU Affero General Public License as | ||
564 | 9 | # published by the Free Software Foundation, either version 3 of the | ||
565 | 10 | # License, or (at your option) any later version. | ||
566 | 11 | # | ||
567 | 12 | # This program is distributed in the hope that it will be useful, | ||
568 | 13 | # but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
569 | 14 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
570 | 15 | # GNU Affero General Public License for more details. | ||
571 | 16 | # | ||
572 | 17 | # You should have received a copy of the GNU Affero General Public License | ||
573 | 18 | # along with this program. If not, see <http://www.gnu.org/licenses/>. | ||
574 | 19 | # | ||
575 | 20 | ############################################################################## | ||
576 | 21 | |||
577 | 22 | from openerp.tests import common | ||
578 | 23 | |||
579 | 24 | |||
580 | 25 | class Test_Base_Contact(common.TransactionCase): | ||
581 | 26 | |||
582 | 27 | def setUp(self): | ||
583 | 28 | """*****setUp*****""" | ||
584 | 29 | super(Test_Base_Contact, self).setUp() | ||
585 | 30 | cr, uid = self.cr, self.uid | ||
586 | 31 | ModelData = self.registry('ir.model.data') | ||
587 | 32 | self.partner = self.registry('res.partner') | ||
588 | 33 | |||
589 | 34 | # Get test records reference | ||
590 | 35 | for attr, module, name in [ | ||
591 | 36 | ('main_partner_id', 'base', 'main_partner'), | ||
592 | 37 | ('bob_contact_id', 'base_contact', 'res_partner_contact1'), | ||
593 | 38 | ('bob_job1_id', 'base_contact', 'res_partner_contact1_work_position1'), | ||
594 | 39 | ('roger_contact_id', 'base', 'res_partner_main2'), | ||
595 | 40 | ('roger_job2_id', 'base_contact', 'res_partner_main2_position_consultant')]: | ||
596 | 41 | r = ModelData.get_object_reference(cr, uid, module, name) | ||
597 | 42 | setattr(self, attr, r[1] if r else False) | ||
598 | 43 | |||
599 | 44 | def test_00_show_only_standalone_contact(self): | ||
600 | 45 | """Check that only standalone contact are shown if context explicitly state to not display all positions""" | ||
601 | 46 | cr, uid = self.cr, self.uid | ||
602 | 47 | ctx = {'search_show_all_positions': False} | ||
603 | 48 | partner_ids = self.partner.search(cr, uid, [], context=ctx) | ||
604 | 49 | partner_ids.sort() | ||
605 | 50 | self.assertTrue(self.bob_job1_id not in partner_ids) | ||
606 | 51 | self.assertTrue(self.roger_job2_id not in partner_ids) | ||
607 | 52 | |||
608 | 53 | def test_01_show_all_positions(self): | ||
609 | 54 | """Check that all contact are show if context is empty or explicitly state to display all positions""" | ||
610 | 55 | cr, uid = self.cr, self.uid | ||
611 | 56 | |||
612 | 57 | partner_ids = self.partner.search(cr, uid, [], context=None) | ||
613 | 58 | self.assertTrue(self.bob_job1_id in partner_ids) | ||
614 | 59 | self.assertTrue(self.roger_job2_id in partner_ids) | ||
615 | 60 | |||
616 | 61 | ctx = {'search_show_all_positions': True} | ||
617 | 62 | partner_ids = self.partner.search(cr, uid, [], context=ctx) | ||
618 | 63 | self.assertTrue(self.bob_job1_id in partner_ids) | ||
619 | 64 | self.assertTrue(self.roger_job2_id in partner_ids) | ||
620 | 65 | |||
621 | 66 | def test_02_reading_other_contact_one2many_show_all_positions(self): | ||
622 | 67 | """Check that readonly partner's ``other_contact_ids`` return all values whatever the context""" | ||
623 | 68 | cr, uid = self.cr, self.uid | ||
624 | 69 | |||
625 | 70 | def read_other_contacts(pid, context=None): | ||
626 | 71 | return self.partner.read(cr, uid, [pid], ['other_contact_ids'], context=context)[0]['other_contact_ids'] | ||
627 | 72 | |||
628 | 73 | def read_contacts(pid, context=None): | ||
629 | 74 | return self.partner.read(cr, uid, [pid], ['child_ids'], context=context)[0]['child_ids'] | ||
630 | 75 | |||
631 | 76 | ctx = None | ||
632 | 77 | self.assertEqual(read_other_contacts(self.bob_contact_id, context=ctx), [self.bob_job1_id]) | ||
633 | 78 | ctx = {'search_show_all_positions': False} | ||
634 | 79 | self.assertEqual(read_other_contacts(self.bob_contact_id, context=ctx), [self.bob_job1_id]) | ||
635 | 80 | ctx = {'search_show_all_positions': True} | ||
636 | 81 | self.assertEqual(read_other_contacts(self.bob_contact_id, context=ctx), [self.bob_job1_id]) | ||
637 | 82 | |||
638 | 83 | ctx = None | ||
639 | 84 | self.assertTrue(self.bob_job1_id in read_contacts(self.main_partner_id, context=ctx)) | ||
640 | 85 | ctx = {'search_show_all_positions': False} | ||
641 | 86 | self.assertTrue(self.bob_job1_id in read_contacts(self.main_partner_id, context=ctx)) | ||
642 | 87 | ctx = {'search_show_all_positions': True} | ||
643 | 88 | self.assertTrue(self.bob_job1_id in read_contacts(self.main_partner_id, context=ctx)) | ||
644 | 89 | |||
645 | 90 | def test_03_search_match_attached_contacts(self): | ||
646 | 91 | """Check that searching partner also return partners having attached contacts matching search criteria""" | ||
647 | 92 | cr, uid = self.cr, self.uid | ||
648 | 93 | # Bob's contact has one other position which is related to 'Your Company' | ||
649 | 94 | # so search for all contacts working for 'Your Company' should contain bob position. | ||
650 | 95 | partner_ids = self.partner.search(cr, uid, [('parent_id', 'ilike', 'Your Company')], context=None) | ||
651 | 96 | self.assertTrue(self.bob_job1_id in partner_ids) | ||
652 | 97 | |||
653 | 98 | # but when searching without 'all positions', we should get the position standalone contact instead. | ||
654 | 99 | ctx = {'search_show_all_positions': False} | ||
655 | 100 | partner_ids = self.partner.search(cr, uid, [('parent_id', 'ilike', 'Your Company')], context=ctx) | ||
656 | 101 | self.assertTrue(self.bob_contact_id in partner_ids) | ||
657 | 102 | |||
658 | 103 | def test_04_contact_creation(self): | ||
659 | 104 | """Check that we're begin to create a contact""" | ||
660 | 105 | cr, uid = self.cr, self.uid | ||
661 | 106 | |||
662 | 107 | # Create a contact using only name | ||
663 | 108 | new_contact_id = self.partner.create(cr, uid, {'name': 'Bob Egnops'}) | ||
664 | 109 | self.assertEqual(self.partner.browse(cr, uid, new_contact_id).contact_type, 'standalone') | ||
665 | 110 | |||
666 | 111 | # Create a contact with only contact_id | ||
667 | 112 | new_contact_id = self.partner.create(cr, uid, {'contact_id': self.bob_contact_id}) | ||
668 | 113 | new_contact = self.partner.browse(cr, uid, new_contact_id) | ||
669 | 114 | self.assertEqual(new_contact.name, 'Bob Egnops') | ||
670 | 115 | self.assertEqual(new_contact.contact_type, 'attached') | ||
671 | 116 | |||
672 | 117 | # Create a contact with both contact_id and name; | ||
673 | 118 | # contact's name should override provided value in that case | ||
674 | 119 | new_contact_id = self.partner.create(cr, uid, {'contact_id': self.bob_contact_id, 'name': 'Rob Egnops'}) | ||
675 | 120 | self.assertEqual(self.partner.browse(cr, uid, new_contact_id).name, 'Bob Egnops') | ||
676 | 121 | |||
677 | 122 | # Reset contact to standalone | ||
678 | 123 | self.partner.write(cr, uid, [new_contact_id], {'contact_id': False}) | ||
679 | 124 | self.assertEqual(self.partner.browse(cr, uid, new_contact_id).contact_type, 'standalone') | ||
680 | 125 | |||
681 | 126 | def test_05_contact_fields_sync(self): | ||
682 | 127 | """Check that contact's fields are correctly synced between parent contact or related contacts""" | ||
683 | 128 | cr, uid = self.cr, self.uid | ||
684 | 129 | |||
685 | 130 | # Test DOWNSTREAM sync | ||
686 | 131 | self.partner.write(cr, uid, [self.bob_contact_id], {'name': 'Rob Egnops'}) | ||
687 | 132 | self.assertEqual(self.partner.browse(cr, uid, self.bob_job1_id).name, 'Rob Egnops') | ||
688 | 133 | |||
689 | 134 | # Test UPSTREAM sync | ||
690 | 135 | self.partner.write(cr, uid, [self.bob_job1_id], {'name': 'Bob Egnops'}) | ||
691 | 136 | self.assertEqual(self.partner.browse(cr, uid, self.bob_contact_id).name, 'Bob Egnops') |
Bonjour Xavier,
Voila quelques remarques concernant le module:
l87: vals['name'] = self.browse(...). Il manque un .name apres le browse. Cependant 'name' est required sur le partenaire donc ce cas ne devrait pas arriver (du moins via l'interface web)...
La valeur du context search_ show_all_ positions va doubler le nombre de recherche sur partner (eg: le name_get sur partner est deja lent avec un grand nombre de partenaire). J'ai beau reflechir je ne vois pas comment on pourrait eviter cela si on veut en effet cacher les resultats des attached.
Est-ce que le champ contact_type est réellement nécessaire ? Il se resume globalement a indiquer si contact_id vaut False.