Merge lp:~hirt/ocb-addons/6.1_base_contact_finalize into lp:ocb-addons/6.1

Proposed by Etienne Hirt on 2013-11-05
Status: Needs review
Proposed branch: lp:~hirt/ocb-addons/6.1_base_contact_finalize
Merge into: lp:ocb-addons/6.1
Diff against target: 973 lines (+559/-100)
4 files modified
base_contact/__openerp__.py (+11/-6)
base_contact/base_contact.py (+294/-53)
base_contact/base_contact_view.xml (+252/-40)
base_contact/security/ir.model.access.csv (+2/-1)
To merge this branch: bzr merge lp:~hirt/ocb-addons/6.1_base_contact_finalize
Reviewer Review Type Date Requested Status
Omar (Pexego) code review, no test 2013-11-05 Needs Fixing on 2013-11-20
Review via email: mp+193941@code.launchpad.net

Description of the change

Finalising the base contact 6.1 desig by adding the missing fields as described in https://bugs.launchpad.net/openobject-addons/+bug/923440

To post a comment you must log in.
Pedro Manuel Baeza (pedro.baeza) wrote :

Hi, Etienne, thank you for making again the MP properly. I will check it, but it's a huge diff for only a bugfix. I'm not sure if this is going to fit into OCB project and goals.

Let's see what others reviewers say.

Regards.

Etienne Hirt (hirt) wrote :

Dear Pedro,

Thanks for your offer to check it.

Somehow the redesign of the base_contact module for 6.1 was never
finished as it was abandoned by openerp. I prepared the fixes some time
ago to have the function available at Art of Technology. This
functionality is also the main reason for us not considering Version 7
(yet).

The effort now is to have others being able to profit from it, too and
also to profit from the community.

Best Regards

Etienne

On 05.11.2013 17:09, Pedro Manuel Baeza wrote:
> Hi, Etienne, thank you for making again the MP properly. I will check it, but it's a huge diff for only a bugfix. I'm not sure if this is going to fit into OCB project and goals.
>
> Let's see what others reviewers say.
>
> Regards.

Omar (Pexego) (omar7r) wrote :

At first sight, you should remove pdb import and the commented lines, on the other hand, you convert the partner_id field, from related to function field without search function, this field could be used in several addons as filter, with function field will not work anymore at least without search function.
IMHO I prefer name_search as works now, because your way don't support, for example, with compound first names or more than one last name, this is applyable to view_init function too.

It's a huge diff as Pedro said and I think that isn't the best way to complete this addon but we can wait for opinion of other reviewers, for now, I will mark it as needs fixing, for the up things.

Regards

review: Needs Fixing (code review, no test)
Etienne Hirt (hirt) wrote :

Dear Omar,

thanks for the review and your valuable comments. Please check my
answers below.

Best Regards

Etienne

On 20.11.2013 01:19, Omar (Pexego) wrote:
> Review: Needs Fixing code review, no test
>
> At first sight, you should remove pdb import and the commented lines, on the other hand,
mostly done
> you convert the partner_id field, from related to function field without search function, this field could be used in several addons as filter, with function field will not work anymore at least without search function.
With the related field you have no real control what partner is selected
as main partner.
Originally I wanted to have conditional storage but this somehow did not
work. I removed it then because contacts are nowhere searched for
partners as far as I know. Also IMHO the related fields can not be
searched if not stored.
What do you propose?
> IMHO I prefer name_search as works now, because your way don't support, for example, with compound first names or more than one last name, this is applyable to view_init function too.
Please check again the proposed search function. Because it's intention
is to extend the original function instead of limit it. Should we also
consider one lastname with multiple/compound first names?

Without the view_init function all newly entered contacts are filled
into lastname only. Thus the proposed function is an extension but not
perfect as it can not guess that multiple Firstnames are used.
>
> It's a huge diff as Pedro said and I think that isn't the best way to complete this addon but we can wait for opinion of other reviewers, for now, I will mark it as needs fixing, for the up things.
>
> Regards

Omar (Pexego) (omar7r) wrote :

Hi Etienne,

Thanks for updating. Related fields can not be grouped but, searched yes. Other solution, you could have a search function defined in your function field.

At Spain for example, everyone have two last names and one or two first names, like Jose Antonio or Diego Armando, we could not have limitation in that.

Regards

Etienne Hirt (hirt) wrote :

Hi Omar,

Thanks for insisting. With your inputs I found now a solution that supports any number of first and lastname for all searches. This was not the case in the baseline I started with.

Also the partner_id I could change back to related as the sorting of the address_id fits the requirements for the main job.

The view_init is a helper function that is a 90% solution but helps the user even if the remaining 10% when there is more than one firstname.

The partner_contact class is ready for re-reviewing. But the partner_address requires some further work. I therefore set the state to WIP.

Best Regards

6817. By Etienne Hirt on 2013-11-23

remove the executable flag that wrongly added in last commit

6818. By Etienne Hirt on 2014-01-05

* store partner_id in location directly to solve usability issue of having
  locations assigned to partners if any and to view it while using.
  Attention: this commit does not store the partner_ids yet at updating
             but for install the module it is added.
* override copy method for res.partner.location to avoid copying the
  related addresses's

6819. By Etienne Hirt on 2014-01-05

add auto_init for partner_id in res_partner_location

6820. By Etienne Hirt on 2014-01-18

* streamline location editing
* enhance and streamline search

6821. By Etienne Hirt on 2014-01-18

delete action as menu deletion did not work for upgrade contact/address with new action

6822. By Etienne Hirt on 2014-01-18

*further streamlining location editing
*restrict deletion of partner when there are assigned addresses

6823. By Etienne Hirt on 2014-01-19

* changes of partner of address does still not fully update the location partner_id
to be continued

6824. By Etienne Hirt on 2014-01-19

* use storage triggers for partner_id in res_partner_location
* minor UI adjustments:
** group extended for location menu but not addresses
** add partner to functions/addresses in contact form

6825. By Etienne Hirt on 2014-01-20

* enhance contact and location search
* fix length of name in contact to allow full lenght of last_name + first_name

6826. By Etienne Hirt on 2014-01-24

void delection of assigned locations

6827. By Etienne Hirt on 2014-05-29

commit files no longer executable

6828. By Etienne Hirt on 2014-05-29

do merge

6829. By Etienne Hirt on 2014-07-19

add street to res_partner_address displayname

6830. By Etienne Hirt on 2014-11-17

add other phone to address tree and form

6831. By Etienne Hirt on 2015-01-16

* store all related fields from location if one changes
* add trigger on change_location_id also because the on_change function does not store all changes!

Unmerged revisions

6831. By Etienne Hirt on 2015-01-16

* store all related fields from location if one changes
* add trigger on change_location_id also because the on_change function does not store all changes!

6830. By Etienne Hirt on 2014-11-17

add other phone to address tree and form

6829. By Etienne Hirt on 2014-07-19

add street to res_partner_address displayname

6828. By Etienne Hirt on 2014-05-29

do merge

6827. By Etienne Hirt on 2014-05-29

commit files no longer executable

6826. By Etienne Hirt on 2014-01-24

void delection of assigned locations

6825. By Etienne Hirt on 2014-01-20

* enhance contact and location search
* fix length of name in contact to allow full lenght of last_name + first_name

6824. By Etienne Hirt on 2014-01-19

* use storage triggers for partner_id in res_partner_location
* minor UI adjustments:
** group extended for location menu but not addresses
** add partner to functions/addresses in contact form

6823. By Etienne Hirt on 2014-01-19

* changes of partner of address does still not fully update the location partner_id
to be continued

6822. By Etienne Hirt on 2014-01-18

*further streamlining location editing
*restrict deletion of partner when there are assigned addresses

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file 'account_asset/i18n/ca.po' (properties changed: +x to -x)
2=== modified file 'account_asset/i18n/de.po' (properties changed: +x to -x)
3=== modified file 'account_asset/i18n/es.po' (properties changed: +x to -x)
4=== modified file 'account_asset/i18n/es_CR.po' (properties changed: +x to -x)
5=== modified file 'account_asset/i18n/fr.po' (properties changed: +x to -x)
6=== modified file 'account_asset/i18n/fr_BE.po' (properties changed: +x to -x)
7=== modified file 'account_asset/i18n/pl.po' (properties changed: +x to -x)
8=== modified file 'account_asset/i18n/pt.po' (properties changed: +x to -x)
9=== modified file 'account_asset/i18n/sv.po' (properties changed: +x to -x)
10=== modified file 'account_asset/security/ir.model.access.csv' (properties changed: +x to -x)
11=== modified file 'account_asset/wizard/__init__.py' (properties changed: +x to -x)
12=== modified file 'account_asset/wizard/account_asset_change_duration.py' (properties changed: +x to -x)
13=== modified file 'account_asset/wizard/wizard_asset_compute.py' (properties changed: +x to -x)
14=== modified file 'base_contact/__openerp__.py'
15--- base_contact/__openerp__.py 2012-01-31 13:36:57 +0000
16+++ base_contact/__openerp__.py 2015-01-16 20:04:03 +0000
17@@ -21,7 +21,7 @@
18
19 {
20 'name': 'Contacts Management',
21- 'version': '1.0',
22+ 'version': '1.1',
23 'category': 'Customer Relationship Management',
24 'complexity': "expert",
25 'description': """
26@@ -35,12 +35,17 @@
27
28 It also adds new menu items located in
29 Purchases / Address Book / Contacts
30- Sales / Address Book / Contacts
31-
32-Pay attention that this module converts the existing addresses into "addresses + contacts". It means that some fields of the addresses will be missing (like the contact name), since these are supposed to be defined in an other object.
33+ Sales / Address Book / Contacts and Locations
34+
35+Pay attention that this module converts the existing addresses into "addresses + contacts".
36+It means that some fields of the addresses will be missing (the contact name is copied into
37+addresses to be compatible with other modules contact name). Others are supposed to be defined in an other object.
38+
39+All UI work with lastname(s) firstname(s) but the order has to be observed.
40+When entering a new contact only one firstname is assumed. You migh add the additional firstname(s) later
41 """,
42- 'author': 'OpenERP SA',
43- 'website': 'http://www.openerp.com',
44+ 'author': 'OpenERP SA, Art of Technology AG',
45+ 'website': 'http://www.openerp.com, http://www.aotag.ch',
46 'depends': ['base','process'],
47 'init_xml': [],
48 'update_xml': [
49
50=== modified file 'base_contact/base_contact.py'
51--- base_contact/base_contact.py 2013-09-10 15:12:35 +0000
52+++ base_contact/base_contact.py 2015-01-16 20:04:03 +0000
53@@ -27,31 +27,36 @@
54
55 _name = "res.partner.contact"
56 _description = "Contact"
57-
58+
59 def _name_get_full(self, cr, uid, ids, prop, unknow_none, context=None):
60 result = {}
61 for rec in self.browse(cr, uid, ids, context=context):
62- result[rec.id] = rec.last_name+' '+(rec.first_name or '')
63+ if(rec.first_name):
64+ #last_name is a required field. Therefore always available
65+ result[rec.id] = rec.last_name+' '+rec.first_name
66+ else:
67+ result[rec.id] = rec.last_name
68+
69 return result
70
71 _columns = {
72- 'name': fields.function(_name_get_full, string='Name', size=64, type="char", store=True, select=True),
73+ 'name': fields.function(_name_get_full, string='Name', size=128, type="char", store=True, select=True),
74 'last_name': fields.char('Last Name', size=64, required=True),
75 'first_name': fields.char('First Name', size=64),
76 'mobile': fields.char('Mobile', size=64),
77- 'title': fields.many2one('res.partner.title','Title', domain=[('domain','=','contact')]),
78- 'website': fields.char('Website', size=120),
79+ 'title': fields.many2one('res.partner.title','Title', domain=[('domain','=','contact')], required=True),
80+ 'website': fields.char('Private Website', size=120),
81 'lang_id': fields.many2one('res.lang', 'Language'),
82 'job_ids': fields.one2many('res.partner.address', 'contact_id', 'Functions and Addresses'),
83 'country_id': fields.many2one('res.country','Nationality'),
84 'birthdate': fields.char('Birthdate', size=64),
85 'active': fields.boolean('Active', help="If the active field is set to False,\
86- it will allow you to hide the partner contact without removing it."),
87+ it will allow you to hide the partner contact without removing it."),
88 'partner_id': fields.related('job_ids', 'partner_id', type='many2one',\
89 relation='res.partner', string='Main Employer'),
90 'function': fields.related('job_ids', 'function', type='char', \
91 string='Main Function'),
92- 'email': fields.char('E-Mail', size=240),
93+ 'email': fields.char('Private E-Mail', size=240),
94 'comment': fields.text('Notes', translate=True),
95 'photo': fields.binary('Photo'),
96 }
97@@ -67,17 +72,6 @@
98
99 _order = "name"
100
101- def name_search(self, cr, uid, name='', args=None, operator='ilike', context=None, limit=None):
102- if not args:
103- args = []
104- if context is None:
105- context = {}
106- if name:
107- ids = self.search(cr, uid, ['|',('name', operator, name),('first_name', operator, name)] + args, limit=limit, context=context)
108- else:
109- ids = self.search(cr, uid, args, limit=limit, context=context)
110- return self.name_get(cr, uid, ids, context=context)
111-
112 def name_get(self, cr, uid, ids, context=None):
113 result = {}
114 for obj in self.browse(cr, uid, ids, context=context):
115@@ -85,6 +79,32 @@
116 if obj.partner_id:
117 result[obj.id] = result[obj.id] + ', ' + obj.partner_id.name
118 return result.items()
119+
120+ def view_init(self, cr, uid, fields, context=None):
121+ """
122+ This function shall fill the last_name and first_name if empty from context default_name
123+ It assumes entry of lastnames Firstname or lastname only. Entry of multiple
124+ firstnames has to be adjusted afterwards
125+ """
126+
127+ if context is None:
128+ context = {}
129+ else:
130+ default_name = context.get('default_name')
131+ if default_name:
132+ split = default_name.split(' ')
133+ length = len(split)
134+ if length > 1:
135+ context.update({'default_first_name': split[length-1]})
136+ #join again
137+ default_name = split[0]
138+ for i in range(1,length-1):
139+ default_name = default_name + ' ' + split[i]
140+
141+ #either fill joined rest or original text into context for last name
142+ context.update({'default_last_name': default_name})
143+ pass
144+
145
146 def _auto_init(self, cr, context=None):
147 def table_exists(view_name):
148@@ -99,9 +119,9 @@
149 cr.execute("""
150 INSERT INTO
151 res_partner_contact
152- (id,name,last_name,title,active,email,mobile,birthdate)
153+ (id,name, last_name,first_name,title,active,email,mobile,birthdate)
154 SELECT
155- id,COALESCE(name, '/'),COALESCE(name, '/'),title,true,email,mobile,birthdate
156+ id,name, COALESCE(name, '/'),COALESCE(name, '/'),title,true,email,mobile,birthdate
157 FROM
158 res_partner_address""")
159 cr.execute("alter table res_partner_address add contact_id int references res_partner_contact")
160@@ -115,6 +135,14 @@
161 class res_partner_location(osv.osv):
162 _name = 'res.partner.location'
163 _rec_name = 'street'
164+
165+ def _get_location_from_address_ids(self, cr, uid, ids, context=None):
166+ result = {}
167+
168+ for location in self.pool.get('res.partner.location').search(cr, uid, [('job_ids','in',ids)]):
169+ result[location] = True
170+
171+ return result.keys()
172 _columns = {
173 'street': fields.char('Street', size=128),
174 'street2': fields.char('Street2', size=128),
175@@ -123,13 +151,55 @@
176 'state_id': fields.many2one("res.country.state", 'Fed. State', domain="[('country_id','=',country_id)]"),
177 'country_id': fields.many2one('res.country', 'Country'),
178 'company_id': fields.many2one('res.company', 'Company',select=1),
179- 'job_ids': fields.one2many('res.partner.address', 'location_id', 'Contacts'),
180+ 'job_ids': fields.one2many('res.partner.address', 'location_id', 'Addresses'),
181+ #storage triggers are important if an address is changed to another partner etc.
182 'partner_id': fields.related('job_ids', 'partner_id', type='many2one',\
183- relation='res.partner', string='Main Partner'),
184+ relation='res.partner', string='Main Partner',
185+ store = {
186+ 'res.partner.location': (lambda self,cr,uid,ids,c=None: ids, ['job_ids'], 10),
187+ 'res.partner.address': (_get_location_from_address_ids, ['partner_id'], 10),
188+ }),
189+ 'contact_id': fields.related('job_ids', 'contact_id', type='many2one',\
190+ relation='res.partner.contact', string='First Contact'),
191 }
192 _defaults = {
193 'company_id': lambda s,cr,uid,c: s.pool.get('res.company')._company_default_get(cr, uid, 'res.partner.address', context=c),
194 }
195+
196+ def copy(self, cr, uid, id, default=None, context=None):
197+ """
198+ Overrides orm copy method in order to avoid copying of related objects such as
199+ * job_ids
200+ @param self: the object pointer
201+ @param cr: the current row, from the database cursor,
202+ @param uid: the current user’s ID for security checks,
203+ @param id: Id of crm_lead
204+ @param default: Dictionary of default values for copy.
205+ @param context: A standard dictionary for contextual values
206+ """
207+ if context is None:
208+ context = {}
209+ if default is None:
210+ default = {}
211+
212+ default.update({
213+ 'job_ids': [],
214+ })
215+ return super(osv.osv, self).copy(cr, uid, id, default, context=context)
216+
217+ def view_init(self, cr, uid, fields, context=None):
218+ """
219+ This function shall fill the street from the entry
220+ """
221+
222+ if context is None:
223+ context = {}
224+ else:
225+ default_name = context.get('default_name')
226+ if default_name:
227+ context.update({'default_street': default_name})
228+ pass
229+
230 def _auto_init(self, cr, context=None):
231 def table_exists(view_name):
232 cr.execute('SELECT count(relname) FROM pg_class WHERE relname = %s', (view_name,))
233@@ -144,21 +214,32 @@
234 INSERT INTO
235 res_partner_location
236 (id,street,street2,zip,city,
237- state_id,country_id,company_id)
238+ state_id,country_id,company_id,partner_id)
239 SELECT
240 id,street,street2,zip,city,
241- state_id,country_id,company_id
242+ state_id,country_id,company_id,partner_id
243 FROM
244 res_partner_address""")
245 cr.execute("alter table res_partner_address add location_id int references res_partner_location")
246 cr.execute("update res_partner_address set location_id=id")
247 cr.execute("select setval('res_partner_location_id_seq', (select max(id)+1 from res_partner_address))")
248+ else:
249+ cr.execute("""
250+ UPDATE
251+ res_partner_location l
252+ SET
253+ partner_id = addr.partner_id
254+ FROM
255+ res_partner_address addr INNER JOIN res_partner_location loc ON (loc.id = addr.location_id)
256+ WHERE
257+ l.id = loc.id and l.partner_id is null and addr.partner_id is not null """)
258+
259
260 def name_get(self, cr, uid, ids, context=None):
261 result = {}
262 for obj in self.browse(cr, uid, ids, context=context):
263 res = []
264- if obj.partner_id: res.append(obj.partner_id.name_get()[0][1])
265+ if obj.street: res.append(obj.street)
266 if obj.city: res.append(obj.city)
267 if obj.country_id: res.append(obj.country_id.name_get()[0][1])
268 result[obj.id] = ', '.join(res)
269@@ -168,15 +249,83 @@
270
271 class res_partner_address(osv.osv):
272 _inherit = 'res.partner.address'
273+
274+ def _get_address_from_location_ids(self, cr, uid, ids, context=None):
275+ result = {}
276+
277+ for addr in self.pool.get('res.partner.address').search(cr, uid, [('location_id','in',ids)]):
278+ result[addr] = True
279+
280+ return result.keys()
281+
282+ def _get_address_from_contact_ids(self, cr, uid, ids, context=None):
283+ result = {}
284+ for addr in self.pool.get('res.partner.address').search(cr, uid, [('contact_id','in',ids)]):
285+ result[addr] = True
286+ return result.keys()
287+
288+ def _get_own_addresses(self, cr, uid, ids, context=None):
289+ result = {}
290+ for id in ids:
291+ result[id] = True
292+ return result.keys()
293+
294
295 def _default_location_id(self, cr, uid, context=None):
296 if context is None:
297 context = {}
298 if not context.get('default_partner_id',False):
299 return False
300- ids = self.pool.get('res.partner.location').search(cr, uid, [('partner_id','=',context['default_partner_id'])], context=context)
301+ #if a default location is available than this is the default location
302+ address_pool = self.pool.get('res.partner.address')
303+ address_ids = address_pool.search(cr, uid, [('partner_id','=',context['default_partner_id']),('type','=','default')], context=context)
304+ ids=[]
305+ if address_ids:
306+ addresses = address_pool.browse(cr, uid, address_ids)
307+ for address in addresses:
308+ if address and address.location_id:
309+ ids.append(address.location_id.id)
310+ break
311+ if not ids:
312+ ids = self.pool.get('res.partner.location').search(cr, uid, [('partner_id','=',context['default_partner_id'])], context=context)
313+
314 return ids and ids[0] or False
315+
316+ def onchange_partner_id(self,cr, uid, ids, partner_id=False, context={}):
317+ #if this address is the only one that points to its location then keep the location
318+ if ids:
319+ address = self.pool.get('res.partner.address').browse(cr, uid, ids[0], context=context)
320+ if address.location_id:
321+ location = self.pool.get('res.partner.location').browse(cr, uid, address.location_id.id, context=context)
322+ if len(location.job_ids) <=1:
323+ return {}
324+
325+ if partner_id:
326+ context['default_partner_id'] = partner_id
327+ location_id = self._default_location_id(cr, uid, context)
328+ else:
329+ location_id = False
330+ return {'value':{
331+ 'location_id': location_id,
332+ }
333+ }
334
335+ def onchange_contact_id(self,cr, uid, ids, contact_id=False, context={}):
336+ if not contact_id:
337+ return {'value':{
338+ 'mobile': False,
339+ #setting of name is not required. If no contact_id it is set to False by the relation
340+ 'title': False,
341+ }
342+ }
343+ contact = self.pool.get('res.partner.contact').browse(cr, uid, contact_id, context=context)
344+ return {'value':{
345+ 'mobile': contact.mobile,
346+ 'name': contact.name,
347+ 'title': contact.title and contact.title.id or False,
348+ }}
349+
350+
351 def onchange_location_id(self,cr, uid, ids, location_id=False, context={}):
352 if not location_id:
353 return {}
354@@ -191,37 +340,73 @@
355 }}
356
357 _columns = {
358- 'location_id' : fields.many2one('res.partner.location', 'Location'),
359+ #do not allow to delete a partner as long as jobs are related to it
360+ 'partner_id': fields.many2one('res.partner', 'Partner Name', ondelete='restrict', select=True, help="Keep empty for a private address, not related to partner."),
361+ 'location_id' : fields.many2one('res.partner.location', 'Location', ondelete='restrict',),
362 'contact_id' : fields.many2one('res.partner.contact', 'Contact'),
363+
364+ #add private type
365+ 'type': fields.selection( [ ('default','Default'),('invoice','Invoice'), ('delivery','Delivery'), ('contact','Contact'), ('other','Other'), ('private', 'Private') ],'Address Type', help="Used to select automatically the right address according to the context in sales and purchases documents."),
366+
367+
368+ #field for administer functions
369+ 'sequence_contact': fields.integer('Contact Seq.',help='Order of\
370+ importance of this address in the list of addresses of the linked contact'),
371+ 'date_start': fields.date('Date Start',help="Start date of job(Joining Date)"),
372+ 'date_stop': fields.date('Date Stop', help="Last date of job"),
373+ 'state': fields.selection([('past', 'Past'),('current', 'Current')], \
374+ 'State', required=True, help="Status of Address"),
375
376 # fields from location
377- 'street': fields.related('location_id', 'street', string='Street', type="char", store=True, size=128),
378- 'street2': fields.related('location_id', 'street2', string='Street2', type="char", store=True, size=128),
379- 'zip': fields.related('location_id', 'zip', string='Zip', type="char", store=True, change_default=True, size=24),
380- 'city': fields.related('location_id', 'city', string='City', type="char", store=True, size=128),
381- 'state_id': fields.related('location_id', 'state_id', relation="res.country.state", string='Fed. State', type="many2one", store=True, domain="[('country_id','=',country_id)]"),
382- 'country_id': fields.related('location_id', 'country_id', type='many2one', string='Country', store=True, relation='res.country'),
383+ #Trigger for change of location id of self seems not required because this is handled by onchange_location_id
384+ # triggered in the form, but this does not properly store the values!. None looks at any fields for change
385+ 'street': fields.related('location_id', 'street', string='Street',
386+ type="char", size=128, readonly=True,
387+ store = { 'res.partner.address': (lambda self, cr, uid, ids, c={}: ids, ['location_id'], 5),
388+ 'res.partner.location': (_get_address_from_location_ids, None, 10),}),
389+ 'street2': fields.related('location_id', 'street2', string='Street2',
390+ type="char", size=128, readonly=True,
391+ store = {'res.partner.address': (lambda self, cr, uid, ids, c={}: ids, ['location_id'], 5),
392+ 'res.partner.location': (_get_address_from_location_ids, None, 10),}),
393+ 'zip': fields.related('location_id', 'zip', string='Zip',
394+ type="char", change_default=True, size=24, readonly=True,
395+ store = {'res.partner.address': (lambda self, cr, uid, ids, c={}: ids, ['location_id'], 5),
396+ 'res.partner.location': (_get_address_from_location_ids, None, 10),}),
397+ 'city': fields.related('location_id', 'city', string='City',
398+ type="char", size=128, readonly=True,
399+ store = {'res.partner.address': (lambda self, cr, uid, ids, c={}: ids, ['location_id'], 5),
400+ 'res.partner.location': (_get_address_from_location_ids, None, 10),}),
401+ 'state_id': fields.related('location_id', 'state_id', relation="res.country.state",
402+ string='Fed. State', type="many2one",
403+ domain="[('country_id','=',country_id)]", readonly=True,
404+ store = {'res.partner.address': (lambda self, cr, uid, ids, c={}: ids, ['location_id'], 5),
405+ 'res.partner.location': (_get_address_from_location_ids, None, 10),}),
406+ 'country_id': fields.related('location_id', 'country_id', type='many2one',
407+ string='Country', relation='res.country', readonly=True,
408+ store = {'res.partner.address': (lambda self, cr, uid, ids, c={}: ids, ['location_id'], 5),
409+ 'res.partner.location': (_get_address_from_location_ids, None, 10),}),
410
411- 'phone': fields.char('Phone', size=64),
412- 'fax': fields.char('Fax', size=64),
413- 'email': fields.char('E-Mail', size=240),
414+ #this field is missing compared to 6.0 implementation
415+ 'other': fields.char('Other Phone', size=64, help='Additional phone field'),
416
417 # fields from contact
418 'mobile' : fields.related('contact_id', 'mobile', type='char', size=64, string='Mobile'),
419- 'name' : fields.related('contact_id', 'name', type='char', size=64, string="Contact Name", store=True),
420- 'title' : fields.related('contact_id', 'title', type='many2one', relation='res.partner.title', string="Title", store=True),
421+ #store = {'res.partner.contact': (_get_address_from_contact_ids, ['mobile'], 10),
422+ # 'res.partner.address': (_get_own_addresses,['contact_id'], 20)}), @bug: query wants to store in crm_lead!!!!
423+ 'name' : fields.related('contact_id', 'name', type='char', size=64, string="Contact Name",
424+ store = {'res.partner.contact': (_get_address_from_contact_ids, ['last_name', 'first_name'], 10),
425+ 'res.partner.address': (_get_own_addresses,['contact_id'], 20)}),
426+ 'title' : fields.related('contact_id', 'title', type='many2one', relation='res.partner.title', string="Title"),
427 }
428 def create(self, cr, uid, data, context={}):
429- if not data.get('location_id', False):
430- loc_id = self.pool.get('res.partner.location').create(cr, uid, {
431- 'street': data.get('street',''),
432- 'street2': data.get('street2',''),
433- 'zip': data.get('zip',''),
434- 'city': data.get('city',''),
435- 'country_id': data.get('country_id',False),
436- 'state_id': data.get('state_id',False)
437- }, context=context)
438- data['location_id'] = loc_id
439+ if data.get('location_id', False):
440+ location = self.pool.get('res.partner.location').browse(cr, uid, [data['location_id']])[0]
441+ data['street'] = location.street or False
442+ data['street2'] = location.street2 or False
443+ data['zip'] = location.zip or False
444+ data['city'] = location.city or False
445+ data['country_id'] = location.country_id and location.country_id.id or False
446+ data['state_id'] = location.state_id and location.state_id.id or False
447 result = super(res_partner_address, self).create(cr, uid, data, context=context)
448 return result
449
450@@ -229,26 +414,82 @@
451 result = {}
452 for rec in self.browse(cr,uid, ids, context=context):
453 res = []
454+ if rec.contact_id and rec.contact_id.name:
455+ res.append(rec.contact_id.name)
456 if rec.partner_id:
457 res.append(rec.partner_id.name_get()[0][1])
458- if rec.contact_id and rec.contact_id.name:
459- res.append(rec.contact_id.name)
460 if rec.location_id:
461+ if rec.location_id.street: res.append(rec.location_id.street)
462 if rec.location_id.city: res.append(rec.location_id.city)
463 if rec.location_id.country_id: res.append(rec.location_id.country_id.name_get()[0][1])
464 result[rec.id] = ', '.join(res)
465 return result.items()
466
467 _defaults = {
468- 'location_id': _default_location_id
469+ 'location_id': _default_location_id,
470+ 'sequence_contact' : lambda *a: 0,
471+ 'state': lambda *a: 'current',
472 }
473+
474+ def name_search(self, cr, uid, name='', args=None, operator='ilike', context=None, limit=None):
475+ """
476+ Search function searches in (contact) name and partner_id depending on context
477+ while using otherwise the standard implementation
478+ """
479+ if not args:
480+ args = []
481+ if context is None:
482+ context = {}
483+
484+ if name and context.get('contact_display', 'contact') == 'partner':
485+ ids = self.search(cr, uid, ['|',('name', operator, name),('partner_id', operator, name)] + args, limit=limit, context=context)
486+ return self.name_get(cr, uid, ids, context=context)
487+ else:
488+ return super(res_partner_address, self).name_search(cr, uid, name, args, operator, context, limit)
489+
490+
491+
492+ #order by name. default addresses are assumed to have no name = empty -> are last entry!
493+ _order='state, name, sequence_contact'
494+
495+
496
497 def default_get(self, cr, uid, fields=[], context=None):
498 if context is None:
499 context = {}
500- if 'default_type' in context:
501- del context['default_type']
502+ #if 'default_type' in context:
503+ # del context['default_type']
504 return super(res_partner_address, self).default_get(cr, uid, fields, context)
505
506 res_partner_address()
507
508+class res_partner(osv.osv):
509+ _inherit = 'res.partner'
510+
511+ def _default_address_id(self, cr, uid, ids, prop, unknow_none, context=None):
512+
513+ res = dict.fromkeys(ids, False)
514+
515+ all_ids = self.pool.get('res.partner.address').search(cr, uid, [('partner_id','in',ids), ('type','=','default')])
516+
517+ addresses = self.pool.get('res.partner.address').browse(cr, uid, all_ids)
518+ for addr in addresses:
519+ if(res[addr.partner_id.id] == False):
520+ res[addr.partner_id.id] = addr.id
521+
522+ return res
523+
524+
525+ _columns = {#default_address_id has to be stored to enable search of the related fields!!! Somehow trigger is not required!
526+ 'default_address_id': fields.function(_default_address_id, type='many2one', obj='res.partner.address', string='address_id', store = True),
527+ 'phone': fields.related('default_address_id', 'phone', type='char', string='Phone'),
528+ 'fax': fields.related('default_address_id', 'fax', type='char', string='Fax', store = False),
529+ 'email': fields.related('default_address_id', 'email', type='char', size=240, string='E-mail', store = False),
530+ 'street': fields.related('default_address_id', 'street', type='char', string='Street', store = False),
531+ 'city': fields.related('default_address_id', 'city', type='char', string='City', store = False),
532+ 'country': fields.related('default_address_id','country_id', type='many2one', relation='res.country', string='Country'),
533+ }
534+
535+res_partner()
536+
537+
538
539=== modified file 'base_contact/base_contact_view.xml'
540--- base_contact/base_contact_view.xml 2012-01-31 13:36:57 +0000
541+++ base_contact/base_contact_view.xml 2015-01-16 20:04:03 +0000
542@@ -1,6 +1,106 @@
543 <?xml version="1.0" encoding="utf-8"?>
544 <openerp>
545 <data>
546+
547+ <!-- Address views -->
548+
549+ <record id="view_partner_address_tree" model="ir.ui.view">
550+ <field name="name">res.partner.address.tree</field>
551+ <field name="model">res.partner.address</field>
552+ <field name="inherit_id" ref="base.view_partner_address_tree"/>
553+ <field name="type">tree</field>
554+ <field name="context">{"search_default_state": "current"}</field>
555+ <field name="arch" type="xml">
556+ <tree string="Partner Addresses" position="replace">
557+ <tree string="Addresses" colors="gray:state in ('past')">
558+ <field name="name"/>
559+ <field name="partner_id"/>
560+ <field name="function"/>
561+ <field name="email"/>
562+ <field name="phone"/>
563+ <field name="mobile"/>
564+ <field name="other" />
565+ <field name="country_id"/>
566+ <field name="type"/>
567+ <field name="state" />
568+ </tree>
569+ </tree>
570+ </field>
571+ </record>
572+
573+ <record id="view_res_partner_address_filter" model="ir.ui.view">
574+ <field name="name">res.partner.address.select</field>
575+ <field name="model">res.partner.address</field>
576+ <field name="inherit_id" ref="base.view_res_partner_address_filter"/>
577+ <field name="type">search</field>
578+ <field name="arch" type="xml">
579+ <search string="Search Contact" position="replace" >
580+ <search string="Search Address">
581+ <group>
582+ <field name="partner_id" string="Partner"/>
583+ <field name="name" string="Contact (Lastname(s) Firstname(s))" />
584+ <field name="function"/>
585+ <field name="street" />
586+ <field name="city" />
587+ <field name="country_id" />
588+ <field name="type" />
589+ <field name="state" />
590+ </group>
591+ <newline/>
592+ <group expand="0" string="Group By...">
593+ <filter string="Partner" icon="terp-personal" domain="[]" context="{'group_by' : 'partner_id'}" />
594+ <filter string="Function" icon="terp-go-home" domain="[]" context="{'group_by' : 'function'}" />
595+ <filter string="Type" icon="terp-stock_symbol-selection" domain="[]" context="{'group_by' : 'type'}" />
596+ <filter string="State" icon="terp-stock_symbol-selection" domain="[]" context="{'group_by' : 'state'}" />
597+ </group>
598+ </search>
599+ </search>
600+ </field>
601+ </record>
602+
603+ <!-- Address Tree view for Contact -->
604+ <record id="view_partner_address_tree_contact" model="ir.ui.view">
605+ <field name="name">res.partner.address.tree.contact</field>
606+ <field name="model">res.partner.address</field>
607+ <field name="type">tree</field>
608+ <field eval="17" name="priority"/>
609+ <field name="arch" type="xml">
610+ <tree string="Functions and Addresses" colors="gray:state in ('past')">
611+ <field name="partner_id"/>
612+ <field name="location_id"/>
613+ <field name="function"/>
614+ <field name="email" widget="email"/>
615+ <field name="phone"/>
616+ <field name="other" />
617+ <field name="fax"/>
618+ <field name="type"/>
619+ <field name="state" />
620+ <field name="sequence_contact" string="Seq."/>
621+ </tree>
622+ </field>
623+ </record>
624+
625+ <!-- Adress Tree view for Partner -->
626+ <record id="view_partner_address_tree_partner" model="ir.ui.view">
627+ <field name="name">res.partner.address.tree.partner</field>
628+ <field name="model">res.partner.address</field>
629+ <field name="type">tree</field>
630+ <field eval="18" name="priority"/>
631+ <field name="arch" type="xml">
632+ <tree string="Functions and Addresses" colors="gray:state in ('past')" >
633+ <field name="name"/>
634+ <field name="location_id"/>
635+ <field name="function"/>
636+ <field name="email" widget="email"/>
637+ <field name="phone"/>
638+ <field name="mobile"/>
639+ <field name="other" />
640+ <field name="fax"/>
641+ <field name="type"/>
642+ <field name="state" />
643+ </tree>
644+ </field>
645+ </record>
646
647 <!-- Views for Contacts Tree View -->
648
649@@ -9,8 +109,8 @@
650 <field name="model">res.partner.contact</field>
651 <field name="type">tree</field>
652 <field name="arch" type="xml">
653- <tree string="Partner Contact">
654- <field name="name"/>
655+ <tree string="Contact">
656+ <field name="last_name"/>
657 <field name="first_name"/>
658 <field name="mobile"/>
659 <field name="email"/>
660@@ -28,7 +128,7 @@
661 <field name="model">res.partner.contact</field>
662 <field name="type">form</field>
663 <field name="arch" type="xml">
664- <form string="Partner Contact">
665+ <form string="Contact">
666 <group colspan="4" col="6">
667 <field name="last_name" select="1"/>
668 <field name="first_name" select="1"/>
669@@ -48,22 +148,7 @@
670 <field name="photo" widget='image' nolabel="1"/>
671 </group>
672 </group>
673- <field name="job_ids" colspan="4" nolabel="1" mode="tree,form">
674- <form string="Functions and Addresses">
675- <field name="partner_id" />
676- <field name="location_id" domain="[('partner_id', '=', partner_id)]"/>
677- <field name="function" />
678- <separator string="Professional Info" colspan="4"/>
679- <field name="phone"/>
680- <field name="fax"/>
681- <field name="email" widget="email"/>
682- </form>
683- <tree string="Functions and Addresses">
684- <field name="location_id"/>
685- <field name="function"/>
686- <field name="phone"/>
687- <field name="email"/>
688- </tree>
689+ <field name="job_ids" colspan="4" nolabel="1" mode="tree,form" context="{'tree_view_ref' : 'base_contact.view_partner_address_tree_contact', 'default_contact_id': active_id, 'default_type': 'contact'}">
690 </field>
691 </page>
692 <page string="Extra Information">
693@@ -90,15 +175,15 @@
694 <field name="model">res.partner.contact</field>
695 <field name="type">search</field>
696 <field name="arch" type="xml">
697- <search string="Partner Contact">
698- <field name="name" string="First/Lastname"
699- filter_domain="['|', ('first_name','ilike', self), ('last_name', 'ilike', self)]"/>
700+ <search string="Contact">
701+ <field name="last_name" />
702+ <field name="first_name" />
703 <field name="partner_id" string="Partner"/>
704 </search>
705 </field>
706 </record>
707
708-<!-- Views for Contacts Action -->
709+ <!-- Views for Contacts Action and menu -->
710
711 <record model="ir.actions.act_window" id="action_partner_contact_form">
712 <field name="name">Contacts</field>
713@@ -110,11 +195,27 @@
714 </record>
715 <menuitem name="Contacts" id="menu_partner_contact_form" action="action_partner_contact_form" parent = "base.menu_address_book" sequence="2"/>
716
717- <!-- Rename menuitem for partner addresses -->
718- <record model="ir.ui.menu" id="base.menu_partner_address_form">
719- <field name="name">Addresses</field>
720+ <!-- Views for Address Action and menu -->
721+ <record id="action_partner_address_form" model="ir.actions.act_window">
722+ <field name="name">Addresses</field>
723+ <field name="type">ir.actions.act_window</field>
724+ <field name="res_model">res.partner.address</field>
725+ <field name="view_type">form</field>
726+ <field name="view_mode">tree,form,kanban</field>
727+ <field name="context"> {"search_default_state": "current"}</field>
728+ <field name="search_view_id" ref="view_res_partner_address_filter"/>
729+ <field name="help">Address helps you manage your contacts jobs wether they are private, main/switchboard or contacts.</field>
730 </record>
731-
732+
733+ <!-- Replace menuitem with new action
734+ delete action window as menu deletion did not work -->
735+ <delete model="ir.actions.act_window" id="base.action_partner_address_form" />
736+
737+ <menuitem action="action_partner_address_form" id="menu_partner_address_form"
738+ name="Addresses"
739+ parent="base.menu_address_book" sequence="30"/>
740+
741+
742 <!--
743 Contacts for Suppliers
744 -->
745@@ -126,8 +227,13 @@
746 parent="base.menu_procurement_management_supplier" action="base.action_partner_supplier_form" sequence="1"/>
747 <menuitem name="Contacts" id="menu_purchases_partner_contact_form" action="action_partner_contact_form"
748 parent = "base.menu_procurement_management_supplier" sequence="2"/>
749+
750+ <menuitem action="action_partner_address_form" id="menu_purchase_partner_address_form"
751+ name="Addresses"
752+ parent="base.menu_procurement_management_supplier" sequence="30"/>
753+
754
755- <!-- Views for Partners Form View -->
756+ <!-- Views for Partner -->
757
758 <record model="ir.ui.view" id="view_partner_form_inherit">
759 <field name="name">Partner form inherited</field>
760@@ -135,17 +241,50 @@
761 <field name="inherit_id" ref="base.view_partner_form"/>
762 <field name="type">form</field>
763 <field name="arch" type="xml">
764- <separator string="Postal Address" position="after">
765- <field name="location_id" on_change="onchange_location_id(location_id)" domain="[('partner_id', '=', parent.id)]"/>
766- </separator>
767- <xpath expr="//field[@string='Contact Name']" position="replace">
768- <field name="contact_id"/>
769- </xpath>
770- <field name="title" position="replace"/>
771+ <field name="address" position="replace" >
772+ <field colspan="4" mode="tree,form" name="address" nolabel="1" select="1" height="260" context="{'tree_view_ref' : 'base_contact.view_partner_address_tree_partner', 'default_partner_id': active_id, 'default_type': 'contact'}">
773+ </field>
774+ </field>
775+
776 </field>
777 </record>
778-
779- <!-- Views for Addresses -->
780+
781+ <record id="view_partner_tree" model="ir.ui.view">
782+ <field name="name">res.partner.tree</field>
783+ <field name="model">res.partner</field>
784+ <field name="inherit_id" ref="base.view_partner_tree"/>
785+ <field name="type">tree</field>
786+ <field name="arch" type="xml">
787+
788+ <field name="phone" position="after" >
789+ <field name="fax"/>
790+ </field>
791+
792+ <field name="city" position="before" >
793+ <field name="street"/>
794+ </field>
795+
796+ </field>
797+ </record>
798+
799+ <record id="view_res_partner_filter" model="ir.ui.view">
800+ <field name="name">res.partner.select</field>
801+ <field name="model">res.partner</field>
802+ <field name="inherit_id" ref="base.view_res_partner_filter"/>
803+ <field name="type">search</field>
804+ <field name="arch" type="xml">
805+
806+
807+ <field name="country" position="before">
808+ <field name="street" select="1"/>
809+ <field name="city" select="1"/>
810+ </field>
811+
812+ </field>
813+ </record>
814+
815+
816+ <!-- Views for Location -->
817
818 <record model="ir.ui.view" id="view_partner_location_form">
819 <field name="name">res.partner.location.form</field>
820@@ -153,12 +292,24 @@
821 <field name="type">form</field>
822 <field name="arch" type="xml">
823 <form string="Locations">
824+ <field name="partner_id" readonly="1" />
825 <field name="street" colspan="4"/>
826 <field name="street2" colspan="4"/>
827 <field name="zip"/>
828 <field name="city"/>
829 <field name="country_id" />
830 <field name="state_id"/>
831+ <newline />
832+ <separator string="Addresses" colspan="4" />
833+ <field name="job_ids" nolabel="1" colspan="4" readonly="1" mode="tree">
834+ <tree string="Address">
835+ <field name = "contact_id" />
836+ <field name = "partner_id" />
837+ <field name = "function" />
838+ <field name = "type" />
839+ <field name = "state" />
840+ </tree>
841+ </field>
842 </form>
843 </field>
844 </record>
845@@ -170,6 +321,9 @@
846 <field name="type">tree</field>
847 <field name="arch" type="xml">
848 <tree string="Locations">
849+ <field name="partner_id" string="Partner" />
850+ <field name="contact_id" />
851+ <field name="street" />
852 <field name="city"/>
853 <field name="country_id" />
854 <field name="state_id"/>
855@@ -177,19 +331,77 @@
856 </field>
857 </record>
858
859+ <record model="ir.ui.view" id="view_partner_location_search">
860+ <field name="name">res.partner.location.search</field>
861+ <field name="model">res.partner.location</field>
862+ <field name="type">search</field>
863+ <field name="arch" type="xml">
864+ <search string="Partner Location">
865+ <field name="partner_id" string="Partner"/>
866+ <field name="job_ids" />
867+ <field name="street" />
868+ <field name="city" />
869+ <field name="country_id" />
870+ </search>
871+ </field>
872+ </record>
873+
874+
875+ <record model="ir.actions.act_window" id="action_partner_location_form">
876+ <field name="name">Locations</field>
877+ <field name="res_model">res.partner.location</field>
878+ <field name="view_type">form</field>
879+ <field name="view_mode">tree,form</field>
880+ <field name="view_id" ref="view_partner_location_tree"/>
881+ <field name="search_view_id" ref="view_partner_location_search"/>
882+ </record>
883+ <!-- sequence=30 is because 11 is not enough for beeing below addresses to be checked further -->
884+ <menuitem name="Locations" id="menu_partner_location_form" action="action_partner_location_form"
885+ parent = "base.menu_address_book" groups="base.group_extended" sequence="40"/>
886+ <menuitem name="Locations" id="menu_purchase_partner_location_form" action="action_partner_location_form"
887+ parent = "base.menu_procurement_management_supplier" groups="base.group_extended" sequence="40"/>
888+
889+
890+ <!-- Update default address view -->
891+
892 <record model="ir.ui.view" id="view_partner_address_form_inherited0">
893 <field name='name'>res.partner.address.form.inherited0</field>
894 <field name='model'>res.partner.address</field>
895 <field name="inherit_id" ref="base.view_partner_address_form1"/>
896 <field name='type'>form</field>
897 <field name='arch' type='xml'>
898+
899+ <field name="partner_id" position="replace">
900+ <field name="partner_id" colspan="2" on_change="onchange_partner_id(partner_id)" attrs="{'required':[('contact_id','=', False)]}"/>
901+ </field>
902+
903 <field name="name" position="replace">
904- <field name="contact_id"/>
905- </field>
906+ <field name="contact_id" on_change="onchange_contact_id(contact_id)" attrs="{'required':[('partner_id','=', False)]}"/>
907+ <!-- <field name="name" string="use this field for initial name only" /> would require adaption of create-->
908+ </field>
909+
910+ <field name="type" position="replace">
911+ <field name="type" required="1"/>
912+ </field>
913+ <field name="mobile" position="after">
914+ <field name="other" />
915+
916+ </field>
917 <separator string="Postal Address" position="after">
918- <field name="location_id" on_change="onchange_location_id(location_id)"/>
919+ <field name="location_id" required="1"
920+ on_change="onchange_location_id(location_id)"
921+ domain="[('partner_id', '=', partner_id)]"
922+ context="{'default_partner_id': partner_id}"/>
923 </separator>
924 <field name="title" position="replace"/>
925+ <field name="function" position="after">
926+ <separator string="Status" colspan="6"/>
927+ <field name="state" />
928+ <field name="date_start" />
929+ <field name="date_stop" />
930+ <separator string="Sequence" colspan="6" col="2"/>
931+ <field name="sequence_contact" string="Contact Seq."/>
932+ </field>
933 </field>
934 </record>
935
936
937=== modified file 'base_contact/security/ir.model.access.csv'
938--- base_contact/security/ir.model.access.csv 2012-01-31 13:36:57 +0000
939+++ base_contact/security/ir.model.access.csv 2015-01-16 20:04:03 +0000
940@@ -1,7 +1,8 @@
941 "id","name","model_id:id","group_id:id","perm_read","perm_write","perm_create","perm_unlink"
942 "access_res_partner_contact","res.partner.contact","model_res_partner_contact","base.group_partner_manager",1,1,1,1
943 "access_res_partner_contact_all","res.partner.contact all","model_res_partner_contact","base.group_user",1,0,0,0
944-"access_res_partner_location","res.partner.location","model_res_partner_location","base.group_user",1,0,0,0
945+"access_res_partner_location_manager","res.partner.location","model_res_partner_location","base.group_partner_manager",1,1,1,1
946+"access_res_partner_location_all","res.partner.location","model_res_partner_location","base.group_user",1,0,0,0
947 "access_res_partner_location_sale_salesman","res.partner.location","model_res_partner_location","base.group_sale_salesman",1,1,1,0
948 "access_res_partner_address_sale_salesman","res.partner.address.user","base.model_res_partner_address","base.group_sale_salesman",1,1,1,0
949 "access_group_sale_salesman","res.partner.contact.sale.salesman","model_res_partner_contact","base.group_sale_salesman",1,1,1,0
950
951=== modified file 'base_report_designer/plugin/openerp_report_designer/bin/OOo_run.sh' (properties changed: +x to -x)
952=== modified file 'document/odt2txt.py' (properties changed: +x to -x)
953=== modified file 'document/test_cindex.py' (properties changed: +x to -x)
954=== modified file 'document_ftp/ftpserver/ftpserver.py' (properties changed: +x to -x)
955=== modified file 'document_webdav/test_davclient.py' (properties changed: +x to -x)
956=== modified file 'email_template/html2text.py' (properties changed: +x to -x)
957=== modified file 'l10n_ch/report/ocrbb.ttf' (properties changed: +x to -x)
958=== modified file 'l10n_ch/test/test.v11' (properties changed: +x to -x)
959=== modified file 'l10n_ch/test/test_part_1.v11' (properties changed: +x to -x)
960=== modified file 'l10n_ch/test/test_part_2.v11' (properties changed: +x to -x)
961=== modified file 'mail/static/scripts/openerp_mailgate.py' (properties changed: +x to -x)
962=== modified file 'plugin_thunderbird/static/thunderbird_plugin/install.sh' (properties changed: +x to -x)
963=== modified file 'purchase/purchase.py' (properties changed: +x to -x)
964=== modified file 'purchase/wizard/purchase_line_invoice.py' (properties changed: +x to -x)
965=== modified file 'purchase_double_validation/test/purchase_double_validation_test.yml' (properties changed: +x to -x)
966=== modified file 'purchase_requisition/test/purchase_requisition_demo.yml' (properties changed: +x to -x)
967=== modified file 'wiki/static/src/lib/wiky/Readme.md' (properties changed: +x to -x)
968=== modified file 'wiki/static/src/lib/wiky/autogit.sh' (properties changed: +x to -x)
969=== modified file 'wiki/static/src/lib/wiky/index.html' (properties changed: +x to -x)
970=== modified file 'wiki/static/src/lib/wiky/input_complete' (properties changed: +x to -x)
971=== modified file 'wiki/static/src/lib/wiky/jquery-1.4.2.min.js' (properties changed: +x to -x)
972=== modified file 'wiki/static/src/lib/wiky/wiky.css' (properties changed: +x to -x)
973=== modified file 'wiki/static/src/lib/wiky/wiky.js' (properties changed: +x to -x)

Subscribers

People subscribed via source and target branches