Skip to content

Commit

Permalink
[ADD] Add mail_thread_child module
Browse files Browse the repository at this point in the history
  • Loading branch information
adrienpeiffer committed Oct 1, 2015
1 parent 271e515 commit d07af77
Show file tree
Hide file tree
Showing 5 changed files with 344 additions and 0 deletions.
67 changes: 67 additions & 0 deletions mail_thread_child/README.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
.. image:: https://img.shields.io/badge/licence-AGPL--3-blue.svg
:target: http://www.gnu.org/licenses/agpl-3.0-standalone.html
:alt: License

=================
Mail Thread Child
=================

This module was written to extend the functionality of modification's tracking
to support to track field on other object like one2many on your current object.

.. figure:: static/description/mail_thread_child.png
:alt: Simple exemple with partner bank account

Installation
============

.. image:: https://odoo-community.org/website/image/ir.attachment/5784_f2813bd/datas
:alt: Try me on Runbot
:target: https://runbot.odoo-community.org/runbot/149/8.0

Usage
=====

To use this module, you need to override a models like this exemple:

class ResPartnerBank(models.Model):
_name = "res.partner.bank"
_inherit = ['res.partner.bank', 'mail.thread.child']
_tracked_parent = ['partner_id']
_track_name = 'acc_number'

acc_number = fields.Char(track_visibility='always')

For further information, please visit:

* https://www.odoo.com/forum/help-1

Bug Tracker
===========

Bugs are tracked on `GitHub Issues <https://github.com/OCA/server-tools/issues>`_.
In case of trouble, please check there if your issue has already been reported.
If you spotted it first, help us smashing it by providing a detailed and welcomed feedback
`here <https://github.com/OCA/server-tools/issues/new?body=module:%20mail_thread_child%0Aversion:%208.0%0A%0A**Steps%20to%20reproduce**%0A-%20...%0A%0A**Current%20behavior**%0A%0A**Expected%20behavior**>`_.

Credits
=======

Contributors
------------

* Stéphane Bidoul <[email protected]>
* Adrien Peiffer <[email protected]>

Maintainer
----------

.. image:: http://odoo-community.org/logo.png
:alt: Odoo Community Association
:target: http://odoo-community.org

This module is maintained by the OCA.

OCA, or the Odoo Community Association, is a nonprofit organization whose mission is to support the collaborative development of Odoo features and promote its widespread use.

To contribute to this module, please visit http://odoo-community.org.
2 changes: 2 additions & 0 deletions mail_thread_child/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
# -*- coding: utf-8 -*-
from . import models
40 changes: 40 additions & 0 deletions mail_thread_child/__openerp__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
# -*- coding: utf-8 -*-
##############################################################################
#
# This file is part of mail_thread_child,
# an Odoo module.
#
# Copyright (c) 2015 ACSONE SA/NV (<http://acsone.eu>)
#
# mail_thread_child is free software:
# you can redistribute it and/or modify it under the terms of the GNU
# Affero General Public License as published by the Free Software
# Foundation,either version 3 of the License, or (at your option) any
# later version.
#
# mail_thread_child is distributed
# in the hope that it will be useful, but WITHOUT ANY WARRANTY; without
# even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
# PURPOSE. See the GNU Affero General Public License for more details.
#
# You should have received a copy of the GNU Affero General Public License
# along with mail_thread_child.
# If not, see <http://www.gnu.org/licenses/>.
#
##############################################################################
{
'name': "Mail Thread Child",

'summary': """
Track on child object""",
'author': "ACSONE SA/NV",
'website': "http://acsone.eu",
'category': 'Tools',
'version': '8.0.1.0.0',
'license': 'AGPL-3',

'depends': [
'mail',
],
'data': [],
}
2 changes: 2 additions & 0 deletions mail_thread_child/models/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
# -*- coding: utf-8 -*-
from . import mail_thread_child
233 changes: 233 additions & 0 deletions mail_thread_child/models/mail_thread_child.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,233 @@
# -*- coding: utf-8 -*-
##############################################################################
#
# This file is part of mail_thread_child,
# an Odoo module.
#
# Copyright (c) 2015 ACSONE SA/NV (<http://acsone.eu>)
#
# mail_thread_child is free software:
# you can redistribute it and/or modify it under the terms of the GNU
# Affero General Public License as published by the Free Software
# Foundation,either version 3 of the License, or (at your option) any
# later version.
#
# mail_thread_child is distributed
# in the hope that it will be useful, but WITHOUT ANY WARRANTY; without
# even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
# PURPOSE. See the GNU Affero General Public License for more details.
#
# You should have received a copy of the GNU Affero General Public License
# along with mail_thread_child.
# If not, see <http://www.gnu.org/licenses/>.
#
##############################################################################

from openerp import models, api, _
from openerp.osv.orm import browse_record, browse_null
import logging

_logger = logging.getLogger(__name__)

MODE = [
('create', 'Created'),
('write', 'Changed'),
('unlink', 'Removed'),
]

DICT_MODE = {x[0]: x[1] for x in MODE}


class mail_thread_child(models.Model):
_name = 'mail.thread.child'
_tracked_parent = []
_track = {}
_track_name = ''

@api.model
@api.returns('self', lambda value: value.id)
def create(self, values):
if self.env.context.get('tracking_disable'):
return super(mail_thread_child, self).create(values)
thread = super(mail_thread_child, self).create(values )
# track values
track_ctx = dict(self.env.context)
if 'lang' not in track_ctx:
track_ctx['lang'] = self.env['res.users']\
.browse([self._uid])[0].lang
if not self.env.context.get('mail_notrack'):
tracked_fields = self.with_context(track_ctx)\
._get_tracked_fields(values.keys())
if tracked_fields:
initial_values = {thread.id: dict.fromkeys(tracked_fields,
False)}
thread.with_context(track_ctx)\
.message_track(tracked_fields, initial_values,
mode='create')
return thread

@api.multi
def write(self, values):
if self.env.context.get('tracking_disable'):
return super(mail_thread_child, self).write(values)
# Track initial values of tracked fields
track_ctx = dict(self.env.context)
if 'lang' not in track_ctx:
track_ctx['lang'] = self.env['res.users']\
.browse(self._uid).lang

tracked_fields = None
if not self.env.context.get('mail_notrack'):
tracked_fields = self._get_tracked_fields(values.keys(),
mode='write')

if tracked_fields:
initial_values = dict((record.id, dict((key, getattr(record, key))
for key in tracked_fields))
for record in self)

# Perform write, update followers
result = super(mail_thread_child, self).write(values)
# Perform the tracking
if tracked_fields:
self.with_context(track_ctx)\
.message_track(tracked_fields, initial_values, mode='write')

return result

@api.multi
def unlink(self):
self.message_post_parent(body="", mode='unlink')
res = super(mail_thread_child, self).unlink()
return res

@api.model
def _get_tracked_fields(self, updated_fields, mode="create"):
tracked_fields = []
for name, field in self._fields.items():
visibility = getattr(field, 'track_visibility', False)
if visibility == 'always':
tracked_fields.append(name)
elif mode == 'create' and visibility == 'oncreate':
tracked_fields.append(name)
elif mode == 'write' and visibility == 'onchange' and \
name in updated_fields:
tracked_fields.append(name)
elif name in self._track:
tracked_fields.append(name)

if tracked_fields:
return self.fields_get(tracked_fields)
return {}

@api.multi
def message_track(self, tracked_fields, initial_values, mode="create"):

def convert_for_display(value, col_info):
if not value and col_info['type'] == 'boolean':
return 'False'
if not value:
return ''
if col_info['type'] == 'many2one':
return value.name_get()[0][1]
if col_info['type'] == 'selection':
return dict(col_info['selection'])[value]
return value

def format_message(message_description, tracked_values):
message = ''
if message_description:
message = '<span>%s</span>' % message_description
for name, change in tracked_values.items():
message += '<div> &nbsp; &nbsp; &bull; <b>%s</b>: ' % \
change.get('col_info')
if change.get('old_value'):
message += '%s &rarr; ' % change.get('old_value')
message += '%s</div>' % change.get('new_value')
return message

if not tracked_fields:
return True

for browse_record in self:
initial = initial_values[browse_record.id]
changes = set()
tracked_values = {}

# generate tracked_values data structure:
# {'col_name': {col_info, new_value, old_value}}
for col_name, col_info in tracked_fields.items():
field = self._fields[col_name]
initial_value = initial[col_name]
record_value = getattr(browse_record, col_name)

if record_value == initial_value and \
getattr(field, 'track_visibility', None) == 'always':
tracked_values[col_name] = dict(
col_info=col_info['string'],
new_value=convert_for_display(record_value, col_info),
)
elif record_value != initial_value and\
(record_value or initial_value):
# because browse null != False
if getattr(field, 'track_visibility', None) in \
['always', 'onchange']:
tracked_values[col_name] = dict(
col_info=col_info['string'],
old_value=convert_for_display(initial_value,
col_info),
new_value=convert_for_display(record_value,
col_info),
)
if col_name in tracked_fields:
changes.add(col_name)
if not changes:
continue

# find subtypes and post messages or log if no subtype found
subtypes = []
# By passing this key, that allows to let the subtype empty and so
# don't sent email because partners_to_notify from mail_message.
# notify will be empty
if not self.env.context.get('mail_track_log_only'):
for field, track_info in self._track.items():
if field not in changes:
continue
for subtype, method in track_info.items():
if method(self, browse_record):
subtypes.append(subtype)

posted = False
for subtype in subtypes:
subtype_rec = self.env['ir.model.data']\
.xmlid_to_object(subtype)
if not (subtype_rec and subtype_rec.exists()):
_logger.debug('subtype %s not found' % subtype)
continue
message = format_message(subtype_rec.description if
subtype_rec.description else
subtype_rec.name, tracked_values)
self.message_post_parent(browse_record, message, mode)
if not posted:
message = format_message('', tracked_values)
browse_record.message_post_parent(body=message, mode=mode)
return True

@api.one
def message_post_parent(self, body="", mode="create"):
ctx = self.env.context.copy()
for parent in self._tracked_parent:
parent_field = getattr(self, parent)
if isinstance(parent_field, browse_record) and \
not isinstance(parent_field, browse_null):
parent_model = str(parent_field._model)
params = {'model': parent_model,
'res_id': parent_field.id
}
parent_message = \
_('<div>&bull; <b>%s</b> %s : %s</div>') % \
(self._description, DICT_MODE[mode],
getattr(self, self._track_name))
message = parent_message + body
ctx.update({'params': params})
parent_field.message_post(body=message)

0 comments on commit d07af77

Please sign in to comment.