Merge remote-tracking branch 'upstream/mail-addons-9.0-res_partner_mails_count' into mail-addons-9.0-res_partner_mails_count
Conflicts: mail_sent/README.rst mail_sent/__init__.py mail_sent/__openerp__.pypull/6/head
-
4mail_check_immediately/README.rst
-
2mail_check_immediately/__init__.py
-
18mail_check_immediately/__openerp__.py
-
9mail_check_immediately/doc/changelog.rst
-
67mail_check_immediately/models.py
-
BINmail_check_immediately/static/description/icon.png
-
47mail_check_immediately/static/description/index.html
-
BINmail_check_immediately/static/description/issue.png
-
BINmail_check_immediately/static/description/screenshot.png
-
55mail_check_immediately/static/src/js/main.js
-
25mail_check_immediately/static/src/xml/main.xml
-
9mail_check_immediately/views.xml
-
4mail_delete_odoo_footer/README.rst
-
0mail_delete_odoo_footer/__init__.py
-
12mail_delete_odoo_footer/__openerp__.py
-
BINmail_delete_odoo_footer/static/description/icon.png
-
41mail_fix_553/README.rst
-
1mail_fix_553/__init__.py
-
11mail_fix_553/__openerp__.py
-
10mail_fix_553/data.xml
-
164mail_fix_553/mail_fix_553.py
-
BINmail_fix_553/static/description/icon.png
-
6mail_fix_empty_body/README.rst
-
1mail_fix_empty_body/__init__.py
-
14mail_fix_empty_body/__openerp__.py
-
10mail_fix_empty_body/models.py
-
BINmail_fix_empty_body/static/description/icon.png
-
65mail_fix_empty_body/static/description/index.html
-
BINmail_fix_empty_body/static/description/receive-false.png
-
BINmail_fix_empty_body/static/description/receive-ok.png
-
BINmail_fix_empty_body/static/description/send.png
-
1mail_fix_header_from/__init__.py
-
16mail_fix_header_from/__openerp__.py
-
4mail_fix_header_from/models.py
-
BINmail_fix_header_from/static/description/icon.png
-
8mail_move_message/README.rst
-
2mail_move_message/__init__.py
-
20mail_move_message/__openerp__.py
-
1mail_move_message/controllers/__init__.py
-
55mail_move_message/controllers/main.py
-
9mail_move_message/data/mail_move_message_data.xml
-
26mail_move_message/doc/changelog.rst
-
173mail_move_message/i18n/mail_move_message.pot
-
160mail_move_message/i18n/sl.po
-
BINmail_move_message/images/inbox.png
-
382mail_move_message/mail_move_message_models.py
-
122mail_move_message/mail_move_message_views.xml
-
BINmail_move_message/static/description/delete-message.png
-
BINmail_move_message/static/description/html-message-viewer.png
-
BINmail_move_message/static/description/html-message-viewer1.png
-
BINmail_move_message/static/description/html-message.png
-
BINmail_move_message/static/description/icon.png
-
BINmail_move_message/static/description/inbox-move.png
-
BINmail_move_message/static/description/inbox.png
-
140mail_move_message/static/description/index.html
-
BINmail_move_message/static/description/record-move-back.png
-
BINmail_move_message/static/description/record1.png
-
BINmail_move_message/static/description/record2.png
-
13mail_move_message/static/src/css/mail_move_message.css
-
91mail_move_message/static/src/js/mail_move_message.js
-
8mail_move_message/static/src/xml/mail_move_message_main.xml
-
1mail_outgoing/__init__.py
-
20mail_outgoing/__openerp__.py
-
22mail_outgoing/mail_outgoing_models.py
-
34mail_outgoing/mail_outgoing_views.xml
-
3mail_outgoing/security/ir.model.access.csv
-
33mail_outgoing/security/mail_outgoing.xml
-
BINmail_outgoing/static/description/icon.png
-
4mail_partner_lang/README.rst
-
1mail_partner_lang/__init__.py
-
12mail_partner_lang/__openerp__.py
-
88mail_partner_lang/models.py
-
BINmail_partner_lang/static/description/icon.png
-
1mail_recovery/__init__.py
-
13mail_recovery/__openerp__.py
-
10mail_recovery/data.xml
-
25mail_recovery/static/src/js/mail_recovery.js
-
5mail_sent/README.rst
-
8mail_sent/__openerp__.py
-
9mail_sent/doc/changelog.rst
-
50mail_sent/i18n/mail_sent.pot
-
58mail_sent/i18n/sl.po
-
28mail_sent/models.py
-
47mail_sent/static/description/index.html
-
BINmail_sent/static/description/menu.png
-
BINmail_sent/static/description/messages.png
-
38mail_sent/views.xml
-
2mail_todo_custom/README.rst
-
0mail_todo_custom/__init__.py
-
13mail_todo_custom/__openerp__.py
-
25mail_todo_custom/mail_todo_custom.xml
-
44mail_todo_custom/static/src/js/mail_todo_custom.js
-
4mail_wall_custom/README.rst
-
1mail_wall_custom/__init__.py
-
25mail_wall_custom/__openerp__.py
-
402mail_wall_custom/data.xml
-
13mail_wall_custom/models.py
-
BINmail_wall_custom/static/description/icon.png
-
20mail_wall_custom/views.xml
-
1mail_wall_menu/__init__.py
@ -0,0 +1,4 @@ |
|||
Check mail immediately |
|||
====================== |
|||
|
|||
Description: https://apps.odoo.com/apps/modules/8.0/mail_check_immediately/ |
@ -0,0 +1,2 @@ |
|||
# -*- coding: utf-8 -*- |
|||
import models |
@ -0,0 +1,18 @@ |
|||
{ |
|||
'name' : 'Check mail immediately', |
|||
'version' : '1.0.1', |
|||
'author' : 'IT-Projects LLC, Ivan Yelizariev', |
|||
'license': 'LGPL-3', |
|||
'category' : 'Social Network', |
|||
'website' : 'https://twitter.com/yelizariev', |
|||
'price': 9.00, |
|||
'currency': 'EUR', |
|||
'depends' : ['base', 'web', 'fetchmail', 'mail'], |
|||
'data': [ |
|||
'views.xml', |
|||
], |
|||
'qweb': [ |
|||
"static/src/xml/main.xml", |
|||
], |
|||
'installable': False |
|||
} |
@ -0,0 +1,9 @@ |
|||
.. _changelog: |
|||
|
|||
Changelog |
|||
========= |
|||
|
|||
`1.0.1` |
|||
------- |
|||
|
|||
- FIX: incorrectly displayed last updated time when multiple threads (--workers) |
@ -0,0 +1,67 @@ |
|||
# -*- coding: utf-8 -*- |
|||
import datetime |
|||
|
|||
from openerp.tools.translate import _ |
|||
from openerp import tools |
|||
|
|||
from openerp import exceptions |
|||
from openerp import models, fields, api |
|||
|
|||
|
|||
class FetchMailServer(models.Model): |
|||
_inherit = 'fetchmail.server' |
|||
_name = 'fetchmail.server' |
|||
|
|||
_last_updated = None |
|||
|
|||
run_time = fields.Datetime(string="Launch time") |
|||
|
|||
def _run_time(self): |
|||
if not self._last_updated: |
|||
self._last_updated = tools.datetime.now() |
|||
|
|||
src_tstamp_str = self._last_updated.strftime(tools.misc.DEFAULT_SERVER_DATETIME_FORMAT) |
|||
src_format = tools.misc.DEFAULT_SERVER_DATETIME_FORMAT |
|||
dst_format = tools.misc.DEFAULT_SERVER_DATETIME_FORMAT |
|||
dst_tz_name = self._context.get('tz') or self.env.user.tz |
|||
_now = tools.misc.server_to_local_timestamp(src_tstamp_str, src_format, dst_format, dst_tz_name) |
|||
|
|||
return _now |
|||
|
|||
@api.model |
|||
def _fetch_mails(self): |
|||
|
|||
if self._context.get('run_fetchmail_manually'): |
|||
# if interval less than 5 seconds |
|||
if self._last_updated and (datetime.datetime.now() - self._last_updated) < datetime.timedelta(0, 5): |
|||
raise exceptions.Warning(_('Error'), _('Task can be started no earlier than 5 seconds.')) |
|||
|
|||
super(FetchMailServer, self)._fetch_mails() |
|||
|
|||
res = self.env['fetchmail.server'].sudo().with_context(tz=self.env.user.tz).search([('state', '=', 'done')]) |
|||
if res: |
|||
res[0].run_time = self._run_time() |
|||
|
|||
|
|||
class FetchMailImmediately(models.AbstractModel): |
|||
|
|||
_name = 'fetch_mail.imm' |
|||
|
|||
@api.model |
|||
def get_last_update_time(self): |
|||
res = self.env['fetchmail.server'].sudo().with_context(tz=self.env.user.tz).search([('state', '=', 'done')]) |
|||
array = [r.run_time for r in res] |
|||
if array: |
|||
return array[0] |
|||
else: |
|||
return None |
|||
|
|||
@api.model |
|||
def run_fetchmail_manually(self): |
|||
|
|||
fetchmail_task = self.env.ref('fetchmail.ir_cron_mail_gateway_action') |
|||
fetchmail_task_id = fetchmail_task.id |
|||
fetchmail_model = self.env['fetchmail.server'].sudo() |
|||
|
|||
fetchmail_task._try_lock() |
|||
fetchmail_model.with_context(run_fetchmail_manually=True)._fetch_mails() |
After Width: 100 | Height: 100 | Size: 2.1 KiB |
@ -0,0 +1,47 @@ |
|||
<section class="oe_container"> |
|||
<div class="oe_row oe_spaced"> |
|||
<div class="oe_span12"> |
|||
<h2 class="oe_slogan">Check mail immediately</h2> |
|||
<h3 class="oe_slogan">Keep your inbox up to date</h3> |
|||
</div> |
|||
|
|||
<div class="oe_span12"> |
|||
<div class="oe_demo oe_picture oe_screenshot"> |
|||
<img src="screenshot.png?"/> |
|||
</div> |
|||
</div> |
|||
</div> |
|||
</section> |
|||
|
|||
<section class="oe_container oe_dark"> |
|||
<div class="oe_row oe_spaced"> |
|||
<div class="oe_span12"> |
|||
<h2>Protect your business</h2> |
|||
</div> |
|||
<div class="oe_span6"> |
|||
<p class="oe_mt32"> |
|||
<a href="https://github.com/odoo/odoo/issues/7464">Sometimes</a> odoo mail fetching system doesn't work for really long time. It could be a real problem, if you will not notice it on time. Until this issue is fixed, you can restart odoo every time when you see that last fetching time is more than 5 minutes. |
|||
</p> |
|||
</div> |
|||
|
|||
<div class="oe_span6"> |
|||
<div class="oe_picture"> |
|||
<img src="issue.png?3"/> |
|||
</div> |
|||
</div> |
|||
</div> |
|||
</section> |
|||
|
|||
<section class="oe_container"> |
|||
<div class="oe_row oe_spaced"> |
|||
<div class="oe_span12"> |
|||
<h2>Need our service?</h2> |
|||
<p class="oe_mt32">Contact us by <a href="mailto:it@it-projects.info">email</a> or fill out <a href="https://www.it-projects.info/page/website.contactus " target="_blank">request form</a></p> |
|||
<ul> |
|||
<li><a href="mailto:it@it-projects.info">it@it-projects.info <i class="fa fa-envelope-o"></i></a></li> |
|||
<li><a href="https://www.it-projects.info/page/website.contactus " target="_blank"> |
|||
https://www.it-projects.info/page/website.contactus <i class="fa fa-list-alt"></i></a></li> |
|||
</ul> |
|||
</div> |
|||
</div> |
|||
</section> |
After Width: 217 | Height: 140 | Size: 8.5 KiB |
After Width: 840 | Height: 447 | Size: 88 KiB |
@ -0,0 +1,55 @@ |
|||
openerp.mail_check_immediately = function(instance, local) { |
|||
|
|||
instance.mail.Wall.include({ |
|||
|
|||
init: function(){ |
|||
this._super.apply(this, arguments); |
|||
|
|||
var _this = this; |
|||
|
|||
this.imm_model = new instance.web.Model('fetch_mail.imm'); |
|||
this.events['click a.oe_fetch_new_mails'] = function(){ |
|||
_this.run_fetchmail_manually(); |
|||
} |
|||
}, |
|||
|
|||
start: function() { |
|||
var _this = this; |
|||
|
|||
|
|||
this._super(); |
|||
|
|||
this.get_last_fetched_time(); |
|||
|
|||
this.get_time_loop = setInterval(function(){ |
|||
_this.get_last_fetched_time() |
|||
}, 30000); |
|||
|
|||
}, |
|||
|
|||
run_fetchmail_manually: function(){ |
|||
var _this = this; |
|||
|
|||
this.imm_model.call('run_fetchmail_manually', {context: new instance.web.CompoundContext()}).then(function(){ |
|||
_this.get_last_fetched_time() |
|||
}) |
|||
}, |
|||
|
|||
get_last_fetched_time: function(){ |
|||
var _this = this; |
|||
this.imm_model.call('get_last_update_time', {context: new instance.web.CompoundContext()}).then(function(res){ |
|||
var value; |
|||
if (res) |
|||
value = $.timeago(res); |
|||
value = value || 'undefined'; |
|||
_this.$el.find('span.oe_view_manager_fetch_mail_imm_field').html(value); |
|||
}) |
|||
}, |
|||
|
|||
destroy: function(){ |
|||
clearInterval(this.get_time_loop); |
|||
this._super.apply(this, arguments); |
|||
} |
|||
|
|||
}); |
|||
}; |
@ -0,0 +1,25 @@ |
|||
<?xml version="1.0" encoding="utf-8"?> |
|||
<templates> |
|||
<t t-name="fetch_mail_immediately.header"> |
|||
<tr class="oe_header_row"> |
|||
<td t-att-colspan="colspan or '3'"> |
|||
<div class="oe_view_manager_fetch_mail_imm"> |
|||
<em> |
|||
<span>Mails fetched:</span> |
|||
<a href="#" class="oe_fetch_new_mails" title="Click to fetch mails now"> |
|||
<span class="oe_view_manager_fetch_mail_imm_field"></span> |
|||
</a> |
|||
</em> |
|||
</div> |
|||
</td> |
|||
<td></td> |
|||
</tr> |
|||
</t> |
|||
<t t-extend="mail.wall"> |
|||
<t t-jquery="tr.oe_header_row_top" t-operation="after"> |
|||
<t t-call="fetch_mail_immediately.header"> |
|||
<t t-set="colspan" t-value="2"/> |
|||
</t> |
|||
</t> |
|||
</t> |
|||
</templates> |
@ -0,0 +1,9 @@ |
|||
<openerp> |
|||
<data> |
|||
<template id="assets_backend_inherited_check_mail" name="Check mail immediately bar" inherit_id="web.assets_backend"> |
|||
<xpath expr="." position="inside"> |
|||
<script type="text/javascript" src="/mail_check_immediately/static/src/js/main.js"></script> |
|||
</xpath> |
|||
</template> |
|||
</data> |
|||
</openerp> |
@ -0,0 +1,4 @@ |
|||
Delete Odoo footer in email |
|||
=========================== |
|||
|
|||
Tested on 8.0 ab7b5d7732a7c222a0aea45bd173742acd47242d |
@ -0,0 +1,12 @@ |
|||
{ |
|||
'name' : 'Delete Odoo footer in email (TODO)', |
|||
'version' : '1.0.0', |
|||
'author' : 'IT-Projects LLC, Ivan Yelizariev', |
|||
'license': 'LGPL-3', |
|||
'category' : 'Social Network', |
|||
'website' : 'https://yelizariev.github.io', |
|||
'depends' : [], |
|||
'data':[ |
|||
], |
|||
'installable': False |
|||
} |
After Width: 100 | Height: 100 | Size: 2.1 KiB |
@ -0,0 +1,41 @@ |
|||
Fix mail error 553 |
|||
================== |
|||
|
|||
Module updates 'FROM' field to portal@MYDOMAIN.COM value in order to fix 553 error on a mail service that checks FROM field. |
|||
|
|||
E.g: |
|||
|
|||
* Customer send email from USER@CUSTOMER.com to info@MYDOMAIN.COM |
|||
* odoo accept email and try to send notifcation to related odoo users. E.g to admin@gmail.com. |
|||
* By default odoo prepare notification email with parameters as follows: |
|||
|
|||
* FROM: user@CUSTOMER.com |
|||
* TO: admin@gmail.com |
|||
|
|||
if you mail service provider, e.g. pdd.yandex.ru, doesn't allow emails with a FROM value differ from ...@MYDOMAIN.COM, then you get 553. This is why you need to update FROM value to portal@MYDOMAIN.COM |
|||
|
|||
Configuration |
|||
============= |
|||
|
|||
You can configure default alias at Settings -> System Parameters -> mail.catchall.alias_from |
|||
|
|||
Known issues / Roadmap |
|||
====================== |
|||
|
|||
The module is consist of redefined send function from mail.mail |
|||
model. So it is just copy pasted source code with some |
|||
modification. This function is changed very rarely, but sometime it |
|||
can happens and the module should be updated. You can check commits |
|||
for mail_mail.py here: |
|||
https://github.com/odoo/odoo/commits/8.0/addons/mail/mail_mail.py |
|||
|
|||
Tested on Odoo 8.0 d023c079ed86468436f25da613bf486a4a17d625 |
|||
|
|||
Status |
|||
====== |
|||
|
|||
Related issues at odoo's tracker: |
|||
* https://github.com/odoo/odoo/issues/5864 |
|||
* https://github.com/odoo/odoo/issues/3347 |
|||
|
|||
Fix: https://github.com/odoo-dev/odoo/commit/a4597fe34fcfa8dae28b156410080346bb33af33 |
@ -0,0 +1 @@ |
|||
import mail_fix_553 |
@ -0,0 +1,11 @@ |
|||
{ |
|||
"name" : "Fix mail error 553", |
|||
"version" : "0.3", |
|||
"author" : "IT-Projects LLC, Ivan Yelizariev", |
|||
'license': 'LGPL-3', |
|||
"category" : "Social Network", |
|||
"website" : "https://yelizariev.github.io", |
|||
"depends" : ["base", "mail"], |
|||
"data": ["data.xml"], |
|||
'installable': False |
|||
} |
@ -0,0 +1,10 @@ |
|||
<?xml version="1.0" encoding="utf-8"?> |
|||
<openerp> |
|||
<data noupdate="1"> |
|||
<!-- Catchall Email Alias --> |
|||
<record id="icp_mail_catchall_alias_from" model="ir.config_parameter"> |
|||
<field name="key">mail.catchall.alias_from</field> |
|||
<field name="value">portal</field> |
|||
</record> |
|||
</data> |
|||
</openerp> |
@ -0,0 +1,164 @@ |
|||
# -*- coding: utf-8 -*- |
|||
|
|||
import base64 |
|||
import logging |
|||
from email.utils import formataddr |
|||
from urlparse import urljoin |
|||
|
|||
from openerp import api, tools |
|||
from openerp import SUPERUSER_ID |
|||
from openerp.addons.base.ir.ir_mail_server import MailDeliveryException |
|||
from openerp.osv import fields, osv |
|||
from openerp.tools.safe_eval import safe_eval as eval |
|||
from openerp.tools.translate import _ |
|||
|
|||
_logger = logging.getLogger(__name__) |
|||
|
|||
import re |
|||
|
|||
class mail_mail(osv.Model): |
|||
_inherit = "mail.mail" |
|||
|
|||
def send(self, cr, uid, ids, auto_commit=False, raise_exception=False, context=None): |
|||
# copy-paste from addons/mail/mail_mail.py |
|||
""" Sends the selected emails immediately, ignoring their current |
|||
state (mails that have already been sent should not be passed |
|||
unless they should actually be re-sent). |
|||
Emails successfully delivered are marked as 'sent', and those |
|||
that fail to be deliver are marked as 'exception', and the |
|||
corresponding error mail is output in the server logs. |
|||
|
|||
:param bool auto_commit: whether to force a commit of the mail status |
|||
after sending each mail (meant only for scheduler processing); |
|||
should never be True during normal transactions (default: False) |
|||
:param bool raise_exception: whether to raise an exception if the |
|||
email sending process has failed |
|||
:return: True |
|||
""" |
|||
|
|||
# NEW STUFF |
|||
catchall_alias = self.pool['ir.config_parameter'].get_param(cr, uid, "mail.catchall.alias_from", context=context) |
|||
catchall_domain = self.pool['ir.config_parameter'].get_param(cr, uid, "mail.catchall.domain", context=context) |
|||
|
|||
correct_email_from = '@%s>?\s*$'%catchall_domain |
|||
default_email_from = '%s@%s' % (catchall_alias, catchall_domain) |
|||
|
|||
|
|||
context = dict(context or {}) |
|||
ir_mail_server = self.pool.get('ir.mail_server') |
|||
ir_attachment = self.pool['ir.attachment'] |
|||
for mail in self.browse(cr, SUPERUSER_ID, ids, context=context): |
|||
try: |
|||
# TDE note: remove me when model_id field is present on mail.message - done here to avoid doing it multiple times in the sub method |
|||
if mail.model: |
|||
model_id = self.pool['ir.model'].search(cr, SUPERUSER_ID, [('model', '=', mail.model)], context=context)[0] |
|||
model = self.pool['ir.model'].browse(cr, SUPERUSER_ID, model_id, context=context) |
|||
else: |
|||
model = None |
|||
if model: |
|||
context['model_name'] = model.name |
|||
|
|||
# load attachment binary data with a separate read(), as prefetching all |
|||
# `datas` (binary field) could bloat the browse cache, triggerring |
|||
# soft/hard mem limits with temporary data. |
|||
attachment_ids = [a.id for a in mail.attachment_ids] |
|||
attachments = [(a['datas_fname'], base64.b64decode(a['datas'])) |
|||
for a in ir_attachment.read(cr, SUPERUSER_ID, attachment_ids, |
|||
['datas_fname', 'datas'])] |
|||
|
|||
# specific behavior to customize the send email for notified partners |
|||
email_list = [] |
|||
if mail.email_to: |
|||
email_list.append(self.send_get_email_dict(cr, uid, mail, context=context)) |
|||
for partner in mail.recipient_ids: |
|||
email_list.append(self.send_get_email_dict(cr, uid, mail, partner=partner, context=context)) |
|||
# headers |
|||
headers = {} |
|||
bounce_alias = self.pool['ir.config_parameter'].get_param(cr, uid, "mail.bounce.alias", context=context) |
|||
catchall_domain = self.pool['ir.config_parameter'].get_param(cr, uid, "mail.catchall.domain", context=context) |
|||
if bounce_alias and catchall_domain: |
|||
if mail.model and mail.res_id: |
|||
headers['Return-Path'] = '%s-%d-%s-%d@%s' % (bounce_alias, mail.id, mail.model, mail.res_id, catchall_domain) |
|||
else: |
|||
headers['Return-Path'] = '%s-%d@%s' % (bounce_alias, mail.id, catchall_domain) |
|||
if mail.headers: |
|||
try: |
|||
headers.update(eval(mail.headers)) |
|||
except Exception: |
|||
pass |
|||
|
|||
# Writing on the mail object may fail (e.g. lock on user) which |
|||
# would trigger a rollback *after* actually sending the email. |
|||
# To avoid sending twice the same email, provoke the failure earlier |
|||
mail.write({'state': 'exception'}) |
|||
mail_sent = False |
|||
|
|||
# build an RFC2822 email.message.Message object and send it without queuing |
|||
res = None |
|||
for email in email_list: |
|||
|
|||
# NEW STUFF |
|||
email_from = mail.email_from |
|||
if re.search(correct_email_from, email_from) is None: |
|||
email_from = default_email_from |
|||
|
|||
msg = ir_mail_server.build_email( |
|||
email_from=email_from, # NEW STUFF |
|||
email_to=email.get('email_to'), |
|||
subject=email.get('subject'), |
|||
body=email.get('body'), |
|||
body_alternative=email.get('body_alternative'), |
|||
email_cc=tools.email_split(mail.email_cc), |
|||
reply_to=mail.reply_to, |
|||
attachments=attachments, |
|||
message_id=mail.message_id, |
|||
references=mail.references, |
|||
object_id=mail.res_id and ('%s-%s' % (mail.res_id, mail.model)), |
|||
subtype='html', |
|||
subtype_alternative='plain', |
|||
headers=headers) |
|||
try: |
|||
res = ir_mail_server.send_email(cr, uid, msg, |
|||
mail_server_id=mail.mail_server_id.id, |
|||
context=context) |
|||
except AssertionError as error: |
|||
if error.message == ir_mail_server.NO_VALID_RECIPIENT: |
|||
# No valid recipient found for this particular |
|||
# mail item -> ignore error to avoid blocking |
|||
# delivery to next recipients, if any. If this is |
|||
# the only recipient, the mail will show as failed. |
|||
_logger.warning("Ignoring invalid recipients for mail.mail %s: %s", |
|||
mail.message_id, email.get('email_to')) |
|||
else: |
|||
raise |
|||
if res: |
|||
mail.write({'state': 'sent', 'message_id': res}) |
|||
mail_sent = True |
|||
|
|||
# /!\ can't use mail.state here, as mail.refresh() will cause an error |
|||
# see revid:odo@openerp.com-20120622152536-42b2s28lvdv3odyr in 6.1 |
|||
if mail_sent: |
|||
_logger.info('Mail with ID %r and Message-Id %r successfully sent', mail.id, mail.message_id) |
|||
self._postprocess_sent_message(cr, uid, mail, context=context, mail_sent=mail_sent) |
|||
except MemoryError: |
|||
# prevent catching transient MemoryErrors, bubble up to notify user or abort cron job |
|||
# instead of marking the mail as failed |
|||
_logger.exception('MemoryError while processing mail with ID %r and Msg-Id %r. '\ |
|||
'Consider raising the --limit-memory-hard startup option', |
|||
mail.id, mail.message_id) |
|||
raise |
|||
except Exception as e: |
|||
_logger.exception('failed sending mail.mail %s', mail.id) |
|||
mail.write({'state': 'exception'}) |
|||
self._postprocess_sent_message(cr, uid, mail, context=context, mail_sent=False) |
|||
if raise_exception: |
|||
if isinstance(e, AssertionError): |
|||
# get the args of the original error, wrap into a value and throw a MailDeliveryException |
|||
# that is an except_orm, with name and value as arguments |
|||
value = '. '.join(e.args) |
|||
raise MailDeliveryException(_("Mail Delivery Failed"), value) |
|||
raise |
|||
|
|||
if auto_commit is True: |
|||
cr.commit() |
|||
return True |
After Width: 100 | Height: 100 | Size: 2.1 KiB |
@ -0,0 +1,6 @@ |
|||
Fix "False" in empty email body |
|||
=============================== |
|||
|
|||
Description: https://apps.odoo.com/apps/modules/8.0/mail_fix_empty_body/ |
|||
|
|||
Tested on Odoo 8.0 ab7b5d7732a7c222a0aea45bd173742acd47242d |
@ -0,0 +1 @@ |
|||
import models |
@ -0,0 +1,14 @@ |
|||
{ |
|||
'name' : 'Fix "False" in empty email body', |
|||
'version' : '1.0.0', |
|||
'author' : 'IT-Projects LLC, Ivan Yelizariev', |
|||
'license': 'LGPL-3', |
|||
'category': 'Social Network', |
|||
'website' : 'https://twitter.com/yelizariev', |
|||
'price': 9.00, |
|||
'currency': 'EUR', |
|||
'depends' : ['mail'], |
|||
'data':[ |
|||
], |
|||
'installable': False |
|||
} |
@ -0,0 +1,10 @@ |
|||
from openerp import api, models, fields, SUPERUSER_ID |
|||
|
|||
class mail_compose_message(models.TransientModel): |
|||
_inherit = 'mail.compose.message' |
|||
|
|||
def get_mail_values(self, cr, uid, wizard, res_ids, context=None): |
|||
res = super(mail_compose_message, self).get_mail_values(cr, uid, wizard, res_ids, context) |
|||
for id, d in res.iteritems(): |
|||
d['body'] = d.get('body') or '' |
|||
return res |
After Width: 100 | Height: 100 | Size: 2.1 KiB |
@ -0,0 +1,65 @@ |
|||
<section class="oe_container"> |
|||
<div class="oe_row oe_spaced"> |
|||
<div class="oe_span12"> |
|||
<h2 class="oe_slogan">Fix "False" in empty email body</h2> |
|||
<h3 class="oe_slogan">Feel free to send emails without body</h3> |
|||
</div> |
|||
|
|||
<div class="oe_span8"> |
|||
<p class="oe_mt32"> |
|||
Some time you need to send email with empty body. <br/>E.g. to send some file to partner. |
|||
</p> |
|||
</div> |
|||
<div class="oe_span8 oe_right"> |
|||
<div class="oe_demo oe_picture oe_screenshot"> |
|||
<img src="send.png?1"/> |
|||
</div> |
|||
</div> |
|||
</div> |
|||
</section> |
|||
|
|||
<section class="oe_container oe_dark"> |
|||
<div class="oe_row oe_spaced"> |
|||
<div class="oe_span6"> |
|||
<div class="oe_demo oe_picture oe_screenshot"> |
|||
<img src="receive-false.png"/> |
|||
</div> |
|||
</div> |
|||
|
|||
<div class="oe_span6"> |
|||
<p class="oe_mt32"> |
|||
There is a bug in odoo -- it sends "False" if email body if empty. |
|||
</p> |
|||
</div> |
|||
</div> |
|||
</section> |
|||
|
|||
|
|||
<section class="oe_container"> |
|||
<div class="oe_row oe_spaced"> |
|||
<div class="oe_span6"> |
|||
<p class="oe_mt32"> |
|||
This module just fix the issue. |
|||
</p> |
|||
</div> |
|||
<div class="oe_span6"> |
|||
<div class="oe_demo oe_picture oe_screenshot"> |
|||
<img src="receive-ok.png"/> |
|||
</div> |
|||
</div> |
|||
</div> |
|||
</section> |
|||
|
|||
<section class="oe_container"> |
|||
<div class="oe_row oe_spaced"> |
|||
<div class="oe_span12"> |
|||
<h2>Need our service?</h2> |
|||
<p class="oe_mt32">Contact us by <a href="mailto:it@it-projects.info">email</a> or fill out <a href="https://www.it-projects.info/page/website.contactus " target="_blank">request form</a></p> |
|||
<ul> |
|||
<li><a href="mailto:it@it-projects.info">it@it-projects.info <i class="fa fa-envelope-o"></i></a></li> |
|||
<li><a href="https://www.it-projects.info/page/website.contactus " target="_blank"> |
|||
https://www.it-projects.info/page/website.contactus <i class="fa fa-list-alt"></i></a></li> |
|||
</ul> |
|||
</div> |
|||
</div> |
|||
</section> |
After Width: 366 | Height: 276 | Size: 18 KiB |
After Width: 350 | Height: 280 | Size: 16 KiB |
After Width: 581 | Height: 400 | Size: 31 KiB |
@ -0,0 +1 @@ |
|||
import models |
@ -0,0 +1,16 @@ |
|||
{ |
|||
"name" : "Fix non-ascii header 'from' (OBSOLETE)", |
|||
"version" : "0.3", |
|||
"author" : "IT-Projects LLC, Ivan Yelizariev", |
|||
'license': 'LGPL-3', |
|||
"category" : "Social Network", |
|||
"website" : "https://yelizariev.github.io", |
|||
"description": """ |
|||
Obsolete in odoo 8.0 since Sep 10, 2014 https://github.com/odoo/odoo/commit/f2cf6ced17d3477b8858e3a8f955a42cc8a629ff . You can install this module, if you use older version. |
|||
""", |
|||
"depends" : ["base"], |
|||
#"init_xml" : [], |
|||
#"update_xml" : [], |
|||
#"active": True, |
|||
'installable': False |
|||
} |
@ -0,0 +1,4 @@ |
|||
import re |
|||
from openerp.addons.base.ir import ir_mail_server |
|||
|
|||
ir_mail_server.name_with_email_pattern = re.compile(r'([^<@>]+)\s*<([^ ,<@]+@[^> ,]+)>') |
After Width: 100 | Height: 100 | Size: 2.1 KiB |
@ -0,0 +1,8 @@ |
|||
Mail relocation |
|||
=============== |
|||
|
|||
Description: https://www.odoo.com/apps/modules/8.0/mail_move_message/ |
|||
|
|||
Further information and discussion: http://yelizariev.github.io/odoo/module/2015/04/10/mail-relocation.html |
|||
|
|||
Tested on Odoo 8.0 d023c079ed86468436f25da613bf486a4a17d625 |
@ -0,0 +1,2 @@ |
|||
import controllers |
|||
import mail_move_message_models |
@ -0,0 +1,20 @@ |
|||
{ |
|||
'name' : 'Mail relocation', |
|||
'version' : '1.0.4', |
|||
'author' : 'IT-Projects LLC, Ivan Yelizariev', |
|||
'license': 'LGPL-3', |
|||
'category' : 'Social Network', |
|||
'website' : 'https://twitter.com/yelizariev', |
|||
'price': 9.00, |
|||
'currency': 'EUR', |
|||
'depends' : ['mail', 'web_polymorphic_field'], |
|||
'images': ['images/inbox.png'], |
|||
'data':[ |
|||
'mail_move_message_views.xml', |
|||
'data/mail_move_message_data.xml', |
|||
], |
|||
'qweb': [ |
|||
'static/src/xml/mail_move_message_main.xml', |
|||
], |
|||
'installable': False |
|||
} |
@ -0,0 +1 @@ |
|||
import main |
@ -0,0 +1,55 @@ |
|||
from openerp.addons.web.controllers.main import DataSet |
|||
from openerp.tools.translate import _ |
|||
from openerp import http |
|||
from openerp.http import request |
|||
|
|||
class DataSetCustom(DataSet): |
|||
|
|||
def _extend_name(self, model, records): |
|||
cr, uid, context = request.cr, request.uid, request.context |
|||
Model = request.registry[model] |
|||
fields = Model.fields_get(cr, uid, False, context) |
|||
contact_field = False |
|||
for n, f in fields.iteritems(): |
|||
if f['type'] == 'many2one' and f['relation'] == 'res.partner': |
|||
contact_field = n |
|||
break |
|||
partner_info = {} |
|||
if contact_field: |
|||
partner_info = Model.read(cr, uid, [r[0] for r in records], [contact_field], context) |
|||
partner_info = dict([(p['id'], p[contact_field]) for p in partner_info]) |
|||
res = [] |
|||
for r in records: |
|||
if partner_info.get(r[0]): |
|||
res.append((r[0], _('%s [%s] ID %s') % (r[1], partner_info.get(r[0])[1], r[0]))) |
|||
else: |
|||
res.append((r[0], _('%s ID %s') % (r[1], r[0]))) |
|||
return res |
|||
|
|||
|
|||
@http.route('/web/dataset/call_kw/<model>/name_search', type='json', auth="user") |
|||
def name_search(self, model, method, args, kwargs): |
|||
context = kwargs.get('context') |
|||
if context and context.get('extended_name_with_contact'): |
|||
#add order by ID desc |
|||
cr, uid = request.cr, request.uid |
|||
Model = request.registry[model] |
|||
search_args = list(kwargs.get('args') or []) |
|||
limit = int(kwargs.get('limit') or 100) |
|||
operator = kwargs.get('operator') |
|||
name = kwargs.get('name') |
|||
if Model._rec_name and (not name == '' and operator == 'ilike'): |
|||
search_args += [(Model._rec_name, operator, name)] |
|||
ids = Model.search(cr, uid, search_args, limit=limit, order='id desc', context=context) |
|||
res = Model.name_get(cr, uid, ids, context) |
|||
return self._extend_name(model, res) |
|||
|
|||
return self._call_kw(model, method, args, kwargs) |
|||
|
|||
@http.route('/web/dataset/call_kw/<model>/name_get', type='json', auth="user") |
|||
def name_get(self, model, method, args, kwargs): |
|||
res = self._call_kw(model, method, args, kwargs) |
|||
context = kwargs.get('context') |
|||
if context and context.get('extended_name_with_contact'): |
|||
res = self._extend_name(model, res) |
|||
return res |
@ -0,0 +1,9 @@ |
|||
<?xml version="1.0"?> |
|||
<openerp> |
|||
<data noupdate="1"> |
|||
<record id="mail_relocation_models" model="ir.config_parameter"> |
|||
<field name="key">mail_relocation_models</field> |
|||
<field name="value">crm.lead,project.task</field> |
|||
</record> |
|||
</data> |
|||
</openerp> |
@ -0,0 +1,26 @@ |
|||
.. _changelog: |
|||
|
|||
Changelog |
|||
========= |
|||
|
|||
`1.0.4` |
|||
------- |
|||
|
|||
- FIX: don't allow to relocate message to itself as it cause infinitive loop |
|||
- ADD: 'Move Followers' option -- Add followers of current record to a new record. |
|||
|
|||
`1.0.3` |
|||
------- |
|||
|
|||
- FIX email_from parsing. There was an error with specific email_from value (e.g. '"name @ example" <name@example.com>') |
|||
|
|||
`1.0.2` |
|||
------- |
|||
|
|||
- big improvements in interface |
|||
|
|||
`1.0.1` |
|||
------- |
|||
|
|||
- fix bug "some messages are not shown in inbox after relocation" |
|||
- improve "Move back" tool |
@ -0,0 +1,173 @@ |
|||
# Translation of Odoo Server. |
|||
# This file contains the translation of the following modules: |
|||
# * mail_move_message |
|||
# |
|||
msgid "" |
|||
msgstr "" |
|||
"Project-Id-Version: Odoo Server 8.0\n" |
|||
"Report-Msgid-Bugs-To: \n" |
|||
"POT-Creation-Date: 2015-08-11 06:53+0000\n" |
|||
"PO-Revision-Date: 2015-08-11 06:53+0000\n" |
|||
"Last-Translator: <>\n" |
|||
"Language-Team: \n" |
|||
"MIME-Version: 1.0\n" |
|||
"Content-Type: text/plain; charset=UTF-8\n" |
|||
"Content-Transfer-Encoding: \n" |
|||
"Plural-Forms: \n" |
|||
|
|||
#. module: mail_move_message |
|||
#: view:mail_move_message.wizard:mail_move_message.view_wizard |
|||
msgid "Cancel" |
|||
msgstr "" |
|||
|
|||
#. module: mail_move_message |
|||
#: field:mail_move_message.wizard,create_uid:0 |
|||
msgid "Created by" |
|||
msgstr "" |
|||
|
|||
#. module: mail_move_message |
|||
#: field:mail_move_message.wizard,create_date:0 |
|||
msgid "Created on" |
|||
msgstr "" |
|||
|
|||
#. module: mail_move_message |
|||
#: field:mail_move_message.wizard,id:0 |
|||
msgid "ID" |
|||
msgstr "" |
|||
|
|||
#. module: mail_move_message |
|||
#: field:mail.message,is_moved:0 |
|||
msgid "Is moved" |
|||
msgstr "" |
|||
|
|||
#. module: mail_move_message |
|||
#: field:mail_move_message.wizard,write_uid:0 |
|||
msgid "Last Updated by" |
|||
msgstr "" |
|||
|
|||
#. module: mail_move_message |
|||
#: field:mail_move_message.wizard,write_date:0 |
|||
msgid "Last Updated on" |
|||
msgstr "" |
|||
|
|||
#. module: mail_move_message |
|||
#: field:mail_move_message.wizard,record_url:0 |
|||
msgid "Link to record" |
|||
msgstr "" |
|||
|
|||
#. module: mail_move_message |
|||
#: model:ir.model,name:mail_move_message.model_mail_message |
|||
#: view:mail_move_message.wizard:mail_move_message.view_wizard |
|||
#: field:mail_move_message.wizard,message_id:0 |
|||
msgid "Message" |
|||
msgstr "" |
|||
|
|||
#. module: mail_move_message |
|||
#: view:mail_move_message.wizard:mail_move_message.view_wizard |
|||
msgid "Move" |
|||
msgstr "" |
|||
|
|||
#. module: mail_move_message |
|||
#: help:mail_move_message.wizard,move_back:0 |
|||
msgid "Move message and submessages to original place" |
|||
msgstr "" |
|||
|
|||
#. module: mail_move_message |
|||
#: view:mail_move_message.wizard:mail_move_message.view_wizard |
|||
msgid "Move Message" |
|||
msgstr "" |
|||
|
|||
#. module: mail_move_message |
|||
#: field:mail_move_message.wizard,move_back:0 |
|||
msgid "Move to origin" |
|||
msgstr "" |
|||
|
|||
#. module: mail_move_message |
|||
#. openerp-web |
|||
#: code:addons/mail_move_message/static/src/xml/mail_move_message_main.xml:5 |
|||
#, python-format |
|||
msgid "Move to thread" |
|||
msgstr "" |
|||
|
|||
#. module: mail_move_message |
|||
#: field:mail.message,moved_by_message_id:0 |
|||
msgid "Moved by message" |
|||
msgstr "" |
|||
|
|||
#. module: mail_move_message |
|||
#: field:mail.message,moved_by_user_id:0 |
|||
msgid "Moved by user" |
|||
msgstr "" |
|||
|
|||
#. module: mail_move_message |
|||
#: view:mail_move_message.wizard:mail_move_message.view_wizard |
|||
msgid "Open message" |
|||
msgstr "" |
|||
|
|||
#. module: mail_move_message |
|||
#: field:mail.message,moved_from_parent_id:0 |
|||
msgid "Parent Message (Original)" |
|||
msgstr "" |
|||
|
|||
#. module: mail_move_message |
|||
#: code:addons/mail_move_message/mail_move_message_models.py:107 |
|||
#, python-format |
|||
msgid "Record" |
|||
msgstr "" |
|||
|
|||
#. module: mail_move_message |
|||
#: field:mail_move_message.wizard,res_id:0 |
|||
msgid "Record ID" |
|||
msgstr "" |
|||
|
|||
#. module: mail_move_message |
|||
#: field:mail_move_message.wizard,model_id:0 |
|||
msgid "Record type" |
|||
msgstr "" |
|||
|
|||
#. module: mail_move_message |
|||
#: field:mail.message,moved_from_res_id:0 |
|||
msgid "Related Document ID (Original)" |
|||
msgstr "" |
|||
|
|||
#. module: mail_move_message |
|||
#: field:mail.message,moved_from_model:0 |
|||
msgid "Related Document Model (Original)" |
|||
msgstr "" |
|||
|
|||
#. module: mail_move_message |
|||
#. openerp-web |
|||
#: code:addons/mail_move_message/static/src/js/mail_move_message.js:17 |
|||
#, python-format |
|||
msgid "Relocate Message" |
|||
msgstr "" |
|||
|
|||
#. module: mail_move_message |
|||
#: field:mail_move_message.wizard,parent_id:0 |
|||
msgid "Search by name" |
|||
msgstr "" |
|||
|
|||
#. module: mail_move_message |
|||
#: help:mail.message,moved_by_message_id:0 |
|||
msgid "Top message, that initate moving this message" |
|||
msgstr "" |
|||
|
|||
#. module: mail_move_message |
|||
#: view:mail_move_message.wizard:mail_move_message.view_wizard |
|||
msgid "You cannot move this message. It was already moved with a message bellow. Open one and apply changes there." |
|||
msgstr "" |
|||
|
|||
#. module: mail_move_message |
|||
#: help:mail_move_message.wizard,model_id:0 |
|||
msgid "List available Models is configured at Settings\Technical\Emails\Mail Relocation. Empty for unassigned email" |
|||
msgstr "" |
|||
|
|||
#. module: mail_move_message |
|||
#: help:mail_move_message.wizard,filter_by_partner:0 |
|||
msgid "Show only records with the same partner as email author" |
|||
msgstr "" |
|||
|
|||
#. module: mail_move_message |
|||
#: help:mail_move_message.wizard,move_followers:0 |
|||
msgid "Add followers of current record to a new record.\nYou must use this option, if new record has restricted access.\nYou can change default value for this option at Settings/System Parameters" |
|||
msgstr "" |
@ -0,0 +1,160 @@ |
|||
# Translation of Odoo Server. |
|||
# This file contains the translation of the following modules: |
|||
# * mail_move_message |
|||
# |
|||
msgid "" |
|||
msgstr "" |
|||
"Project-Id-Version: Odoo Server 8.0\n" |
|||
"Report-Msgid-Bugs-To: \n" |
|||
"POT-Creation-Date: 2015-08-11 06:53+0000\n" |
|||
"PO-Revision-Date: 2015-08-11 08:58+0200\n" |
|||
"Last-Translator: Matjaz Mozetic <m.mozetic@matmoz.si>\n" |
|||
"Language-Team: \n" |
|||
"MIME-Version: 1.0\n" |
|||
"Content-Type: text/plain; charset=UTF-8\n" |
|||
"Content-Transfer-Encoding: 8bit\n" |
|||
"Plural-Forms: \n" |
|||
"Language: sl\n" |
|||
"X-Generator: Poedit 1.8.2\n" |
|||
|
|||
#. module: mail_move_message |
|||
#: view:mail_move_message.wizard:mail_move_message.view_wizard |
|||
msgid "Cancel" |
|||
msgstr "Preklic" |
|||
|
|||
#. module: mail_move_message |
|||
#: field:mail_move_message.wizard,create_uid:0 |
|||
msgid "Created by" |
|||
msgstr "Ustvaril" |
|||
|
|||
#. module: mail_move_message |
|||
#: field:mail_move_message.wizard,create_date:0 |
|||
msgid "Created on" |
|||
msgstr "Ustvarjeno" |
|||
|
|||
#. module: mail_move_message |
|||
#: field:mail_move_message.wizard,id:0 |
|||
msgid "ID" |
|||
msgstr "ID" |
|||
|
|||
#. module: mail_move_message |
|||
#: field:mail.message,is_moved:0 |
|||
msgid "Is moved" |
|||
msgstr "Je premaknjeno" |
|||
|
|||
#. module: mail_move_message |
|||
#: field:mail_move_message.wizard,write_uid:0 |
|||
msgid "Last Updated by" |
|||
msgstr "Zadnjič posodobil" |
|||
|
|||
#. module: mail_move_message |
|||
#: field:mail_move_message.wizard,write_date:0 |
|||
msgid "Last Updated on" |
|||
msgstr "Zadnjič posodobljeno" |
|||
|
|||
#. module: mail_move_message |
|||
#: field:mail_move_message.wizard,record_url:0 |
|||
msgid "Link to record" |
|||
msgstr "Povezava do zapisa" |
|||
|
|||
#. module: mail_move_message |
|||
#: model:ir.model,name:mail_move_message.model_mail_message |
|||
#: view:mail_move_message.wizard:mail_move_message.view_wizard |
|||
#: field:mail_move_message.wizard,message_id:0 |
|||
msgid "Message" |
|||
msgstr "Sporočilo" |
|||
|
|||
#. module: mail_move_message |
|||
#: view:mail_move_message.wizard:mail_move_message.view_wizard |
|||
msgid "Move" |
|||
msgstr "Premik" |
|||
|
|||
#. module: mail_move_message |
|||
#: help:mail_move_message.wizard,move_back:0 |
|||
msgid "Move message and submessages to original place" |
|||
msgstr "Premik sporočila in podrejenih sporočil na izvorno mesto" |
|||
|
|||
#. module: mail_move_message |
|||
#: view:mail_move_message.wizard:mail_move_message.view_wizard |
|||
msgid "Move Message" |
|||
msgstr "Premik sporočila" |
|||
|
|||
#. module: mail_move_message |
|||
#: field:mail_move_message.wizard,move_back:0 |
|||
msgid "Move to origin" |
|||
msgstr "Premik na izvor" |
|||
|
|||
#. module: mail_move_message |
|||
#. openerp-web |
|||
#: code:addons/mail_move_message/static/src/xml/mail_move_message_main.xml:5 |
|||
#, python-format |
|||
msgid "Move to thread" |
|||
msgstr "Premik v nit" |
|||
|
|||
#. module: mail_move_message |
|||
#: field:mail.message,moved_by_message_id:0 |
|||
msgid "Moved by message" |
|||
msgstr "Premaknjeno s sporočilom" |
|||
|
|||
#. module: mail_move_message |
|||
#: field:mail.message,moved_by_user_id:0 |
|||
msgid "Moved by user" |
|||
msgstr "Premaknil uporabnik" |
|||
|
|||
#. module: mail_move_message |
|||
#: view:mail_move_message.wizard:mail_move_message.view_wizard |
|||
msgid "Open message" |
|||
msgstr "Odpri sporočilo" |
|||
|
|||
#. module: mail_move_message |
|||
#: field:mail.message,moved_from_parent_id:0 |
|||
msgid "Parent Message (Original)" |
|||
msgstr "Nadrejeno sporočilo (original)" |
|||
|
|||
#. module: mail_move_message |
|||
#: code:addons/mail_move_message/mail_move_message_models.py:107 |
|||
#, python-format |
|||
msgid "Record" |
|||
msgstr "Zapis" |
|||
|
|||
#. module: mail_move_message |
|||
#: field:mail_move_message.wizard,res_id:0 |
|||
msgid "Record ID" |
|||
msgstr "ID zapisa" |
|||
|
|||
#. module: mail_move_message |
|||
#: field:mail_move_message.wizard,model_id:0 |
|||
msgid "Record type" |
|||
msgstr "Tip zapisa" |
|||
|
|||
#. module: mail_move_message |
|||
#: field:mail.message,moved_from_res_id:0 |
|||
msgid "Related Document ID (Original)" |
|||
msgstr "ID povezanega dokumenta (original)" |
|||
|
|||
#. module: mail_move_message |
|||
#: field:mail.message,moved_from_model:0 |
|||
msgid "Related Document Model (Original)" |
|||
msgstr "Model povezanega dokumenta (original)" |
|||
|
|||
#. module: mail_move_message |
|||
#. openerp-web |
|||
#: code:addons/mail_move_message/static/src/js/mail_move_message.js:17 |
|||
#, python-format |
|||
msgid "Relocate Message" |
|||
msgstr "Premik sporočila" |
|||
|
|||
#. module: mail_move_message |
|||
#: field:mail_move_message.wizard,parent_id:0 |
|||
msgid "Search by name" |
|||
msgstr "Iskanje po nazivu" |
|||
|
|||
#. module: mail_move_message |
|||
#: help:mail.message,moved_by_message_id:0 |
|||
msgid "Top message, that initate moving this message" |
|||
msgstr "Zgornje sporočilo, ki je sprožilo premik tega sporočila" |
|||
|
|||
#. module: mail_move_message |
|||
#: view:mail_move_message.wizard:mail_move_message.view_wizard |
|||
msgid "You cannot move this message. It was already moved with a message bellow. Open one and apply changes there." |
|||
msgstr "Tega sporočila ne morete premakniti, ker je bilo že premaknjeno s spodnjim sporočilom. Tam lahko uveljavljate spremembe." |
After Width: 861 | Height: 628 | Size: 128 KiB |
@ -0,0 +1,382 @@ |
|||
from lxml import etree |
|||
from openerp import api, models, fields, SUPERUSER_ID |
|||
from openerp.tools import email_split |
|||
from openerp.tools.translate import _ |
|||
|
|||
class wizard(models.TransientModel): |
|||
_name = 'mail_move_message.wizard' |
|||
|
|||
def _model_selection(self): |
|||
selection = [] |
|||
config_parameters = self.env['ir.config_parameter'] |
|||
model_names = config_parameters.get_param('mail_relocation_models') |
|||
model_names = model_names.split(',') if model_names else [] |
|||
|
|||
if 'default_message_id' in self.env.context: |
|||
message = self.env['mail.message'].browse(self.env.context['default_message_id']) |
|||
if message.model and message.model not in model_names: |
|||
model_names.append(message.model) |
|||
if message.moved_from_model and message.moved_from_model not in model_names: |
|||
model_names.append(message.moved_from_model) |
|||
if model_names: |
|||
selection = [(m.model, m.display_name) for m in self.env['ir.model'].search([('model', 'in', model_names)])] |
|||
|
|||
return selection |
|||
|
|||
@api.model |
|||
def default_get(self, fields_list): |
|||
res = super(wizard, self).default_get(fields_list) |
|||
|
|||
model_fields = self.fields_get() |
|||
if model_fields['model']['selection']: |
|||
res['model'] = model_fields['model']['selection'] and model_fields['model']['selection'][0][0] |
|||
|
|||
if 'message_id' in res: |
|||
message = self.env['mail.message'].browse(res['message_id']) |
|||
email_from = message.email_from |
|||
parts = email_split(email_from.replace(' ',',')) |
|||
if parts: |
|||
email = parts[0] |
|||
name = email_from.find(email) != -1 and email_from[:email_from.index(email)].replace('"', '').replace('<', '').strip() or email_from |
|||
else: |
|||
name, email = email_from |
|||
res['message_name_from'] = name |
|||
res['message_email_from'] = email |
|||
|
|||
res['partner_id'] = message.author_id.id |
|||
if message.author_id and self.env.uid not in [u.id for u in message.author_id.user_ids]: |
|||
res['filter_by_partner'] = True |
|||
if message.author_id and res.get('model'): |
|||
res_id = self.env[res['model']].search([], order='id desc', limit=1) |
|||
res['res_id'] = res_id and res_id[0].id |
|||
|
|||
config_parameters = self.env['ir.config_parameter'] |
|||
res['move_followers'] = config_parameters.get_param('mail_relocation_move_followers') |
|||
|
|||
res['uid'] = self.env.uid |
|||
|
|||
return res |
|||
|
|||
message_id = fields.Many2one('mail.message', string='Message') |
|||
message_body = fields.Html(related='message_id.body', string='Message to move', readonly=True) |
|||
message_from = fields.Char(related='message_id.email_from', string='From', readonly=True) |
|||
message_subject = fields.Char(related='message_id.subject', string='Subject', readonly=True) |
|||
message_moved_by_message_id = fields.Many2one('mail.message', related='message_id.moved_by_message_id', string='Moved with', readonly=True) |
|||
message_moved_by_user_id = fields.Many2one('res.users', related='message_id.moved_by_user_id', string='Moved by', readonly=True) |
|||
message_is_moved = fields.Boolean(string='Is Moved', related='message_id.is_moved', readonly=True) |
|||
parent_id = fields.Many2one('mail.message', string='Search by name', ) |
|||
model = fields.Selection(_model_selection, string='Model') |
|||
res_id = fields.Integer(string='Record') |
|||
can_move = fields.Boolean('Can move', compute='get_can_move') |
|||
move_back = fields.Boolean('MOVE TO ORIGIN', help='Move message and submessages to original place') |
|||
partner_id = fields.Many2one('res.partner', string='Author') |
|||
filter_by_partner = fields.Boolean('Filter Records by partner') |
|||
message_email_from = fields.Char() |
|||
message_name_from = fields.Char() |
|||
# FIXME message_to_read should be True even if current message or any his childs are unread |
|||
message_to_read = fields.Boolean(related='message_id.to_read') |
|||
uid = fields.Integer() |
|||
move_followers = fields.Boolean( |
|||
'Move Followers', |
|||
help="Add followers of current record to a new record.\n" |
|||
"You must use this option, if new record has restricted access.\n" |
|||
"You can change default value for this option at Settings/System Parameters") |
|||
|
|||
@api.depends('message_id') |
|||
@api.one |
|||
def get_can_move(self): |
|||
# message was not moved before OR message is a top message of previous move |
|||
self.can_move = not self.message_id.moved_by_message_id or self.message_id.moved_by_message_id.id == self.message_id.id |
|||
|
|||
@api.onchange('move_back') |
|||
def on_change_move_back(self): |
|||
if not self.move_back: |
|||
return |
|||
self.parent_id = self.message_id.moved_from_parent_id |
|||
model = self.message_id.moved_from_model |
|||
if self.message_id.is_moved: |
|||
self.model = model |
|||
self.res_id = self.message_id.moved_from_res_id |
|||
|
|||
@api.onchange('parent_id', 'res_id', 'model') |
|||
def update_move_back(self): |
|||
model = self.message_id.moved_from_model |
|||
self.move_back = self.parent_id == self.message_id.moved_from_parent_id \ |
|||
and self.res_id == self.message_id.moved_from_res_id \ |
|||
and (self.model == model or (not self.model and not model)) |
|||
|
|||
@api.onchange('parent_id') |
|||
def on_change_parent_id(self): |
|||
if self.parent_id and self.parent_id.model: |
|||
self.model = self.parent_id.model |
|||
self.res_id = self.parent_id.res_id |
|||
else: |
|||
self.model = None |
|||
self.res_id = None |
|||
|
|||
@api.onchange('model', 'filter_by_partner', 'partner_id') |
|||
def on_change_partner(self): |
|||
domain = {'res_id': [('id', '!=', self.message_id.res_id)]} |
|||
if self.model and self.filter_by_partner and self.partner_id: |
|||
fields = self.env[self.model].fields_get(False) |
|||
contact_field = False |
|||
for n, f in fields.iteritems(): |
|||
if f['type'] == 'many2one' and f['relation'] == 'res.partner': |
|||
contact_field = n |
|||
break |
|||
if contact_field: |
|||
domain['res_id'].append((contact_field, '=', self.partner_id.id)) |
|||
if self.model: |
|||
res_id = self.env[self.model].search(domain['res_id'], order='id desc', limit=1) |
|||
self.res_id = res_id and res_id[0].id |
|||
else: |
|||
self.res_id = None |
|||
return {'domain': domain} |
|||
|
|||
@api.one |
|||
def check_access(self): |
|||
cr = self._cr |
|||
uid = self.env.user.id |
|||
operation = 'write' |
|||
context = self._context |
|||
|
|||
if not ( self.model and self.res_id ): |
|||
return True |
|||
model_obj = self.pool[self.model] |
|||
mids = model_obj.exists(cr, uid, [self.res_id]) |
|||
if hasattr(model_obj, 'check_mail_message_access'): |
|||
model_obj.check_mail_message_access(cr, uid, mids, operation, context=context) |
|||
else: |
|||
self.pool['mail.thread'].check_mail_message_access(cr, uid, mids, operation, model_obj=model_obj, context=context) |
|||
|
|||
@api.multi |
|||
def open_moved_by_message_id(self): |
|||
message_id = None |
|||
for r in self: |
|||
message_id = r.message_moved_by_message_id.id |
|||
return { |
|||
'type': 'ir.actions.act_window', |
|||
'res_model': 'mail_move_message.wizard', |
|||
'view_mode': 'form', |
|||
'view_type': 'form', |
|||
'views': [[False, 'form']], |
|||
'target': 'new', |
|||
'context': {'default_message_id': message_id}, |
|||
} |
|||
|
|||
@api.multi |
|||
def move(self): |
|||
for r in self: |
|||
r.check_access() |
|||
if not r.parent_id or not (r.parent_id.model == r.model and |
|||
r.parent_id.res_id == r.res_id): |
|||
#link with the first message of record |
|||
parent = self.env['mail.message'].search([('model','=',r.model), ('res_id','=',r.res_id)], order='id', limit=1) |
|||
r.parent_id = parent.id or None |
|||
|
|||
r.message_id.move(r.parent_id.id, r.res_id, r.model, r.move_back, r.move_followers) |
|||
|
|||
if not ( r.model and r.res_id ): |
|||
obj = self.pool.get('ir.model.data').get_object_reference(self._cr, SUPERUSER_ID, 'mail', 'mail_archivesfeeds')[1] |
|||
return { |
|||
'type' : 'ir.actions.client', |
|||
'name' : 'Archive', |
|||
'tag' : 'reload', |
|||
'params' : {'menu_id': obj}, |
|||
} |
|||
return { |
|||
'name': _('Record'), |
|||
'view_type': 'form', |
|||
'view_mode': 'form', |
|||
'res_model': r.model, |
|||
'res_id': r.res_id, |
|||
'views': [(False, 'form')], |
|||
'type': 'ir.actions.act_window', |
|||
} |
|||
|
|||
@api.one |
|||
def delete(self): |
|||
self.message_id.unlink() |
|||
return {} |
|||
|
|||
@api.model |
|||
def create_partner(self, message_id, relation, partner_id, message_name_from, message_email_from): |
|||
model = self.env[relation] |
|||
message = self.env['mail.message'].browse(message_id) |
|||
if not partner_id and message_name_from: |
|||
partner_id = self.env['res.partner'].with_context({'update_message_author': True}).create({ |
|||
'name': message_name_from, |
|||
'email': message_email_from |
|||
}).id |
|||
|
|||
context = {'partner_id': partner_id} |
|||
if model._rec_name: |
|||
context.update({'default_%s' % model._rec_name: message.subject}) |
|||
|
|||
fields = model.fields_get() |
|||
contact_field = False |
|||
for n, f in fields.iteritems(): |
|||
if f['type'] == 'many2one' and f['relation'] == 'res.partner': |
|||
contact_field = n |
|||
break |
|||
if contact_field: |
|||
context.update({'default_%s' % contact_field: partner_id}) |
|||
return context |
|||
|
|||
@api.one |
|||
def read_close(self): |
|||
self.message_id.set_message_read(True) |
|||
self.message_id.child_ids.set_message_read(True) |
|||
return {'type': 'ir.actions.act_window_close'} |
|||
|
|||
|
|||
class mail_message(models.Model): |
|||
_inherit = 'mail.message' |
|||
|
|||
is_moved = fields.Boolean('Is moved') |
|||
moved_from_res_id = fields.Integer('Related Document ID (Original)') |
|||
moved_from_model = fields.Char('Related Document Model (Original)') |
|||
moved_from_parent_id = fields.Many2one('mail.message', 'Parent Message (Original)', ondelete='set null') |
|||
moved_by_message_id = fields.Many2one('mail.message', 'Moved by message', ondelete='set null', help='Top message, that initate moving this message') |
|||
moved_by_user_id = fields.Many2one('res.users', 'Moved by user', ondelete='set null') |
|||
all_child_ids = fields.One2many('mail.message', string='All childs', compute='_get_all_childs', help='all childs, including subchilds') |
|||
|
|||
@api.one |
|||
def _get_all_childs(self, include_myself=True): |
|||
ids = [] |
|||
if include_myself: |
|||
ids.append(self.id) |
|||
while True: |
|||
new_ids = self.search([('parent_id', 'in', ids), ('id', 'not in', ids)]).ids |
|||
if new_ids: |
|||
ids = ids + new_ids |
|||
continue |
|||
break |
|||
moved_childs = self.search([('moved_by_message_id', '=', self.id)]).ids |
|||
self.all_child_ids = ids + moved_childs |
|||
|
|||
@api.multi |
|||
def move_followers(self, model, ids): |
|||
fol_obj = self.env['mail.followers'] |
|||
for message in self: |
|||
followers = fol_obj.sudo().search([('res_model', '=', message.model), |
|||
('res_id', '=', message.res_id)]) |
|||
for f in followers: |
|||
self.env[model].browse(ids).message_subscribe([f.partner_id.id], [s.id for s in f.subtype_ids]) |
|||
|
|||
@api.one |
|||
def move(self, parent_id, res_id, model, move_back, move_followers=False): |
|||
if parent_id == res_id: |
|||
return |
|||
vals = {} |
|||
if move_back: |
|||
# clear variables if we move everything back |
|||
vals['is_moved'] = False |
|||
vals['moved_by_user_id'] = None |
|||
vals['moved_by_message_id'] = None |
|||
|
|||
vals['moved_from_res_id'] = None |
|||
vals['moved_from_model'] = None |
|||
vals['moved_from_parent_id'] = None |
|||
else: |
|||
vals['parent_id'] = parent_id |
|||
vals['res_id'] = res_id |
|||
vals['model'] = model |
|||
|
|||
vals['is_moved'] = True |
|||
vals['moved_by_user_id'] = self.env.user.id |
|||
vals['moved_by_message_id'] = self.id |
|||
|
|||
for r in self.all_child_ids: |
|||
r_vals = vals.copy() |
|||
if not r.is_moved: |
|||
# moved_from_* variables contain not last, but original |
|||
# reference |
|||
r_vals['moved_from_parent_id'] = r.parent_id.id |
|||
r_vals['moved_from_res_id'] = r.res_id |
|||
r_vals['moved_from_model'] = r.model |
|||
elif move_back: |
|||
r_vals['parent_id'] = r.moved_from_parent_id.id |
|||
r_vals['res_id'] = r.moved_from_res_id |
|||
r_vals['model'] = r.moved_from_model |
|||
print 'update message', r, r_vals |
|||
if move_followers: |
|||
r.sudo().move_followers(r_vals.get('model'), r_vals.get('res_id')) |
|||
r.sudo().write(r_vals) |
|||
r.attachment_ids.sudo().write({ |
|||
'res_id': r_vals.get('res_id'), |
|||
'res_model': r_vals.get('model') |
|||
}) |
|||
|
|||
def name_get(self, cr, uid, ids, context=None): |
|||
if not (context or {}).get('extended_name'): |
|||
return super(mail_message, self).name_get(cr, uid, ids, context=context) |
|||
if isinstance(ids, (list, tuple)) and not len(ids): |
|||
return [] |
|||
if isinstance(ids, (long, int)): |
|||
ids = [ids] |
|||
reads = self.read(cr, uid, ids, ['record_name','model', 'res_id'], context=context) |
|||
res = [] |
|||
for record in reads: |
|||
name = record['record_name'] or '' |
|||
extended_name = ' [%s] ID %s' % (record.get('model', 'UNDEF'), record.get('res_id', 'UNDEF')) |
|||
res.append((record['id'], name + extended_name)) |
|||
return res |
|||
|
|||
def _message_read_dict(self, cr, uid, message, parent_id=False, context=None): |
|||
res = super(mail_message, self)._message_read_dict(cr, uid, message, parent_id, context) |
|||
res['is_moved'] = message.is_moved |
|||
return res |
|||
|
|||
|
|||
class mail_move_message_configuration(models.TransientModel): |
|||
_name = 'mail_move_message.config.settings' |
|||
_inherit = 'res.config.settings' |
|||
|
|||
model_ids = fields.Many2many(comodel_name='ir.model', string='Models') |
|||
move_followers = fields.Boolean('Move Followers') |
|||
|
|||
@api.model |
|||
def get_default_move_message_configs(self, fields): |
|||
config_parameters = self.env['ir.config_parameter'] |
|||
model_obj = self.env['ir.model'] |
|||
model_names = config_parameters.get_param('mail_relocation_models') |
|||
if not model_names: |
|||
return {} |
|||
model_names = model_names.split(',') |
|||
model_ids = model_obj.search([('model', 'in', model_names)]) |
|||
return { |
|||
'model_ids': [m.id for m in model_ids], |
|||
'move_followers': config_parameters.get_param('mail_relocation_move_followers') |
|||
} |
|||
|
|||
@api.multi |
|||
def set_move_message_configs(self): |
|||
config_parameters = self.env['ir.config_parameter'] |
|||
model_names = '' |
|||
for record in self: |
|||
model_names = ','.join([m.model for m in record.model_ids]) |
|||
config_parameters.set_param('mail_relocation_models', model_names) |
|||
config_parameters.set_param('mail_relocation_move_followers', record.move_followers or '') |
|||
|
|||
|
|||
class res_partner(models.Model): |
|||
_inherit = 'res.partner' |
|||
|
|||
@api.model |
|||
def create(self, vals): |
|||
res = super(res_partner, self).create(vals) |
|||
if 'update_message_author' in self.env.context and 'email' in vals: |
|||
mail_message_obj = self.env['mail.message'] |
|||
# Escape special SQL characters in email_address to avoid invalid matches |
|||
email_address = (vals['email'].replace('\\', '\\\\').replace('%', '\\%').replace('_', '\\_')) |
|||
email_brackets = "<%s>" % email_address |
|||
messages = mail_message_obj.search([ |
|||
'|', |
|||
('email_from', '=ilike', email_address), |
|||
('email_from', 'ilike', email_brackets), |
|||
('author_id', '=', False) |
|||
]) |
|||
if messages: |
|||
messages.sudo().write({'author_id': res.id}) |
|||
return res |
@ -0,0 +1,122 @@ |
|||
<?xml version="1.0" encoding="utf-8"?> |
|||
<openerp><data> |
|||
|
|||
<template id="assets_backend" name="custom bar assets" inherit_id="web.assets_backend"> |
|||
<xpath expr="." position="inside"> |
|||
<link rel="stylesheet" href="/mail_move_message/static/src/css/mail_move_message.css"/> |
|||
<script type="text/javascript" src="/mail_move_message/static/src/js/mail_move_message.js"></script> |
|||
</xpath> |
|||
</template> |
|||
|
|||
<record id="view_wizard" model="ir.ui.view"> |
|||
<field name="name">mail_move_message.wizard.view</field> |
|||
<field name="model">mail_move_message.wizard</field> |
|||
<field name="arch" type="xml"> |
|||
<form string="Move Message"> |
|||
|
|||
<field name="can_move" invisible="1"/> |
|||
<field name="message_is_moved" invisible="1"/> |
|||
<field name="message_name_from" invisible="1"/> |
|||
<field name="message_email_from" invisible="1"/> |
|||
<field name="message_to_read" invisible="1"/> |
|||
<field name="uid" invisible="1"/> |
|||
|
|||
<p attrs="{'invisible':[('can_move', '!=', False)]}">You cannot move this message. It was already moved with a message bellow. Open one and apply changes there.</p> |
|||
<group attrs="{'invisible':[('can_move', '!=', False)]}"> |
|||
<field name="message_moved_by_message_id" context="{'extended_name':1}"/> |
|||
<field name="message_moved_by_user_id"/> |
|||
<button name="open_moved_by_message_id" string="Open message" type="object" class="oe_highlight"/> |
|||
</group> |
|||
|
|||
<group attrs="{'invisible':[('can_move', '=', False)]}" colspan="2"> |
|||
<label for="model"/> |
|||
<div> |
|||
<field name="model" widget="polymorphic" polymorphic="res_id" class="oe_inline"/> |
|||
</div> |
|||
<label for="filter_by_partner"/> |
|||
<div> |
|||
<field name="filter_by_partner"/> |
|||
<field name="partner_id" style="width:50%"/> |
|||
<button string="Create Partner" attrs="{'invisible':[('partner_id','!=',False)]}" class="oe_highlight" special="quick_create" field="partner_id" context="{'force_email':True,'default_email':message_email_from,'default_name':message_name_from, 'update_message_author':True}"/> |
|||
</div> |
|||
<label for="res_id"/> |
|||
<div> |
|||
<field name="res_id" context="{'extended_name_with_contact':1}" widget="many2one" attrs="{'readonly': [('model','=',False)]}" style="width:50%"/> |
|||
<button string="Create new record" name="create_record" type="object" class="oe_highlight" attrs="{'invisible':['|',('model','=',False)]}" special="quick_create" field="res_id" use_for_mail_move_message="True"/> |
|||
</div> |
|||
<label for="move_back" attrs="{'invisible':[('message_is_moved','=',False)]}"/> |
|||
<div attrs="{'invisible':[('message_is_moved','=',False)]}"> |
|||
<field name="move_back"/> |
|||
</div> |
|||
<label for="move_followers"/> |
|||
<div> |
|||
<field name="move_followers"/> |
|||
</div> |
|||
</group> |
|||
<button name="move" string="Move" type="object" class="oe_highlight" attrs="{'invisible':[('can_move', '=', False)]}"/> |
|||
<button string="Close" class="" special="cancel" /> |
|||
<separator string="Message"/> |
|||
<group> |
|||
<field name="message_subject"/> |
|||
<field name="message_from"/> |
|||
<field name="message_id" invisible="1"/> |
|||
</group> |
|||
<div class="openerp mail_move_message"> |
|||
<div class="oe_mail"> |
|||
<div class="oe_msg"> |
|||
<div class="oe_msg_content"> |
|||
<div class="oe_msg_body"> |
|||
<!-- use built-in css for messages --> |
|||
<field name="message_body"/> |
|||
</div> |
|||
</div> |
|||
</div> |
|||
</div> |
|||
</div> |
|||
<footer> |
|||
<button name="read_close" string="Mark as read and Close" type="object" class="oe_highlight"/> or |
|||
<button special="cancel" string="Close" class="oe_link"/> |
|||
|
|||
|
|||
<button name="delete" string="Delete message" type="object" class="oe_highlight oe_right" confirm="Do you really want to delete this message?" attrs="{'invisible':[('uid','!=',1)]}"/> |
|||
</footer> |
|||
</form> |
|||
</field> |
|||
</record> |
|||
|
|||
<!-- Relocation config wizard --> |
|||
<record id="view_mail_move_message_config_settings" model="ir.ui.view"> |
|||
<field name="name">relocation settings</field> |
|||
<field name="model">mail_move_message.config.settings</field> |
|||
<field name="arch" type="xml"> |
|||
<form string="Configure Mail Relocation" class="oe_form_configuration"> |
|||
<header> |
|||
<button string="Apply" type="object" name="execute" class="oe_highlight"/> |
|||
or |
|||
<button string="Cancel" type="object" name="cancel" class="oe_link"/> |
|||
</header> |
|||
<div name="general"> |
|||
<separator string="Models"/> |
|||
<field name="model_ids" widget="many2many_tags"/> |
|||
<separator string="Options"/> |
|||
<label for="move_followers"/> |
|||
<field name="move_followers"/> |
|||
</div> |
|||
</form> |
|||
</field> |
|||
</record> |
|||
|
|||
<record id="action_mail_move_message_config" model="ir.actions.act_window"> |
|||
<field name="name">Mail Relocation</field> |
|||
<field name="type">ir.actions.act_window</field> |
|||
<field name="res_model">mail_move_message.config.settings</field> |
|||
<field name="view_id" ref="view_mail_move_message_config_settings"/> |
|||
<field name="view_mode">form</field> |
|||
<field name="target">inline</field> |
|||
</record> |
|||
|
|||
<!-- Add menu entry in Settings/Email --> |
|||
<menuitem name="Mail Relocation" id="menu_mail_move_message" parent="base.menu_email" sequence="99" action="action_mail_move_message_config"/> |
|||
|
|||
</data> |
|||
</openerp> |
After Width: 1012 | Height: 546 | Size: 73 KiB |
After Width: 1012 | Height: 546 | Size: 62 KiB |
After Width: 1012 | Height: 546 | Size: 51 KiB |
After Width: 890 | Height: 473 | Size: 58 KiB |
After Width: 120 | Height: 120 | Size: 3.0 KiB |
After Width: 1054 | Height: 562 | Size: 85 KiB |
After Width: 750 | Height: 400 | Size: 69 KiB |
@ -0,0 +1,140 @@ |
|||
<section class="oe_container"> |
|||
<div class="oe_row oe_spaced"> |
|||
<div class="oe_span12"> |
|||
<h2 class="oe_slogan">Mail relocation</h2> |
|||
<h3 class="oe_slogan">Relocate customer's mails to a correct place (lead, task etc.)</h3> |
|||
</div> |
|||
|
|||
<div class="oe_span12"> |
|||
<p class="oe_mt32"> |
|||
The module helps you to organise mails from customers. |
|||
</p> |
|||
<p> |
|||
If customer send mail to user's personal alias (e.g. admin@yourcompany.example.com), a user would be able to relocate such mail to corresponding record. |
|||
</p> |
|||
</div> |
|||
</div> |
|||
</section> |
|||
|
|||
<section class="oe_container oe_dark"> |
|||
<div class="oe_row oe_spaced"> |
|||
<div class="oe_span12"> |
|||
<h2>Usage</h2> |
|||
<p class="oe_mt32"> |
|||
Click on an icon of two cross arrows. |
|||
</p> |
|||
</div> |
|||
|
|||
<div class="oe_span12"> |
|||
<div class="oe_demo oe_picture oe_screenshot"> |
|||
<img src="inbox.png?3"/> |
|||
</div> |
|||
</div> |
|||
</div> |
|||
</section> |
|||
|
|||
<section class="oe_container"> |
|||
<div class="oe_row oe_spaced"> |
|||
<div class="oe_span12"> |
|||
<p class="oe_mt32"> |
|||
Find a record you need and click on "Move" button. |
|||
</p> |
|||
</div> |
|||
|
|||
<div class="oe_span12"> |
|||
<div class="oe_demo oe_picture oe_screenshot"> |
|||
<img src="inbox-move.png?4"/> |
|||
</div> |
|||
</div> |
|||
</div> |
|||
</section> |
|||
|
|||
<section class="oe_container oe_dark"> |
|||
<div class="oe_row oe_spaced"> |
|||
<div class="oe_span12"> |
|||
<p class="oe_mt32"> |
|||
Done! You've attached mail and its child mails to the record. |
|||
</p> |
|||
</div> |
|||
|
|||
<div class="oe_span12"> |
|||
<div class="oe_demo oe_picture oe_screenshot"> |
|||
<img src="record1.png?3"/> |
|||
</div> |
|||
<p class="oe_mt32"> |
|||
Scroll down... Moved mails are marked by highlighted icon. |
|||
</p> |
|||
<div class="oe_demo oe_picture oe_screenshot"> |
|||
<img src="record2.png?4"/> |
|||
</div> |
|||
</div> |
|||
</div> |
|||
</section> |
|||
|
|||
<section class="oe_container"> |
|||
<div class="oe_row oe_spaced"> |
|||
<div class="oe_span12"> |
|||
<p class="oe_mt32"> |
|||
You could easily return mails back if you change your mind. |
|||
</p> |
|||
</div> |
|||
|
|||
<div class="oe_span12"> |
|||
<div class="oe_demo oe_picture oe_screenshot"> |
|||
<img src="record-move-back.png?1"/> |
|||
</div> |
|||
<p class="oe_mt32"> |
|||
Further information and discussion: <a href="https://yelizariev.github.io/odoo/module/2015/04/10/mail-relocation.html">https://yelizariev.github.io/odoo/module/2015/04/10/mail-relocation.html</a> |
|||
</p> |
|||
</div> |
|||
</div> |
|||
</section> |
|||
|
|||
|
|||
<section class="oe_container oe_dark"> |
|||
<div class="oe_row oe_spaced"> |
|||
<div class="oe_span12"> |
|||
<h2>Tips and tricks</h2> |
|||
<p class="oe_mt32"> |
|||
Also, the module can be used as a viewer for wide html messages |
|||
</p> |
|||
</div> |
|||
<div class="oe_span12"> |
|||
<div class="oe_demo oe_picture oe_screenshot"> |
|||
<img src="html-message.png?1"/> |
|||
</div> |
|||
<p class="oe_mt32"> |
|||
Check message and then close window. |
|||
</p> |
|||
<div class="oe_demo oe_picture oe_screenshot"> |
|||
<img src="html-message-viewer1.png"/> |
|||
</div> |
|||
<p class="oe_mt32"> |
|||
Also, you can mark as read and close |
|||
</p> |
|||
<div class="oe_demo oe_picture oe_screenshot"> |
|||
<img src="html-message-viewer.png?2"/> |
|||
</div> |
|||
<p class="oe_mt32"> |
|||
In addition, the module can be used to delete a message (it's available for superuser only) |
|||
</p> |
|||
<div class="oe_demo oe_picture oe_screenshot"> |
|||
<img src="delete-message.png"/> |
|||
</div> |
|||
</div> |
|||
</div> |
|||
</section> |
|||
|
|||
<section class="oe_container"> |
|||
<div class="oe_row oe_spaced"> |
|||
<div class="oe_span12"> |
|||
<h2>Need our service?</h2> |
|||
<p class="oe_mt32">Contact us by <a href="mailto:it@it-projects.info">email</a> or fill out <a href="https://www.it-projects.info/page/website.contactus " target="_blank">request form</a></p> |
|||
<ul> |
|||
<li><a href="mailto:it@it-projects.info">it@it-projects.info <i class="fa fa-envelope-o"></i></a></li> |
|||
<li><a href="https://www.it-projects.info/page/website.contactus " target="_blank"> |
|||
https://www.it-projects.info/page/website.contactus <i class="fa fa-list-alt"></i></a></li> |
|||
</ul> |
|||
</div> |
|||
</div> |
|||
</section> |
After Width: 904 | Height: 482 | Size: 68 KiB |
After Width: 1007 | Height: 537 | Size: 58 KiB |
After Width: 750 | Height: 400 | Size: 70 KiB |
@ -0,0 +1,13 @@ |
|||
.openerp .oe_mail .oe_msg .oe_msg_icons .oe_move.oe_moved a { |
|||
color: #ffefe; |
|||
text-shadow: 0px 1px #961b1b,0px -1px #961b1b, -1px 0px #961b1b, 1px 0px #961b1b, 0px 3px 3px rgba(0,0,0,0.1); |
|||
} |
|||
|
|||
.openerp .oe_mail .oe_msg .oe_msg_icons .oe_move:hover a { |
|||
color: #ffa1a1; |
|||
text-shadow: 0px 1px #ff0000,0px -1px #ff0000, -1px 0px #ff0000, 1px 0px #ff0000, 0px 3px 3px rgba(0,0,0,0.1); |
|||
} |
|||
|
|||
.openerp.mail_move_message{ |
|||
width: 864px; |
|||
} |
@ -0,0 +1,91 @@ |
|||
openerp.mail_move_message = function (session) { |
|||
var _t = session.web._t, |
|||
_lt = session.web._lt; |
|||
|
|||
var mail = session.mail; |
|||
|
|||
mail.ThreadMessage.include({ |
|||
bind_events: function(){ |
|||
this._super.apply(this, arguments); |
|||
this.$('.oe_move').on('click', this.on_move_message) |
|||
}, |
|||
on_move_message: function(event){ |
|||
var self = this; |
|||
var context = { |
|||
'default_message_id': this.id |
|||
} |
|||
var action = { |
|||
name: _t('Relocate Message'), |
|||
type: 'ir.actions.act_window', |
|||
res_model: 'mail_move_message.wizard', |
|||
view_mode: 'form', |
|||
view_type: 'form', |
|||
views: [[false, 'form']], |
|||
target: 'new', |
|||
context: context, |
|||
}; |
|||
|
|||
self.do_action(action, { |
|||
'on_close': function(){ |
|||
self.check_for_rerender(); |
|||
} |
|||
}); |
|||
} |
|||
}) |
|||
|
|||
mail.MessageCommon.include({ |
|||
init: function (parent, datasets, options) { |
|||
this._super(parent, datasets, options); |
|||
this.is_moved = datasets.is_moved || false; |
|||
} |
|||
}) |
|||
|
|||
session.web.form.WidgetButton.include({ |
|||
on_click: function() { |
|||
if(this.node.attrs.special == 'quick_create'){ |
|||
var self = this; |
|||
var related_field = this.field_manager.fields[this.node.attrs['field']]; |
|||
var context_built = $.Deferred(); |
|||
if(this.node.attrs.use_for_mail_move_message) { |
|||
var model = new session.web.Model(this.view.dataset.model); |
|||
var partner_id = self.field_manager.fields['partner_id'].get_value(); |
|||
var message_name_from = self.field_manager.fields['message_name_from'].get_value(); |
|||
var message_email_from = self.field_manager.fields['message_email_from'].get_value(); |
|||
context_built = model.call('create_partner', [self.view.dataset.context.default_message_id, |
|||
related_field.field.relation, partner_id, message_name_from, message_email_from]); |
|||
} |
|||
else { |
|||
context_built.resolve(this.build_context()); |
|||
} |
|||
$.when(context_built).pipe(function (context) { |
|||
if(self.node.attrs.use_for_mail_move_message) { |
|||
self.field_manager.fields['partner_id'].set_value(context['partner_id']); |
|||
} |
|||
var pop = new session.web.form.FormOpenPopup(this); |
|||
pop.show_element( |
|||
related_field.field.relation, |
|||
false, |
|||
context, |
|||
{ |
|||
title: _t("Create new record"), |
|||
} |
|||
); |
|||
pop.on('closed', self, function () { |
|||
self.force_disabled = false; |
|||
self.check_disable(); |
|||
}); |
|||
pop.on('create_completed', self, function(id) { |
|||
related_field.set_value(id); |
|||
if(self.field_manager.fields['filter_by_partner']) { |
|||
self.field_manager.fields['filter_by_partner'].set_value(true); |
|||
} |
|||
}); |
|||
}); |
|||
} |
|||
else { |
|||
this._super.apply(this, arguments); |
|||
} |
|||
}, |
|||
}); |
|||
|
|||
} |
@ -0,0 +1,8 @@ |
|||
<?xml version="1.0" encoding="UTF-8"?> |
|||
<template> |
|||
<t t-extend="mail.thread.message"> |
|||
<t t-jquery=".oe_msg_icons" t-operation="append"> |
|||
<span t-attf-class="oe_move #{widget.is_moved?'oe_moved':''}"><a title="Move to thread" class="oe_e">f</a></span> |
|||
</t> |
|||
</t> |
|||
</template> |
@ -0,0 +1 @@ |
|||
import mail_outgoing_models |
@ -0,0 +1,20 @@ |
|||
{ |
|||
'name' : 'Outgoing mails menu', |
|||
'version' : '1.0.0', |
|||
'author' : 'IT-Projects LLC, Ivan Yelizariev', |
|||
'license': 'LGPL-3', |
|||
'category' : 'Social Network', |
|||
'website' : 'https://yelizariev.github.io', |
|||
'description': """ |
|||
Allows to check outgoing mails, i.e. failed or delayed. |
|||
|
|||
Tested on Odoo 8.0 ab7b5d7732a7c222a0aea45bd173742acd47242d |
|||
""", |
|||
'depends' : ['mail'], |
|||
'data':[ |
|||
'security/mail_outgoing.xml', |
|||
'security/ir.model.access.csv', |
|||
'mail_outgoing_views.xml', |
|||
], |
|||
'installable': False |
|||
} |
@ -0,0 +1,22 @@ |
|||
from openerp.osv import osv |
|||
|
|||
class mail_message(osv.Model): |
|||
_inherit = 'mail.message' |
|||
|
|||
def check_access_rule(self, cr, uid, ids, operation, context=None): |
|||
group_all_emails = self.pool.get('ir.model.data').xmlid_to_object(cr, uid, 'mail_outgoing.group_all_emails', context=context) |
|||
|
|||
user = self.pool['res.users'].browse(cr, uid, uid, context) |
|||
user_groups = set(user.groups_id) |
|||
if user_groups.issuperset(group_all_emails): |
|||
return |
|||
|
|||
return super(mail_message, self).check_access_rule(cr, uid, ids, operation, context) |
|||
|
|||
class mail_mail(osv.Model): |
|||
_name = 'mail.mail' |
|||
_inherit = ['mail.mail', 'ir.needaction_mixin'] |
|||
_needaction = True |
|||
|
|||
def _needaction_domain_get(self, cr, uid, context=None): |
|||
return [('state','in', ['outgoing', 'exception'])] |
@ -0,0 +1,34 @@ |
|||
<?xml version="1.0" encoding="utf-8"?> |
|||
<openerp> |
|||
<data> |
|||
|
|||
<record id="action_mail_outgoing" model="ir.actions.act_window"> |
|||
<field name="name">Outgoing</field> |
|||
<field name="res_model">mail.mail</field> |
|||
<field name="view_type">form</field> |
|||
<field name="view_mode">tree,form</field> |
|||
<field name="context">{}</field> |
|||
<field name="search_view_id" ref="mail.view_mail_search"/> |
|||
<field name="help" type="html"> |
|||
<p> |
|||
Failed or delayed emails will be shown here. You will be able to retry failed emails and send delayed emails manually. |
|||
</p> |
|||
</field> |
|||
</record> |
|||
|
|||
<menuitem name="Outgoing" id="menu_mail_outgoing" parent="mail.mail_feeds" action="action_mail_outgoing" sequence="20"/> |
|||
|
|||
<!-- After installation of the module, open the related menu --> |
|||
<record id="action_mail_outgoing_menu" model="ir.actions.client"> |
|||
<field name="name">Open Outgoing Menu</field> |
|||
<field name="tag">reload</field> |
|||
<field name="params" eval="{'menu_id': ref('mail_outgoing.menu_mail_outgoing')}"/> |
|||
</record> |
|||
<record id="base.open_menu" model="ir.actions.todo"> |
|||
<field name="action_id" ref="action_mail_outgoing_menu"/> |
|||
<field name="state">open</field> |
|||
</record> |
|||
|
|||
|
|||
</data> |
|||
</openerp> |
@ -0,0 +1,3 @@ |
|||
id,name,model_id:id,group_id:id,perm_read,perm_write,perm_create,perm_unlink |
|||
access_mail_mail_system,mail.mail.system,model_mail_mail,mail_outgoing.group_all_emails,1,1,1,1 |
|||
|
@ -0,0 +1,33 @@ |
|||
<?xml version="1.0" encoding="utf-8"?> |
|||
<openerp> |
|||
<data> |
|||
<record model="res.groups" id="group_all_emails"> |
|||
<field name="name">all_emails</field> |
|||
<field name="comment">Gets access to all emails. For example to check outgoing emails.</field> |
|||
</record> |
|||
|
|||
<record model="ir.rule" id="rule_mail_mail_user"> |
|||
<field name="name">mail.mail: user</field> |
|||
<field name="model_id" ref="mail.model_mail_mail"/> |
|||
<field name="domain_force">[('author_id', '=', user.partner_id.id)]</field> |
|||
<field name="groups" eval="[(4, ref('base.group_user'))]"/> |
|||
<field name="perm_read" eval="True"/> |
|||
<field name="perm_write" eval="True"/> |
|||
<field name="perm_create" eval="True"/> |
|||
<field name="perm_unlink" eval="False"/> |
|||
<field name="global" eval="False"/> |
|||
</record> |
|||
|
|||
<record model="ir.rule" id="rule_mail_mail_system"> |
|||
<field name="name">mail.mail: system</field> |
|||
<field name="model_id" ref="mail.model_mail_mail"/> |
|||
<field name="domain_force">[(1, '=', 1)]</field> |
|||
<field name="groups" eval="[(4, ref('mail_outgoing.group_all_emails'))]"/> |
|||
<field name="perm_read" eval="True"/> |
|||
<field name="perm_write" eval="True"/> |
|||
<field name="perm_create" eval="True"/> |
|||
<field name="perm_unlink" eval="True"/> |
|||
<field name="global" eval="False"/> |
|||
</record> |
|||
</data> |
|||
</openerp> |
After Width: 100 | Height: 100 | Size: 2.1 KiB |
@ -0,0 +1,4 @@ |
|||
Use partner language in mail |
|||
============================ |
|||
|
|||
FIXME: there is issue with frozen dict in new odoo. |
@ -0,0 +1 @@ |
|||
import models |
@ -0,0 +1,12 @@ |
|||
{ |
|||
'name' : 'Use partner language in mail', |
|||
'version' : '1.0.0', |
|||
'author' : 'IT-Projects LLC, Ivan Yelizariev', |
|||
'license': 'LGPL-3', |
|||
'category' : 'Mail', |
|||
'website' : 'https://yelizariev.github.io', |
|||
|
|||
'depends' : ['mail'], |
|||
'data':[], |
|||
'installable': False, |
|||
} |
@ -0,0 +1,88 @@ |
|||
# -*- coding: utf-8 -*- |
|||
from openerp.osv import osv,fields |
|||
from openerp import SUPERUSER_ID |
|||
|
|||
class mail_thread(osv.Model): |
|||
_inherit = "mail.thread" |
|||
|
|||
def message_track(self, cr, uid, ids, tracked_fields, initial_values, context={}): |
|||
|
|||
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> • <b>%s</b>: ' % change.get('col_info') |
|||
if change.get('old_value'): |
|||
message += '%s → ' % change.get('old_value') |
|||
message += '%s</div>' % change.get('new_value') |
|||
return message |
|||
|
|||
if not tracked_fields: |
|||
return True |
|||
|
|||
update_fields = [f for f in tracked_fields] |
|||
|
|||
for browse_record in self.browse(cr, uid, ids, context=context): |
|||
p = getattr(browse_record, 'partner_id', None) |
|||
if p: |
|||
browse_record._context.update({'lang':p.lang}) |
|||
|
|||
initial = initial_values[browse_record.id] |
|||
changes = set() |
|||
tracked_values = {} |
|||
|
|||
# update translation |
|||
tracked_fields = self._get_tracked_fields(cr, uid, update_fields, browse_record._context) |
|||
|
|||
# generate tracked_values data structure: {'col_name': {col_info, new_value, old_value}} |
|||
for col_name, col_info in tracked_fields.items(): |
|||
initial_value = initial[col_name] |
|||
record_value = getattr(browse_record, col_name) |
|||
|
|||
if record_value == initial_value and getattr(self._all_columns[col_name].column, '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(self._all_columns[col_name].column, '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 = [] |
|||
for field, track_info in self._track.items(): |
|||
if field not in changes: |
|||
continue |
|||
for subtype, method in track_info.items(): |
|||
if method(self, cr, uid, browse_record, context): |
|||
subtypes.append(subtype) |
|||
|
|||
posted = False |
|||
for subtype in subtypes: |
|||
subtype_rec = self.pool.get('ir.model.data').xmlid_to_object(cr, uid, subtype, context=context) |
|||
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(cr, uid, browse_record.id, body=message, subtype=subtype, context=context) |
|||
posted = True |
|||
if not posted: |
|||
message = format_message('', tracked_values) |
|||
self.message_post(cr, uid, browse_record.id, body=message, context=context) |
|||
return True |
After Width: 100 | Height: 100 | Size: 2.1 KiB |
@ -0,0 +1 @@ |
|||
# -*- coding: utf-8 -*- |
@ -0,0 +1,13 @@ |
|||
# -*- coding: utf-8 -*- |
|||
{ |
|||
'name': "Mail recovery", |
|||
'author': "IT-Projects LLC, Ildar Nasyrov", |
|||
'license': 'LGPL-3', |
|||
'website': "https://twitter.com/nasyrov_ildar", |
|||
'category': 'Uncategorized', |
|||
'version': '1.0.0', |
|||
'depends': ['mail'], |
|||
'data': [ |
|||
'data.xml', |
|||
], |
|||
} |
@ -0,0 +1,10 @@ |
|||
<?xml version="1.0" encoding="utf-8"?> |
|||
<openerp> |
|||
<data> |
|||
<template id="assets_backend" name="message storage" inherit_id="web.assets_backend"> |
|||
<xpath expr="." position="inside"> |
|||
<script type="text/javascript" src="/mail_recovery/static/src/js/mail_recovery.js"></script> |
|||
</xpath> |
|||
</template> |
|||
</data> |
|||
</openerp> |
@ -0,0 +1,25 @@ |
|||
openerp.mail_recovery = function (session) { |
|||
var mail = session.mail; |
|||
|
|||
mail.ThreadComposeMessage = mail.ThreadComposeMessage.extend({ |
|||
bind_events: function () { |
|||
var self = this; |
|||
this.$('textarea').on('focus', self.on_focus_textarea); |
|||
this.$('textarea').on('keyup', self.on_keyup_textarea); |
|||
this._super(); |
|||
}, |
|||
on_focus_textarea: function(event) { |
|||
var $input = $(event.target); |
|||
if (!$input.val()) { |
|||
$input.val(window.localStorage['message_storage']); |
|||
} |
|||
}, |
|||
on_keyup_textarea: function(event) { |
|||
window.localStorage['message_storage'] = $(event.target).val(); |
|||
}, |
|||
on_message_post: function (event) { |
|||
window.localStorage['message_storage'] = ''; |
|||
return this._super(event); |
|||
}, |
|||
}); |
|||
}; |
@ -0,0 +1,9 @@ |
|||
.. _changelog: |
|||
|
|||
Changelog |
|||
========= |
|||
|
|||
`1.0.2` |
|||
------- |
|||
|
|||
- FIX access issue "read access error on delete" |
@ -0,0 +1,50 @@ |
|||
# Translation of Odoo Server. |
|||
# This file contains the translation of the following modules: |
|||
# * mail_sent |
|||
# |
|||
msgid "" |
|||
msgstr "" |
|||
"Project-Id-Version: Odoo Server 8.0\n" |
|||
"Report-Msgid-Bugs-To: \n" |
|||
"POT-Creation-Date: 2015-08-11 06:59+0000\n" |
|||
"PO-Revision-Date: 2015-08-11 06:59+0000\n" |
|||
"Last-Translator: <>\n" |
|||
"Language-Team: \n" |
|||
"MIME-Version: 1.0\n" |
|||
"Content-Type: text/plain; charset=UTF-8\n" |
|||
"Content-Transfer-Encoding: \n" |
|||
"Plural-Forms: \n" |
|||
|
|||
#. module: mail_sent |
|||
#: model:ir.actions.client,help:mail_sent.action_mail_sent_feeds |
|||
msgid "<p>\n" |
|||
" No message found and no message sent yet.\n" |
|||
" </p><p>\n" |
|||
" Click on the top-right icon to compose a message. This\n" |
|||
" message will be sent by email if it's an internal contact.\n" |
|||
" </p>\n" |
|||
" " |
|||
msgstr "" |
|||
|
|||
#. module: mail_sent |
|||
#: model:ir.model,name:mail_sent.model_mail_message |
|||
msgid "Message" |
|||
msgstr "" |
|||
|
|||
#. module: mail_sent |
|||
#: model:ir.model,name:mail_sent.model_mail_notification |
|||
msgid "Notifications" |
|||
msgstr "" |
|||
|
|||
#. module: mail_sent |
|||
#: model:ir.actions.client,name:mail_sent.action_mail_sent_feeds |
|||
#: model:ir.ui.menu,name:mail_sent.mail_sentfeeds |
|||
#: field:mail.message,sent:0 |
|||
msgid "Sent" |
|||
msgstr "" |
|||
|
|||
#. module: mail_sent |
|||
#: help:mail.message,sent:0 |
|||
msgid "Was message sent to someone" |
|||
msgstr "" |
|||
|
@ -0,0 +1,58 @@ |
|||
# Translation of Odoo Server. |
|||
# This file contains the translation of the following modules: |
|||
# * mail_sent |
|||
# |
|||
msgid "" |
|||
msgstr "" |
|||
"Project-Id-Version: Odoo Server 8.0\n" |
|||
"Report-Msgid-Bugs-To: \n" |
|||
"POT-Creation-Date: 2015-08-11 06:59+0000\n" |
|||
"PO-Revision-Date: 2015-08-11 09:01+0200\n" |
|||
"Last-Translator: Matjaz Mozetic <m.mozetic@matmoz.si>\n" |
|||
"Language-Team: \n" |
|||
"MIME-Version: 1.0\n" |
|||
"Content-Type: text/plain; charset=UTF-8\n" |
|||
"Content-Transfer-Encoding: 8bit\n" |
|||
"Plural-Forms: \n" |
|||
"Language: sl\n" |
|||
"X-Generator: Poedit 1.8.2\n" |
|||
|
|||
#. module: mail_sent |
|||
#: model:ir.actions.client,help:mail_sent.action_mail_sent_feeds |
|||
msgid "" |
|||
"<p>\n" |
|||
" No message found and no message sent yet.\n" |
|||
" </p><p>\n" |
|||
" Click on the top-right icon to compose a message. This\n" |
|||
" message will be sent by email if it's an internal contact.\n" |
|||
" </p>\n" |
|||
" " |
|||
msgstr "" |
|||
"<p>\n" |
|||
" Nobeno sporočilo ni bilo najdeno in ni bilo še poslano.\n" |
|||
" </p><p>\n" |
|||
" Pritisnite na ikono zgoraj desno za sestavljanje sporočila. To\n" |
|||
" sporočilo bo, če gre za interni stik, poslano preko e-pošte.\n" |
|||
" </p>\n" |
|||
" " |
|||
|
|||
#. module: mail_sent |
|||
#: model:ir.model,name:mail_sent.model_mail_message |
|||
msgid "Message" |
|||
msgstr "Sporočilo" |
|||
|
|||
#. module: mail_sent |
|||
#: model:ir.model,name:mail_sent.model_mail_notification |
|||
msgid "Notifications" |
|||
msgstr "Obvestila" |
|||
|
|||
#. module: mail_sent |
|||
#: model:ir.actions.client,name:mail_sent.action_mail_sent_feeds |
|||
#: model:ir.ui.menu,name:mail_sent.mail_sentfeeds field:mail.message,sent:0 |
|||
msgid "Sent" |
|||
msgstr "Poslano" |
|||
|
|||
#. module: mail_sent |
|||
#: help:mail.message,sent:0 |
|||
msgid "Was message sent to someone" |
|||
msgstr "Je bilo sporočilo komu poslano" |
@ -0,0 +1,28 @@ |
|||
from openerp import api, models, fields |
|||
|
|||
|
|||
class mail_message(models.Model): |
|||
_inherit = 'mail.message' |
|||
|
|||
@api.one |
|||
@api.depends('author_id', 'notified_partner_ids') |
|||
def _get_sent(self): |
|||
self_sudo = self.sudo() |
|||
self_sudo.sent = len(self_sudo.notified_partner_ids) > 1 or len(self_sudo.notified_partner_ids) == 1 and self_sudo.author_id and self_sudo.notified_partner_ids[0].id != self_sudo.author_id.id |
|||
|
|||
sent = fields.Boolean('Sent', compute=_get_sent, help='Was message sent to someone', store=True) |
|||
|
|||
|
|||
class mail_notification(models.Model): |
|||
_inherit = 'mail.notification' |
|||
|
|||
def _notify(self, cr, uid, message_id, **kwargs): |
|||
super(mail_notification, self)._notify(cr, uid, message_id, **kwargs) |
|||
self.pool['mail.message'].browse(cr, uid, message_id)._get_sent() |
|||
|
|||
|
|||
class mail_compose_message(models.TransientModel): |
|||
|
|||
_inherit = 'mail.compose.message' |
|||
sent = fields.Boolean('Sent', help='dummy field to fix inherit error') |
|||
|
@ -0,0 +1,47 @@ |
|||
<section class="oe_container"> |
|||
<div class="oe_row oe_spaced"> |
|||
<div class="oe_span12"> |
|||
<h2 class="oe_slogan">Sentbox</h2> |
|||
<h3 class="oe_slogan">Quick way to check sent messages</h3> |
|||
</div> |
|||
<div class="oe_span6"> |
|||
<div class="oe_row_img oe_centered"> |
|||
<img class="oe_picture oe_screenshot" src="menu.png"/> |
|||
</div> |
|||
</div> |
|||
<div class="oe_span6"> |
|||
<p class="oe_mt32"> |
|||
The module adds usual menu. |
|||
</p> |
|||
</div> |
|||
</div> |
|||
</section> |
|||
|
|||
<section class="oe_container oe_dark"> |
|||
<div class="oe_row oe_spaced"> |
|||
<div class="oe_span12"> |
|||
<p class="oe_mt32"> |
|||
This menu shows messages in threads (like in other menus), but shows only sent messages. |
|||
</p> |
|||
</div> |
|||
<div class="oe_span12"> |
|||
<div class="oe_demo oe_picture oe_screenshot"> |
|||
<img src="messages.png?1"/> |
|||
</div> |
|||
</div> |
|||
</div> |
|||
</section> |
|||
|
|||
<section class="oe_container"> |
|||
<div class="oe_row oe_spaced"> |
|||
<div class="oe_span12"> |
|||
<h2>Need our service?</h2> |
|||
<p class="oe_mt32">Contact us by <a href="mailto:it@it-projects.info">email</a> or fill out <a href="https://www.it-projects.info/page/website.contactus " target="_blank">request form</a></p> |
|||
<ul> |
|||
<li><a href="mailto:it@it-projects.info">it@it-projects.info <i class="fa fa-envelope-o"></i></a></li> |
|||
<li><a href="https://www.it-projects.info/page/website.contactus " target="_blank"> |
|||
https://www.it-projects.info/page/website.contactus <i class="fa fa-list-alt"></i></a></li> |
|||
</ul> |
|||
</div> |
|||
</div> |
|||
</section> |
After Width: 217 | Height: 167 | Size: 5.8 KiB |
After Width: 625 | Height: 217 | Size: 37 KiB |
@ -0,0 +1,38 @@ |
|||
<?xml version="1.0" encoding="utf-8"?> |
|||
<openerp> |
|||
<data> |
|||
<record id="action_mail_sent_feeds" model="ir.actions.client"> |
|||
<field name="name">Sent</field> |
|||
<field name="tag">mail.wall</field> |
|||
<field name="context">{ |
|||
'default_model': 'res.users', |
|||
'default_res_id': uid, |
|||
'thread_model': 'res.partner', |
|||
'needaction_menu_ref': ['mail.mail_tomefeeds', 'mail.mail_starfeeds', 'mail.mail_inboxfeeds'] |
|||
}</field> |
|||
<field name="params" eval=""{ |
|||
'domain': [ |
|||
('author_id.user_ids', 'in', [uid]), |
|||
('sent', '=', True), |
|||
], |
|||
'view_mailbox': True, |
|||
'show_compose_message': False |
|||
}""/> |
|||
<field name="help" type="html"> |
|||
<p> |
|||
No message found and no message sent yet. |
|||
</p><p> |
|||
Click on the top-right icon to compose a message. This |
|||
message will be sent by email if it's an internal contact. |
|||
</p> |
|||
</field> |
|||
</record> |
|||
|
|||
<record id="mail_sentfeeds" model="ir.ui.menu"> |
|||
<field name="name">Sent</field> |
|||
<field name="sequence" eval="10"/> |
|||
<field name="action" ref="action_mail_sent_feeds"/> |
|||
<field name="parent_id" ref="mail.mail_feeds"/> |
|||
</record> |
|||
</data> |
|||
</openerp> |
@ -0,0 +1,2 @@ |
|||
Mark unstarred email as read, remove filter in 'To-do' folder |
|||
============================================================= |
@ -0,0 +1,13 @@ |
|||
# -*- coding: utf-8 -*- |
|||
{ |
|||
'name': "Mark unstarred email as read, remove filter in 'To-do' folder", |
|||
'version': '1.0', |
|||
'author': 'IT-Projects LLC', |
|||
'license': 'LGPL-3', |
|||
'website': "https://yelizariev.github.io", |
|||
'category': 'Tools', |
|||
'depends': ['mail'], |
|||
'data': ['mail_todo_custom.xml'], |
|||
'demo': [], |
|||
'installable': False, |
|||
} |
@ -0,0 +1,25 @@ |
|||
<?xml version="1.0" encoding="utf-8"?> |
|||
<openerp> |
|||
<data> |
|||
|
|||
<template id="assets_backend_inherited_mail_todo_custom" name="Mail todo custom" inherit_id="web.assets_backend"> |
|||
<xpath expr="." position="inside"> |
|||
<script type="text/javascript" src="/mail_todo_custom/static/src/js/mail_todo_custom.js"></script> |
|||
</xpath> |
|||
</template> |
|||
|
|||
<record id="mail.action_mail_star_feeds" model="ir.actions.client"> |
|||
<field name="context"> |
|||
{ |
|||
'default_model': 'res.users', |
|||
'default_res_id': uid, |
|||
'thread_model': 'res.partner', |
|||
'search_default_message_unread': False, |
|||
'view_todo': True |
|||
} |
|||
</field> |
|||
</record> |
|||
|
|||
|
|||
</data> |
|||
</openerp> |
@ -0,0 +1,44 @@ |
|||
openerp.mail_todo_custom = function(session) { |
|||
|
|||
var mail = session.mail; |
|||
|
|||
mail.ThreadMessage.include({ |
|||
|
|||
on_star: function (event) { |
|||
event.stopPropagation(); |
|||
var self=this; |
|||
var button = self.$('.oe_star:first'); |
|||
|
|||
this.ds_message.call('set_message_starred', [[self.id], !self.is_favorite, true]) |
|||
.then(function (star) { |
|||
self.is_favorite=star; |
|||
if (self.is_favorite) { |
|||
button.addClass('oe_starred'); |
|||
} else { |
|||
button.removeClass('oe_starred'); |
|||
} |
|||
|
|||
if (self.options.view_inbox && self.is_favorite) { |
|||
self.on_message_read_unread(true); |
|||
} |
|||
else { |
|||
self.check_for_rerender(); |
|||
} |
|||
if (self.options.view_todo && !self.is_favorite) { |
|||
self.on_message_read_unread(true); |
|||
} |
|||
}); |
|||
return false; |
|||
}, |
|||
|
|||
}); |
|||
|
|||
mail.Widget.include({ |
|||
|
|||
init: function (parent, action) { |
|||
this._super(parent, action); |
|||
this.action.params.view_todo = this.action.context.view_todo || false; |
|||
}, |
|||
|
|||
}); |
|||
} |
@ -0,0 +1,4 @@ |
|||
Custom mail wall |
|||
================ |
|||
|
|||
Tested on Odoo 8.0 ab7b5d7732a7c222a0aea45bd173742acd47242d |
@ -0,0 +1 @@ |
|||
import models |
@ -0,0 +1,25 @@ |
|||
{ |
|||
'name' : 'Custom mail wall', |
|||
'version' : '1.0.0', |
|||
'author' : 'IT-Projects LLC, Ivan Yelizariev', |
|||
'license': 'LGPL-3', |
|||
'category' : 'Custom', |
|||
'website' : 'https://yelizariev.github.io', |
|||
'depends' : ['gamification', |
|||
'gamification_extra', |
|||
'hr', |
|||
'sale', |
|||
'sales_team', |
|||
'crm', |
|||
'calendar', |
|||
'project', |
|||
'mail_wall_widgets', |
|||
'sale_mediation_custom', |
|||
'access_custom', |
|||
], |
|||
'data':[ |
|||
'views.xml', |
|||
'data.xml', |
|||
], |
|||
'installable': False, |
|||
} |
@ -0,0 +1,402 @@ |
|||
<?xml version="1.0" encoding="UTF-8"?> |
|||
<openerp> |
|||
<data> |
|||
<record id="goal_average_payment_time" model="gamification.goal.definition"> |
|||
<field name="name">Average payment time</field> |
|||
<field name="description"></field> |
|||
<field name="monetary" eval="False"/> |
|||
<field name="suffix">days</field> |
|||
<field name="computation_mode">avg</field> |
|||
<field name="display_mode">progress</field> |
|||
<field name="model_id" ref="sale.model_sale_order"/> |
|||
<field name="field_id" ref="sale_mediation_custom.field_sale_order_invoice_deal_time"/> |
|||
<field name="field_date_id" ref="sale.field_sale_order_date_order"/> |
|||
<field name="condition">lower</field> |
|||
<field name="domain">[('user_id','=', user.id),('state','in', ['done'])]</field> |
|||
</record> |
|||
<record id="goal_average_payment_time_all" model="gamification.goal.definition"> |
|||
<field name="name">Average payment time [ALL]</field> |
|||
<field name="description"></field> |
|||
<field name="monetary" eval="False"/> |
|||
<field name="suffix">days</field> |
|||
<field name="computation_mode">avg</field> |
|||
<field name="display_mode">progress</field> |
|||
<field name="model_id" ref="sale.model_sale_order"/> |
|||
<field name="field_id" ref="sale_mediation_custom.field_sale_order_invoice_deal_time"/> |
|||
<field name="field_date_id" ref="sale.field_sale_order_date_order"/> |
|||
<field name="condition">lower</field> |
|||
<field name="domain">[('state','in', ['done'])]</field> |
|||
</record> |
|||
<record id="goal_average_deal_time" model="gamification.goal.definition"> |
|||
<field name="name">Average deal time</field> |
|||
<field name="description"></field> |
|||
<field name="monetary" eval="False"/> |
|||
<field name="suffix">days</field> |
|||
<field name="computation_mode">avg</field> |
|||
<field name="display_mode">progress</field> |
|||
<field name="model_id" ref="crm.model_crm_lead"/> |
|||
<field name="field_id" ref="sale_mediation_custom.field_crm_lead_deal_time"/> |
|||
<field name="field_date_id" ref="crm.field_crm_lead_create_date"/> |
|||
<field name="condition">lower</field> |
|||
<field name="domain">[('user_id','=', user.id), ('sales_funnel_type', 'in', ['won']), ('date_closed', '!=', False)]</field> |
|||
</record> |
|||
<record id="goal_average_deal_time_all" model="gamification.goal.definition"> |
|||
<field name="name">Average deal time [ALL]</field> |
|||
<field name="description"></field> |
|||
<field name="monetary" eval="False"/> |
|||
<field name="suffix">days</field> |
|||
<field name="computation_mode">avg</field> |
|||
<field name="display_mode">progress</field> |
|||
<field name="model_id" ref="crm.model_crm_lead"/> |
|||
<field name="field_id" ref="sale_mediation_custom.field_crm_lead_deal_time"/> |
|||
<field name="field_date_id" ref="crm.field_crm_lead_create_date"/> |
|||
<field name="condition">lower</field> |
|||
<field name="domain">[('sales_funnel_type', 'in', ['won']), ('date_closed', '!=', False)]</field> |
|||
</record> |
|||
<record id="goal_oldest_lead" model="gamification.goal.definition"> |
|||
<field name="name">Oldest lead</field> |
|||
<field name="description"></field> |
|||
<field name="monetary" eval="False"/> |
|||
<field name="suffix">days</field> |
|||
<field name="computation_mode">max</field> |
|||
<field name="display_mode">progress</field> |
|||
<field name="condition">lower</field> |
|||
<field name="domain">[('user_id','=', user.id), ('sales_funnel_type', '=', 'lead')]</field> |
|||
<field name="model_id" ref="crm.model_crm_lead"/> |
|||
<field name="field_id" ref="sale_mediation_custom.field_crm_lead_last_action_time"/> |
|||
</record> |
|||
<record id="goal_oldest_lead_all" model="gamification.goal.definition"> |
|||
<field name="name">Oldest lead [ALL]</field> |
|||
<field name="description"></field> |
|||
<field name="monetary" eval="False"/> |
|||
<field name="suffix">days</field> |
|||
<field name="computation_mode">max</field> |
|||
<field name="display_mode">progress</field> |
|||
<field name="condition">lower</field> |
|||
<field name="domain">[('sales_funnel_type', '=', 'lead')]</field> |
|||
<field name="model_id" ref="crm.model_crm_lead"/> |
|||
<field name="field_id" ref="sale_mediation_custom.field_crm_lead_last_action_time"/> |
|||
</record> |
|||
<record id="goal_oldest_opp" model="gamification.goal.definition"> |
|||
<field name="name">Oldest Opportunity</field> |
|||
<field name="description"></field> |
|||
<field name="monetary" eval="False"/> |
|||
<field name="suffix">days</field> |
|||
<field name="computation_mode">max</field> |
|||
<field name="display_mode">progress</field> |
|||
<field name="condition">lower</field> |
|||
<field name="domain">[('user_id','=', user.id), '|', ('sales_funnel_type', '=', 'quotation'),('sales_funnel_type', '=', 'negotiation')]</field> |
|||
<field name="model_id" ref="crm.model_crm_lead"/> |
|||
<field name="field_id" ref="sale_mediation_custom.field_crm_lead_last_action_time"/> |
|||
</record> |
|||
<record id="goal_oldest_opp_all" model="gamification.goal.definition"> |
|||
<field name="name">Oldest Opportunity [ALL]</field> |
|||
<field name="description"></field> |
|||
<field name="monetary" eval="False"/> |
|||
<field name="suffix">days</field> |
|||
<field name="computation_mode">max</field> |
|||
<field name="display_mode">progress</field> |
|||
<field name="condition">lower</field> |
|||
<field name="domain">['|', ('sales_funnel_type', '=', 'quotation'),('sales_funnel_type', '=', 'negotiation')]</field> |
|||
<field name="model_id" ref="crm.model_crm_lead"/> |
|||
<field name="field_id" ref="sale_mediation_custom.field_crm_lead_last_action_time"/> |
|||
</record> |
|||
|
|||
<record id="goal_personal_comission" model="gamification.goal.definition"> |
|||
<field name="name">Personal comission</field> |
|||
<field name="description"></field> |
|||
<field name="monetary" eval="True"/> |
|||
<field name="computation_mode">python</field> |
|||
<field name="display_mode">progress</field> |
|||
<field name="condition">higher</field> |
|||
<field name="domain">[('state','!=','cancel'),('user_id','=',user.id),('type','=','out_invoice')]</field><!-- duplicated at compute_code--> |
|||
<field name="model_id" ref="account.model_account_invoice_report"/> |
|||
<field name="field_id" ref="account.field_account_invoice_report_price_total"/> |
|||
<field name="field_date_id" eval="ref('account.field_account_invoice_report_date')" /> |
|||
<field name="compute_code"> |
|||
personal_comission = object.user_id.employee_ids and object.user_id.employee_ids[0].personal_comission or 0.0 |
|||
result = object.sum * personal_comission / 100.0 |
|||
</field> |
|||
</record> |
|||
|
|||
<record id="goal_team_bonus" model="gamification.goal.definition"> |
|||
<field name="name">Team bonus</field> |
|||
<field name="description">Monthly team bonus</field> |
|||
<field name="monetary" eval="True"/> |
|||
<field name="computation_mode">python</field> |
|||
<field name="display_mode">progress</field> |
|||
<field name="condition">higher</field> |
|||
<field name="domain">[('state','!=','cancel'),('section_id','=',user.default_section_id.id),('type','=','out_invoice')]</field><!-- duplicated at compute_code--> |
|||
<field name="model_id" ref="account.model_account_invoice_report"/> |
|||
<field name="field_id" ref="account.field_account_invoice_report_price_total"/> |
|||
<field name="field_date_id" eval="ref('account.field_account_invoice_report_date')" /> |
|||
<field name="compute_code"> |
|||
<![CDATA[ |
|||
minimum = object.user_id.default_section_id.invoiced_forecast |
|||
maximum = object.user_id.default_section_id.invoiced_target |
|||
current = object.sum |
|||
salary = 1000 # TODO |
|||
team_bonus = object.user_id.employee_ids and object.user_id.employee_ids[0].team_bonus or 0.0 |
|||
|
|||
if current < maximum: |
|||
current = maximum |
|||
if current < minimum or minimum == maximum: |
|||
result = 0.0 |
|||
else: |
|||
result = salary * (team_bonus / (12 * 100)) * (current - minimum) / (maximum - minimum) |
|||
result = int(result * 100) / 100.0 |
|||
]]> |
|||
</field> |
|||
</record> |
|||
|
|||
<record model="res.groups" id="group_dashboard_accountant"> |
|||
<field name="name">dashboard_accountant</field> |
|||
<field name="category_id" ref="access_custom.module_category_custom"/> |
|||
<field name="comment">Shows dashboard to accountant</field> |
|||
</record> |
|||
<record model="res.groups" id="group_dashboard_sales_management"> |
|||
<field name="name">dashboard_sales_managment</field> |
|||
<field name="category_id" ref="access_custom.module_category_custom"/> |
|||
<field name="comment">Shows sales management dashboard</field> |
|||
</record> |
|||
<record model="res.groups" id="group_dashboard_management"> |
|||
<field name="name">dashboard_managment</field> |
|||
<field name="category_id" ref="access_custom.module_category_custom"/> |
|||
<field name="comment">Shows management dashboard. It includes: sales management + accountant dashboard + EXTRA</field> |
|||
<field name="implied_ids" eval="[(4, ref('mail_wall_custom.group_dashboard_sales_management')),(4, ref('mail_wall_custom.group_dashboard_accountant'))]"/> |
|||
</record> |
|||
|
|||
<record model="res.groups" id="group_dashboard_sales_person"> |
|||
<field name="name">dashboard_sales_person</field> |
|||
<field name="category_id" ref="access_custom.module_category_custom"/> |
|||
<field name="comment">Shows dashboard to salesperson</field> |
|||
</record> |
|||
|
|||
<record model="res.groups" id="group_dashboard_admin"> |
|||
<field name="name">dashboard_admin</field> |
|||
<field name="category_id" ref="access_custom.module_category_custom"/> |
|||
<field name="comment">Shows admin dashboard</field> |
|||
</record> |
|||
|
|||
<record id="widget_outstanding_invoices" model="mail.wall.widgets.widget"> |
|||
<field name="name">Outstanding sale orders</field> |
|||
<field name="model_id" ref="sale.model_sale_order"/> |
|||
<field name="domain">[('user_id', '=', user.id),('state', 'in', ['progress', 'manual'])]</field> |
|||
<field name="value_field_id" ref="sale.field_sale_order_amount_total"/> |
|||
<field name="value_field_monetary" eval="True"/> |
|||
<field name="content"><![CDATA[${record.name} - <b>${record.partner_id.name}</b>, ${record.date_order}]]></field> |
|||
<field name="group_ids" eval="[(4, ref('mail_wall_custom.group_dashboard_sales_person'), 0)]"/> |
|||
<field name="type">list</field> |
|||
<field name="limit">10</field> |
|||
<field name="active" eval="True"/> |
|||
</record> |
|||
|
|||
<record id="widget_outstanding_invoices_all" model="mail.wall.widgets.widget"> |
|||
<field name="name">Outstanding sale orders [ALL]</field> |
|||
<field name="model_id" ref="sale.model_sale_order"/> |
|||
<field name="domain">[('state', 'in', ['progress', 'manual'])]</field> |
|||
<field name="value_field_id" ref="sale.field_sale_order_amount_total"/> |
|||
<field name="value_field_monetary" eval="True"/> |
|||
<field name="content"><![CDATA[${record.name} - <b>${record.partner_id.name}</b>, ${record.date_order}]]></field> |
|||
<field name="group_ids" eval="[(4, ref('mail_wall_custom.group_dashboard_sales_management'), 0),(4, ref('mail_wall_custom.group_dashboard_accountant'), 0)]"/> |
|||
<field name="type">list</field> |
|||
<field name="limit">10</field> |
|||
<field name="active" eval="True"/> |
|||
</record> |
|||
|
|||
<record id="widget_sent_invoices_all" model="mail.wall.widgets.widget"> |
|||
<field name="name">Sent invoices [ALL]</field> |
|||
<field name="model_id" ref="account.model_account_invoice"/> |
|||
<field name="domain">[('type', 'in', ['out_invoice']),('state','in',['open'])]</field> |
|||
<field name="value_field_id" ref="account.field_account_invoice_amount_total"/> |
|||
<field name="value_field_monetary" eval="True"/> |
|||
<field name="content"><![CDATA[${record.name} - <b>${record.partner_id.name}</b>, ${record.date_invoice}]]></field> |
|||
<field name="group_ids" eval="[(4, ref('mail_wall_custom.group_dashboard_accountant'), 0)]"/> |
|||
<field name="type">list</field> |
|||
<field name="limit">10</field> |
|||
<field name="active" eval="True"/> |
|||
</record> |
|||
|
|||
<record id="widget_received_invoices_all" model="mail.wall.widgets.widget"> |
|||
<field name="name">Received invoices [ALL]</field> |
|||
<field name="model_id" ref="account.model_account_invoice"/> |
|||
<field name="domain">[('type', 'in', ['in_invoice']),('state','in',['open'])]</field> |
|||
<field name="value_field_id" ref="account.field_account_invoice_amount_total"/> |
|||
<field name="value_field_monetary" eval="True"/> |
|||
<field name="content"><![CDATA[${record.name} - <b>${record.partner_id.name}</b>, ${record.date_invoice}]]></field> |
|||
<field name="group_ids" eval="[(4, ref('mail_wall_custom.group_dashboard_accountant'), 0)]"/> |
|||
<field name="type">list</field> |
|||
<field name="limit">10</field> |
|||
<field name="active" eval="True"/> |
|||
</record> |
|||
|
|||
<record id="widget_tasks" model="mail.wall.widgets.widget"> |
|||
<field name="name">Task deadlines</field> |
|||
<field name="model_id" ref="project.model_project_task"/> |
|||
<field name="domain">[('user_id', '=', user.id),('date_deadline','!=',False)]</field> |
|||
<field name="content"><![CDATA[${record.name} - <b>${record.project_id and record.project_id.name or 'NO PROJECT'}</b>, ${record.date_deadline}]]></field> |
|||
<field name="group_ids" eval="[(4, ref('mail_wall_custom.group_dashboard_sales_person'), 0),(4, ref('mail_wall_custom.group_dashboard_sales_management'), 0),(4, ref('mail_wall_custom.group_dashboard_admin'), 0),(4, ref('mail_wall_custom.group_dashboard_accountant'), 0)]"/> |
|||
<field name="agenda" eval="True"/> |
|||
<field name="field_date_id" ref="project.field_project_task_date_deadline"/> |
|||
<field name="type">list</field> |
|||
<field name="limit">10</field> |
|||
<field name="order">priority DESC</field> |
|||
<field name="active" eval="True"/> |
|||
</record> |
|||
<record id="widget_tasks_all" model="mail.wall.widgets.widget"> |
|||
<field name="name">Task deadlines [ALL]</field> |
|||
<field name="model_id" ref="project.model_project_task"/> |
|||
<field name="domain">[('date_deadline','!=',False)]</field> |
|||
<field name="content"><![CDATA[${record.name} - <b>${record.project_id and record.project_id.name or 'NO PROJECT'}</b>, ${record.date_deadline}]]></field> |
|||
<field name="group_ids" eval="[(4, ref('mail_wall_custom.group_dashboard_sales_management'), 0),(4, ref('mail_wall_custom.group_dashboard_accountant'), 0)]"/> |
|||
<field name="agenda" eval="True"/> |
|||
<field name="field_date_id" ref="project.field_project_task_date_deadline"/> |
|||
<field name="type">list</field> |
|||
<field name="limit">10</field> |
|||
<field name="order">priority DESC</field> |
|||
<field name="active" eval="True"/> |
|||
</record> |
|||
|
|||
|
|||
<record id="widget_next_action" model="mail.wall.widgets.widget"> |
|||
<field name="name">Next action</field> |
|||
<field name="model_id" ref="crm.model_crm_lead"/> |
|||
<field name="domain">[('user_id', '=', user.id), '|',('date_action','!=',False),('title_action', '!=', False)]</field> |
|||
<field name="content"><![CDATA[${record.name}: <b>${record.date_action}</b> - ${record.title_action}]]></field> |
|||
<field name="group_ids" eval="[(4, ref('mail_wall_custom.group_dashboard_sales_person'), 0)]"/> |
|||
<field name="agenda" eval="True"/> |
|||
<field name="field_date_id" ref="crm.field_crm_lead_date_action"/> |
|||
<field name="value_field_id" ref="crm.field_crm_lead_planned_revenue"/> |
|||
<field name="value_field_monetary" eval="True"/> |
|||
<field name="type">list</field> |
|||
<field name="limit">10</field> |
|||
<field name="order">priority DESC,probability DESC,planned_revenue DESC</field> |
|||
<field name="active" eval="True"/> |
|||
</record> |
|||
<record id="widget_next_action_all" model="mail.wall.widgets.widget"> |
|||
<field name="name">Next action [ALL]</field> |
|||
<field name="model_id" ref="crm.model_crm_lead"/> |
|||
<field name="domain">['|',('date_action','!=',False),('title_action', '!=', False)]</field> |
|||
<field name="content"><![CDATA[${record.name}: <b>${record.date_action}</b> - ${record.title_action}]]></field> |
|||
<field name="group_ids" eval="[(4, ref('mail_wall_custom.group_dashboard_sales_management'), 0)]"/> |
|||
<field name="agenda" eval="True"/> |
|||
<field name="field_date_id" ref="crm.field_crm_lead_date_action"/> |
|||
<field name="value_field_id" ref="crm.field_crm_lead_planned_revenue"/> |
|||
<field name="value_field_monetary" eval="True"/> |
|||
<field name="type">list</field> |
|||
<field name="limit">10</field> |
|||
<field name="order">priority DESC,probability DESC,planned_revenue DESC</field> |
|||
<field name="active" eval="True"/> |
|||
</record> |
|||
|
|||
<record id="widget_calls" model="mail.wall.widgets.widget"> |
|||
<field name="name">Calls</field> |
|||
<field name="model_id" ref="crm.model_crm_phonecall"/> |
|||
<field name="domain">[('user_id', '=', user.id),('state', 'not in', ['cancel'])]</field> |
|||
<field name="content"><![CDATA[${record.name} - <b>${record.partner_id.name}</b>, ${record.date}]]></field> |
|||
<field name="group_ids" eval="[(4, ref('mail_wall_custom.group_dashboard_sales_person'), 0),(4, ref('mail_wall_custom.group_dashboard_sales_management'), 0),(4, ref('mail_wall_custom.group_dashboard_admin'), 0),(4, ref('mail_wall_custom.group_dashboard_accountant'), 0)]"/> |
|||
<field name="agenda" eval="True"/> |
|||
<field name="field_date_id" ref="crm.field_crm_phonecall_date"/> |
|||
<field name="type">list</field> |
|||
<field name="limit">10</field> |
|||
<field name="order">date DESC</field> |
|||
<field name="active" eval="True"/> |
|||
</record> |
|||
<record id="widget_calls_all" model="mail.wall.widgets.widget"> |
|||
<field name="name">Calls [ALL]</field> |
|||
<field name="model_id" ref="crm.model_crm_phonecall"/> |
|||
<field name="domain">[('state', 'not in', ['cancel'])]</field> |
|||
<field name="content"><![CDATA[${record.name} - <b>${record.partner_id.name}</b>, ${record.date}]]></field> |
|||
<field name="group_ids" eval="[(4, ref('mail_wall_custom.group_dashboard_management'), 0)]"/> |
|||
<field name="agenda" eval="True"/> |
|||
<field name="field_date_id" ref="crm.field_crm_phonecall_date"/> |
|||
<field name="type">list</field> |
|||
<field name="limit">10</field> |
|||
<field name="order">date DESC</field> |
|||
<field name="active" eval="True"/> |
|||
</record> |
|||
|
|||
<record id="widget_meetings" model="mail.wall.widgets.widget"> |
|||
<field name="name">Meetings</field> |
|||
<field name="model_id" ref="calendar.model_calendar_event"/> |
|||
<field name="domain">[('partner_ids', 'in', [user.partner_id.id])]</field> |
|||
<field name="content"><![CDATA[${record.name} - ${record.start_datetime}]]></field> |
|||
<field name="group_ids" eval="[(4, ref('mail_wall_custom.group_dashboard_sales_person'), 0),(4, ref('mail_wall_custom.group_dashboard_sales_management'), 0),(4, ref('mail_wall_custom.group_dashboard_admin'), 0),(4, ref('mail_wall_custom.group_dashboard_accountant'), 0)]"/> |
|||
<field name="agenda" eval="True"/> |
|||
<field name="field_date_id" ref="calendar.field_calendar_event_start_datetime"/> |
|||
<field name="type">list</field> |
|||
<field name="limit">10</field> |
|||
<field name="order">start_datetime DESC</field> |
|||
<field name="active" eval="True"/> |
|||
</record> |
|||
|
|||
<record id="widget_meetings_all" model="mail.wall.widgets.widget"> |
|||
<field name="name">Meetings [ALL]</field> |
|||
<field name="model_id" ref="calendar.model_calendar_event"/> |
|||
<field name="domain">[]</field> |
|||
<field name="content"><![CDATA[${record.name} - ${record.start_datetime}]]></field> |
|||
<field name="group_ids" eval="[(4, ref('mail_wall_custom.group_dashboard_management'), 0)]"/> |
|||
<field name="agenda" eval="True"/> |
|||
<field name="field_date_id" ref="crm.field_crm_phonecall_date"/> |
|||
<field name="agenda" eval="True"/> |
|||
<field name="field_date_id" ref="calendar.field_calendar_event_start_datetime"/> |
|||
<field name="type">list</field> |
|||
<field name="limit">10</field> |
|||
<field name="order">start_datetime DESC</field> |
|||
<field name="active" eval="True"/> |
|||
</record> |
|||
|
|||
<record id="widget_sales_funnel" model="mail.wall.widgets.widget"> |
|||
<field name="name">Conversion Rate</field> |
|||
<field name="model_id" ref="crm.model_crm_lead"/> |
|||
<field name="domain">[('user_id','=', user.id), ('sales_funnel_type', 'in', ['won', 'lost'])]</field> |
|||
<field name="group_ids" eval="[(4, ref('mail_wall_custom.group_dashboard_sales_person'), 0)]"/> |
|||
<field name="type">funnel</field> |
|||
<field name="value_field_id" ref="crm.field_crm_lead_planned_revenue"/> |
|||
<field name="value_field_monetary" eval="True"/> |
|||
<field name="stage_field_id" ref="sale_mediation_custom.field_crm_lead_stage_closed_id"/> |
|||
<field name="won_domain">[('sales_funnel_type', 'in', ['won'])]</field> |
|||
<field name="active" eval="True"/> |
|||
</record> |
|||
<record id="widget_sales_funnel_all" model="mail.wall.widgets.widget"> |
|||
<field name="name">Conversion Rate [ALL]</field> |
|||
<field name="model_id" ref="crm.model_crm_lead"/> |
|||
<field name="domain">[('sales_funnel_type', 'in', ['won', 'lost'])]</field> |
|||
<field name="group_ids" eval="[(4, ref('mail_wall_custom.group_dashboard_sales_management'), 0)]"/> |
|||
<field name="type">funnel</field> |
|||
<field name="value_field_id" ref="crm.field_crm_lead_planned_revenue"/> |
|||
<field name="value_field_monetary" eval="True"/> |
|||
<field name="stage_field_id" ref="sale_mediation_custom.field_crm_lead_stage_closed_id"/> |
|||
<field name="won_domain">[('sales_funnel_type', 'in', ['won'])]</field> |
|||
<field name="active" eval="True"/> |
|||
</record> |
|||
|
|||
<record id="widget_sales_contributed" model="mail.wall.widgets.widget"> |
|||
<field name="name">Sales contributed</field> |
|||
<field name="model_id" ref="sale.model_sale_order"/> |
|||
<field name="domain">[('state', 'in', ['done'])]</field> |
|||
<field name="won_domain">[('user_id','=', user.id), ('state', 'in', ['done'])]</field> |
|||
<field name="group_ids" eval="[(4, ref('mail_wall_custom.group_dashboard_sales_person'), 0)]"/> |
|||
<field name="type">slice</field> |
|||
<field name="value_field_id" ref="sale.field_sale_order_amount_total"/> |
|||
<field name="value_field_monetary" eval="True"/> |
|||
<field name="active" eval="True"/> |
|||
</record> |
|||
|
|||
|
|||
<!-- |
|||
<record id="goal_average_deal_time" model="gamification.goal.definition"> |
|||
<field name="name">Average deal time</field> |
|||
<field name="description"></field> |
|||
<field name="monetary" eval="False"/> |
|||
<field name="suffix"></field> |
|||
<field name="computation_mode"></field> |
|||
<field name="display_mode">progress</field> |
|||
<field name="condition">higher</field> |
|||
<field name="domain"></field> |
|||
<field name="model_id" ref="crm.model_crm_lead"/> |
|||
<field name="field_id" ref="sale_mediation_custom.field_crm_lead_last_action_time"/> |
|||
</record> |
|||
--> |
|||
</data> |
|||
</openerp> |
@ -0,0 +1,13 @@ |
|||
from openerp import api,models,fields |
|||
from openerp.osv import fields as old_fields |
|||
|
|||
class hr_employee(models.Model): |
|||
_inherit = 'hr.employee' |
|||
|
|||
default_section_id = fields.Many2one('crm.case.section', 'Default Sales Team', related='user_id.default_section_id') |
|||
|
|||
personal_comission = fields.Float('Personal comission', help='Personal comission for sales. Value 1.0 is equal 1%') |
|||
|
|||
team_bonus = fields.Float('Team bonus', help='Maximum team bonus (per year). Value 1.0 is equal 1%') |
|||
|
|||
company_bonus = fields.Float('Company bonus', help='Maximum team bonus (per year). Value 1.0 is equal 1%') |
After Width: 100 | Height: 100 | Size: 2.1 KiB |
@ -0,0 +1,20 @@ |
|||
<?xml version="1.0" encoding="UTF-8"?> |
|||
<openerp> |
|||
<data> |
|||
<record id="view_employee_form" model="ir.ui.view"> |
|||
<field name="name">hr.employee.form</field> |
|||
<field name="model">hr.employee</field> |
|||
<field name="inherit_id" ref="hr.view_employee_form"/> |
|||
<field name="arch" type="xml"> |
|||
<xpath expr="//group[@name='active_group']" position="before"> |
|||
<group string="Bonuses"> |
|||
<field name="default_section_id"/> |
|||
<field name="personal_comission"/> |
|||
<field name="team_bonus"/> |
|||
<field name="company_bonus"/> |
|||
</group> |
|||
</xpath> |
|||
</field> |
|||
</record> |
|||
</data> |
|||
</openerp> |
@ -0,0 +1 @@ |
|||
import models |