From d41086e21010188f7a842607e4f855408472c798 Mon Sep 17 00:00:00 2001 From: Ivan Yelizariev Date: Mon, 20 Jan 2014 14:00:07 +0600 Subject: [PATCH 001/281] upload --- __init__.py | 1 + __openerp__.py | 36 ++++++++++++++++++++++++++++++++++++ mail_fix_553.py | 25 +++++++++++++++++++++++++ 3 files changed, 62 insertions(+) create mode 100644 __init__.py create mode 100644 __openerp__.py create mode 100644 mail_fix_553.py diff --git a/__init__.py b/__init__.py new file mode 100644 index 0000000..c978833 --- /dev/null +++ b/__init__.py @@ -0,0 +1 @@ +import mail_fix_553 diff --git a/__openerp__.py b/__openerp__.py new file mode 100644 index 0000000..7a9d6af --- /dev/null +++ b/__openerp__.py @@ -0,0 +1,36 @@ +{ + "name" : "Fix error 553", + "version" : "0.3", + "author" : "Ivan Yelizariev", + "category" : "Mail", + "website" : "https://it-projects.info", + "description": """Update 'Reply-to' field to catchall value in order to fix problem like that: + + 2014-01-18 06:25:56,532 6789 INFO trunk openerp.addons.mail.mail_thread: Routing mail from to admin@MYDOMAIN.com with Message-Id <49131390026345@web16h.yandex.ru>: direct alias match: (u'res.users', 1, {}, 1, browse_record(mail.alias, 1)) +2014-01-18 06:25:57,212 6789 ERROR trunk openerp.addons.base.ir.ir_mail_server: Mail delivery failed via SMTP server 'smtp.yandex.ru'. +SMTPSenderRefused: 553 +5.7.1 Sender address rejected: not owned by auth user. +MYLOGIN@yandex.ru +Traceback (most recent call last): + File "/mnt/files/src/openerp-server/server/openerp/addons/base/ir/ir_mail_server.py", line 465, in send_email + smtp.sendmail(smtp_from, smtp_to_list, message.as_string()) + File "/usr/lib/python2.7/smtplib.py", line 722, in sendmail + raise SMTPSenderRefused(code, resp, from_addr) +SMTPSenderRefused: (553, '5.7.1 Sender address rejected: not owned by auth user.', 'MYLOGIN@yandex.ru') + +2014-01-18 06:25:57,216 6789 ERROR trunk openerp.addons.mail.mail_mail: failed sending mail.mail 2 +Traceback (most recent call last): + File "/mnt/files/src/openerp-server/addons/mail/mail_mail.py", line 284, in send + context=context) + File "/mnt/files/src/openerp-server/server/openerp/addons/base/ir/ir_mail_server.py", line 478, in send_email + raise MailDeliveryException(_("Mail Delivery Failed"), msg) +MailDeliveryException: (u'Mail Delivery Failed', u"Mail delivery failed via SMTP server 'smtp.yandex.ru'.\nSMTPSenderRefused: 553\n5.7.1 Sender address rejected: not owned by auth user.\nMYLOGIN@yandex.ru") +2014-01-18 06:25:57,223 6789 INFO trunk openerp.addons.fetchmail.fetchmail: fetched/processed 1 email(s) on imap server yandex + + """, + "depends" : ["base", "mail"], + #"init_xml" : [], + #"update_xml" : [], + #"active": True, + "installable": True +} diff --git a/mail_fix_553.py b/mail_fix_553.py new file mode 100644 index 0000000..e528dcd --- /dev/null +++ b/mail_fix_553.py @@ -0,0 +1,25 @@ +# -*- coding: utf-8 -*- + +import re +from openerp.osv import osv, fields +from openerp import SUPERUSER_ID + +class mail_mail(osv.Model): + _inherit = "mail.mail" + def send(self, cr, uid, ids, context=None, **kwargs): + catchall_alias = self.pool['ir.config_parameter'].get_param(cr, uid, "mail.catchall.alias", context=context) + catchall_domain = self.pool['ir.config_parameter'].get_param(cr, uid, "mail.catchall.domain", context=context) + + fix_ids = [] + for mail in self.browse(cr, SUPERUSER_ID, ids, context=context): + if re.search('@%s>?\s*$'%catchall_domain, mail.email_from) is None: + print 'fix:', mail.email_from + fix_ids.append(mail.id) + + email_from = '%s@%s' % (catchall_alias, catchall_domain) + print 'new email', email_from + + if fix_ids: + self.write(cr, uid, fix_ids, {'email_from': email_from}, context=context) + + return super(mail_mail, self).send(cr, uid, ids, context=context, **kwargs) From 71a4248a54d73c05d7f31b6a26b38fa457705075 Mon Sep 17 00:00:00 2001 From: Ivan Yelizariev Date: Mon, 20 Jan 2014 14:21:16 +0600 Subject: [PATCH 002/281] rm print --- mail_fix_553.py | 2 -- 1 file changed, 2 deletions(-) diff --git a/mail_fix_553.py b/mail_fix_553.py index e528dcd..378b4cb 100644 --- a/mail_fix_553.py +++ b/mail_fix_553.py @@ -13,11 +13,9 @@ class mail_mail(osv.Model): fix_ids = [] for mail in self.browse(cr, SUPERUSER_ID, ids, context=context): if re.search('@%s>?\s*$'%catchall_domain, mail.email_from) is None: - print 'fix:', mail.email_from fix_ids.append(mail.id) email_from = '%s@%s' % (catchall_alias, catchall_domain) - print 'new email', email_from if fix_ids: self.write(cr, uid, fix_ids, {'email_from': email_from}, context=context) From f460124c9a27b1bf195fd30134bc4b31446e2bc0 Mon Sep 17 00:00:00 2001 From: Ivan Yelizariev Date: Thu, 6 Feb 2014 14:42:15 +0600 Subject: [PATCH 003/281] update description --- __openerp__.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/__openerp__.py b/__openerp__.py index 7a9d6af..b9987d3 100644 --- a/__openerp__.py +++ b/__openerp__.py @@ -6,17 +6,17 @@ "website" : "https://it-projects.info", "description": """Update 'Reply-to' field to catchall value in order to fix problem like that: - 2014-01-18 06:25:56,532 6789 INFO trunk openerp.addons.mail.mail_thread: Routing mail from to admin@MYDOMAIN.com with Message-Id <49131390026345@web16h.yandex.ru>: direct alias match: (u'res.users', 1, {}, 1, browse_record(mail.alias, 1)) + 2014-01-18 06:25:56,532 6789 INFO trunk openerp.addons.mail.mail_thread: Routing mail from to info@MYDOMAIN.com with Message-Id <49131390026345@web16h.yandex.ru>: direct alias match: (u'res.users', 1, {}, 1, browse_record(mail.alias, 1)) 2014-01-18 06:25:57,212 6789 ERROR trunk openerp.addons.base.ir.ir_mail_server: Mail delivery failed via SMTP server 'smtp.yandex.ru'. SMTPSenderRefused: 553 5.7.1 Sender address rejected: not owned by auth user. -MYLOGIN@yandex.ru +user@CUSTOMER.com Traceback (most recent call last): File "/mnt/files/src/openerp-server/server/openerp/addons/base/ir/ir_mail_server.py", line 465, in send_email smtp.sendmail(smtp_from, smtp_to_list, message.as_string()) File "/usr/lib/python2.7/smtplib.py", line 722, in sendmail raise SMTPSenderRefused(code, resp, from_addr) -SMTPSenderRefused: (553, '5.7.1 Sender address rejected: not owned by auth user.', 'MYLOGIN@yandex.ru') +SMTPSenderRefused: (553, '5.7.1 Sender address rejected: not owned by auth user.', 'user@CUSTOMER.com') 2014-01-18 06:25:57,216 6789 ERROR trunk openerp.addons.mail.mail_mail: failed sending mail.mail 2 Traceback (most recent call last): @@ -24,7 +24,7 @@ Traceback (most recent call last): context=context) File "/mnt/files/src/openerp-server/server/openerp/addons/base/ir/ir_mail_server.py", line 478, in send_email raise MailDeliveryException(_("Mail Delivery Failed"), msg) -MailDeliveryException: (u'Mail Delivery Failed', u"Mail delivery failed via SMTP server 'smtp.yandex.ru'.\nSMTPSenderRefused: 553\n5.7.1 Sender address rejected: not owned by auth user.\nMYLOGIN@yandex.ru") +MailDeliveryException: (u'Mail Delivery Failed', u"Mail delivery failed via SMTP server 'smtp.yandex.ru'.\nSMTPSenderRefused: 553\n5.7.1 Sender address rejected: not owned by auth user.\nuser@CUSTOMER.com") 2014-01-18 06:25:57,223 6789 INFO trunk openerp.addons.fetchmail.fetchmail: fetched/processed 1 email(s) on imap server yandex """, From 27eb42fd28e6ae229e8c34da82b75919fe366284 Mon Sep 17 00:00:00 2001 From: Ivan Yelizariev Date: Thu, 6 Feb 2014 16:19:37 +0600 Subject: [PATCH 004/281] more radical solution --- mail_fix_553.py | 109 ++++++++++++++++++++++++++++++++++++++++++++---- 1 file changed, 100 insertions(+), 9 deletions(-) diff --git a/mail_fix_553.py b/mail_fix_553.py index 378b4cb..84a5604 100644 --- a/mail_fix_553.py +++ b/mail_fix_553.py @@ -1,23 +1,114 @@ # -*- coding: utf-8 -*- +import base64 +import logging import re -from openerp.osv import osv, fields +from urllib import urlencode +from urlparse import urljoin + +from openerp import 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.translate import _ + +_logger = logging.getLogger(__name__) class mail_mail(osv.Model): _inherit = "mail.mail" - def send(self, cr, uid, ids, context=None, **kwargs): + def send(self, cr, uid, ids, auto_commit=False, raise_exception=False, context=None): + """ 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 + """ catchall_alias = self.pool['ir.config_parameter'].get_param(cr, uid, "mail.catchall.alias", context=context) catchall_domain = self.pool['ir.config_parameter'].get_param(cr, uid, "mail.catchall.domain", context=context) - fix_ids = [] + correct_email_from = '@%s>?\s*$'%catchall_domain + default_email_from = '%s@%s' % (catchall_alias, catchall_domain) + + ir_mail_server = self.pool.get('ir.mail_server') + for mail in self.browse(cr, SUPERUSER_ID, ids, context=context): - if re.search('@%s>?\s*$'%catchall_domain, mail.email_from) is None: - fix_ids.append(mail.id) + try: + # handle attachments + attachments = [] + for attach in mail.attachment_ids: + attachments.append((attach.datas_fname, base64.b64decode(attach.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) + + # build an RFC2822 email.message.Message object and send it without queuing + res = None + for email in email_list: + email_from = mail.email_from + if re.search(correct_email_from, email_from) is None: + email_from = default_email_from - email_from = '%s@%s' % (catchall_alias, catchall_domain) + msg = ir_mail_server.build_email( + email_from=email_from, + 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) + res = ir_mail_server.send_email(cr, uid, msg, + mail_server_id=mail.mail_server_id.id, + context=context) + + if res: + mail.write({'state': 'sent', 'message_id': res}) + mail_sent = True + else: + mail.write({'state': 'exception'}) + mail_sent = False - if fix_ids: - self.write(cr, uid, fix_ids, {'email_from': email_from}, context=context) + # /!\ 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: + self._postprocess_sent_message(cr, uid, mail, context=context) + except Exception as e: + _logger.exception('failed sending mail.mail %s', mail.id) + mail.write({'state': 'exception'}) + 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 - return super(mail_mail, self).send(cr, uid, ids, context=context, **kwargs) + if auto_commit == True: + cr.commit() + return True From 20270554bb36a85becff543194d0584629289687 Mon Sep 17 00:00:00 2001 From: Ivan Yelizariev Date: Sat, 10 May 2014 18:19:21 +0600 Subject: [PATCH 005/281] upload mail_fix_header_from --- __init__.py | 4 ++++ __openerp__.py | 14 ++++++++++++++ 2 files changed, 18 insertions(+) create mode 100644 __init__.py create mode 100644 __openerp__.py diff --git a/__init__.py b/__init__.py new file mode 100644 index 0000000..83c8941 --- /dev/null +++ b/__init__.py @@ -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*<([^ ,<@]+@[^> ,]+)>') diff --git a/__openerp__.py b/__openerp__.py new file mode 100644 index 0000000..34ca0de --- /dev/null +++ b/__openerp__.py @@ -0,0 +1,14 @@ +{ + "name" : "Fix non-ascii header 'from'", + "version" : "0.3", + "author" : "Ivan Yelizariev", + "category" : "Mail", + "website" : "https://it-projects.info", + "description": """ + """, + "depends" : ["base"], + #"init_xml" : [], + #"update_xml" : [], + #"active": True, + "installable": True +} From 86dfe59fdfbf43656c45b8669c3510d8a396465d Mon Sep 17 00:00:00 2001 From: Ivan Yelizariev Date: Mon, 23 Jun 2014 11:28:35 +0600 Subject: [PATCH 006/281] update Reply-to if it posible --- __openerp__.py | 10 +++++++++- mail_fix_553.py | 5 ++++- 2 files changed, 13 insertions(+), 2 deletions(-) diff --git a/__openerp__.py b/__openerp__.py index b9987d3..e100d20 100644 --- a/__openerp__.py +++ b/__openerp__.py @@ -4,7 +4,15 @@ "author" : "Ivan Yelizariev", "category" : "Mail", "website" : "https://it-projects.info", - "description": """Update 'Reply-to' field to catchall value in order to fix problem like that: + "description": """ +Module uses system parameters: + +* mail.catchall.alias +* mail.catchall.domain + +Module updates reply-to field if it posible to sender alias + +Module updates 'FROM' field to catchall value in order to fix problem like that: 2014-01-18 06:25:56,532 6789 INFO trunk openerp.addons.mail.mail_thread: Routing mail from to info@MYDOMAIN.com with Message-Id <49131390026345@web16h.yandex.ru>: direct alias match: (u'res.users', 1, {}, 1, browse_record(mail.alias, 1)) 2014-01-18 06:25:57,212 6789 ERROR trunk openerp.addons.base.ir.ir_mail_server: Mail delivery failed via SMTP server 'smtp.yandex.ru'. diff --git a/mail_fix_553.py b/mail_fix_553.py index 84a5604..8eb5ce4 100644 --- a/mail_fix_553.py +++ b/mail_fix_553.py @@ -65,8 +65,11 @@ class mail_mail(osv.Model): res = None for email in email_list: email_from = mail.email_from + reply_to = mail.reply_to if re.search(correct_email_from, email_from) is None: email_from = default_email_from + else: + reply_to = email_from msg = ir_mail_server.build_email( email_from=email_from, @@ -75,7 +78,7 @@ class mail_mail(osv.Model): body=email.get('body'), body_alternative=email.get('body_alternative'), email_cc=tools.email_split(mail.email_cc), - reply_to=mail.reply_to, + reply_to=reply_to, attachments=attachments, message_id=mail.message_id, references=mail.references, From 152ced4a89855e7642148c406ca374ece3a58cec Mon Sep 17 00:00:00 2001 From: Ivan Yelizariev Date: Fri, 11 Jul 2014 12:21:20 +0600 Subject: [PATCH 007/281] new module mail_partner_lang --- __init__.py | 1 + __openerp__.py | 13 +++++++++++++ 2 files changed, 14 insertions(+) create mode 100644 __init__.py create mode 100644 __openerp__.py diff --git a/__init__.py b/__init__.py new file mode 100644 index 0000000..bff786c --- /dev/null +++ b/__init__.py @@ -0,0 +1 @@ +import models diff --git a/__openerp__.py b/__openerp__.py new file mode 100644 index 0000000..269dcde --- /dev/null +++ b/__openerp__.py @@ -0,0 +1,13 @@ +{ + 'name' : 'Use partner language in mail', + 'version' : '1.0.0', + 'author' : 'Ivan Yelizariev', + 'category' : 'Mail', + 'website' : 'https://it-projects.info', + + 'depends' : ['mail'], + 'data':[], + 'installable': True, + 'description': ''' + ''', +} From 68ccc18cf06b87f9940716d60e5165a16e8f035c Mon Sep 17 00:00:00 2001 From: Ivan Yelizariev Date: Fri, 11 Jul 2014 12:21:42 +0600 Subject: [PATCH 008/281] copy-paste code from mail_thread.py --- models.py | 79 +++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 79 insertions(+) create mode 100644 models.py diff --git a/models.py b/models.py new file mode 100644 index 0000000..71db677 --- /dev/null +++ b/models.py @@ -0,0 +1,79 @@ +# -*- 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=None): + + 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 = '%s' % message_description + for name, change in tracked_values.items(): + message += '
    • %s: ' % change.get('col_info') + if change.get('old_value'): + message += '%s → ' % change.get('old_value') + message += '%s
' % change.get('new_value') + return message + + if not tracked_fields: + return True + + for browse_record in self.browse(cr, uid, ids, context=context): + initial = initial_values[browse_record.id] + changes = set() + tracked_values = {} + + # generate tracked_values data structure: {'col_name': {col_info, new_value, old_value}} + for col_name, col_info in tracked_fields.items(): + 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 From 818e88386f2a682a36b4f631a6c17ff0b467d3c6 Mon Sep 17 00:00:00 2001 From: Ivan Yelizariev Date: Fri, 11 Jul 2014 12:43:49 +0600 Subject: [PATCH 009/281] update lang context to partner value --- models.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/models.py b/models.py index 71db677..23c4406 100644 --- a/models.py +++ b/models.py @@ -33,6 +33,10 @@ class mail_thread(osv.Model): return True 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 = {} From 776b783247e9bd808eab32184cb1d0b204ee4722 Mon Sep 17 00:00:00 2001 From: Ivan Yelizariev Date: Thu, 17 Jul 2014 20:10:13 +0600 Subject: [PATCH 010/281] [FIX] colulmn name was not translated --- models.py | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/models.py b/models.py index 23c4406..1836778 100644 --- a/models.py +++ b/models.py @@ -5,7 +5,7 @@ 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=None): + 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': @@ -32,6 +32,8 @@ class mail_thread(osv.Model): 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: @@ -41,6 +43,9 @@ class mail_thread(osv.Model): 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] From 63ad78c0e8da2349c4615f3d32600a9631fa424b Mon Sep 17 00:00:00 2001 From: Ivan Yelizariev Date: Mon, 24 Nov 2014 15:53:26 +0200 Subject: [PATCH 011/281] upload mass_mailing_extra --- __init__.py | 1 + __openerp__.py | 20 ++++++++++++++++++++ models.py | 16 ++++++++++++++++ views.xml | 32 ++++++++++++++++++++++++++++++++ 4 files changed, 69 insertions(+) create mode 100644 __init__.py create mode 100644 __openerp__.py create mode 100644 models.py create mode 100644 views.xml diff --git a/__init__.py b/__init__.py new file mode 100644 index 0000000..bff786c --- /dev/null +++ b/__init__.py @@ -0,0 +1 @@ +import models diff --git a/__openerp__.py b/__openerp__.py new file mode 100644 index 0000000..a372310 --- /dev/null +++ b/__openerp__.py @@ -0,0 +1,20 @@ +{ + 'name' : 'Improvements for mass mailing', + 'version' : '1.0.0', + 'author' : 'Ivan Yelizariev', + 'category' : 'Mail', + 'website' : 'https://it-projects.info', + 'description': """ +Modules adds: + +* partners info in mail.mail.statistics tree +* partners info in mail.mail.statistics form + +Tested on 8.0 f8d5a6727d3e8d428d9bef93da7ba6b11f344284 + """, + 'depends' : ['mass_mailing'], + 'data':[ + 'views.xml', + ], + 'installable': True +} diff --git a/models.py b/models.py new file mode 100644 index 0000000..0d8c4d6 --- /dev/null +++ b/models.py @@ -0,0 +1,16 @@ +from openerp import api,models,fields + +class MailMailStats(models.Model): + + _inherit = 'mail.mail.statistics' + + partner_ids = fields.Many2many('res.partner', related='mail_mail_id.recipient_ids', string='Partners') + + @api.one + def _get_partner_ids_text(self): + res = [] + for p in self.partner_ids: + res.append('%s <%s>' % (p.name, p.email)) + self.partner_ids_text = ', '.join(res) + + partner_ids_text = fields.Char('Partners', compute=_get_partner_ids_text) diff --git a/views.xml b/views.xml new file mode 100644 index 0000000..34a67a8 --- /dev/null +++ b/views.xml @@ -0,0 +1,32 @@ + + + + + + mail.mail.statistics.form + mail.mail.statistics + + + + + + + + + + + + + + mail.mail.statistics.tree + mail.mail.statistics + + + + + + + + + + From 4bd3f0502e072a7aa90822819be74da63ef513d9 Mon Sep 17 00:00:00 2001 From: Ivan Yelizariev Date: Tue, 25 Nov 2014 12:34:35 +0200 Subject: [PATCH 012/281] [IMP] get partner from Document ID --- models.py | 24 ++++++++++++++++++------ views.xml | 8 +++++++- 2 files changed, 25 insertions(+), 7 deletions(-) diff --git a/models.py b/models.py index 0d8c4d6..1fc804f 100644 --- a/models.py +++ b/models.py @@ -4,13 +4,25 @@ class MailMailStats(models.Model): _inherit = 'mail.mail.statistics' - partner_ids = fields.Many2many('res.partner', related='mail_mail_id.recipient_ids', string='Partners') + partner_ids = fields.Many2many('res.partner', related='mail_mail_id.recipient_ids', string='Partners (Mail)') @api.one - def _get_partner_ids_text(self): - res = [] + def _get_partner_id(self): + if self.model=='res.partner': + self.partner_id = self.res_id + else: + self.partner_id = None + + partner_id = fields.Many2one('res.partner', compute=_get_partner_id, string='Partner (Document ID)') + + @api.one + def _get_partners(self): + res = {} for p in self.partner_ids: - res.append('%s <%s>' % (p.name, p.email)) - self.partner_ids_text = ', '.join(res) + res[p.id] = p + if self.partner_id and self.partner_id.id not in res: + res[self.partner_id.id] = self.partner_id + self.partners = ', '.join([('%s <%s>' % (p.name, p.email)) for id,p in res.items()]) + - partner_ids_text = fields.Char('Partners', compute=_get_partner_ids_text) + partners = fields.Char('Partners', compute=_get_partners) diff --git a/views.xml b/views.xml index 34a67a8..19362b4 100644 --- a/views.xml +++ b/views.xml @@ -10,6 +10,12 @@ + + + @@ -23,7 +29,7 @@ - + From b51bc60a454433dceef1217a2e1df9b17551417d Mon Sep 17 00:00:00 2001 From: Ivan Yelizariev Date: Tue, 16 Dec 2014 22:40:12 +0200 Subject: [PATCH 013/281] upload mail_delete_odoo_footer --- __init__.py | 1 + __openerp__.py | 15 +++++++++++++++ mail_delete_odoo_footer_models.py | 27 +++++++++++++++++++++++++++ 3 files changed, 43 insertions(+) create mode 100644 __init__.py create mode 100644 __openerp__.py create mode 100644 mail_delete_odoo_footer_models.py diff --git a/__init__.py b/__init__.py new file mode 100644 index 0000000..4431a85 --- /dev/null +++ b/__init__.py @@ -0,0 +1 @@ +import mail_delete_odoo_footer_models diff --git a/__openerp__.py b/__openerp__.py new file mode 100644 index 0000000..76ca110 --- /dev/null +++ b/__openerp__.py @@ -0,0 +1,15 @@ +{ + 'name' : 'Delete "Sent by..." footer', + 'version' : '1.0.0', + 'author' : 'Ivan Yelizariev', + 'category' : 'Sale', + 'website' : 'https://it-projects.info', + 'description': """ + +Tested on 8.0 ab7b5d7 + """, + 'depends' : ['mail'], + 'data':[ + ], + 'installable': True +} diff --git a/mail_delete_odoo_footer_models.py b/mail_delete_odoo_footer_models.py new file mode 100644 index 0000000..584489c --- /dev/null +++ b/mail_delete_odoo_footer_models.py @@ -0,0 +1,27 @@ +from openerp.osv import osv +from openerp import tools, SUPERUSER_ID + +class mail_mail(osv.Model): + _inherit = 'mail.mail' + + def _get_partner_access_link(self, cr, uid, mail, partner=None, context=None): + return None + +class mail_notification(osv.Model): + _inherit = 'mail.notification' + + def get_signature_footer(self, cr, uid, user_id, res_model=None, res_id=None, context=None, user_signature=True): + footer = "" + if not user_id: + return footer + + # add user signature + user = self.pool.get("res.users").browse(cr, SUPERUSER_ID, [user_id], context=context)[0] + if user_signature: + if user.signature: + signature = user.signature + else: + signature = "--
%s" % user.name + footer = tools.append_content_to_html(footer, signature, plaintext=False) + + return footer From dda1ce604155d27a59dba1c6a95899ac496256f5 Mon Sep 17 00:00:00 2001 From: Ivan Yelizariev Date: Sat, 27 Dec 2014 14:09:53 +0200 Subject: [PATCH 014/281] unify doc --- __openerp__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/__openerp__.py b/__openerp__.py index 76ca110..48bfedf 100644 --- a/__openerp__.py +++ b/__openerp__.py @@ -6,7 +6,7 @@ 'website' : 'https://it-projects.info', 'description': """ -Tested on 8.0 ab7b5d7 +Tested on 8.0 ab7b5d7732a7c222a0aea45bd173742acd47242d """, 'depends' : ['mail'], 'data':[ From 507846838f4a40d9fada5fdd5c2527339581394c Mon Sep 17 00:00:00 2001 From: Ivan Yelizariev Date: Thu, 8 Jan 2015 19:06:57 +0200 Subject: [PATCH 015/281] upload "dashboard" modules --- __init__.py | 1 + __openerp__.py | 19 +++ models.py | 220 +++++++++++++++++++++++++++++++++++ security/ir.model.access.csv | 2 + static/src/css/main.css | 23 ++++ static/src/js/main.js | 115 ++++++++++++++++++ static/src/xml/main.xml | 122 +++++++++++++++++++ views.xml | 103 ++++++++++++++++ 8 files changed, 605 insertions(+) create mode 100644 __init__.py create mode 100644 __openerp__.py create mode 100644 models.py create mode 100644 security/ir.model.access.csv create mode 100644 static/src/css/main.css create mode 100644 static/src/js/main.js create mode 100644 static/src/xml/main.xml create mode 100644 views.xml diff --git a/__init__.py b/__init__.py new file mode 100644 index 0000000..bff786c --- /dev/null +++ b/__init__.py @@ -0,0 +1 @@ +import models diff --git a/__openerp__.py b/__openerp__.py new file mode 100644 index 0000000..fbb65c2 --- /dev/null +++ b/__openerp__.py @@ -0,0 +1,19 @@ +{ + 'name' : 'Extra Widgets for mail wall', + 'version' : '1.0.0', + 'author' : 'Ivan Yelizariev', + 'category' : 'Custom', + 'website' : 'https://it-projects.info', + 'description': """ + +Tested on odoo 8.0 ab7b5d7732a7c222a0aea45bd173742acd47242d + """, + 'depends' : ['mail','gamification'], + 'data':[ + 'views.xml', + #'data.xml', + 'security/ir.model.access.csv', + ], + 'qweb': ['static/src/xml/main.xml'], + 'installable': True, +} diff --git a/models.py b/models.py new file mode 100644 index 0000000..8a40d0d --- /dev/null +++ b/models.py @@ -0,0 +1,220 @@ +from openerp.osv import osv,fields as old_fields +from openerp import api, models, fields, tools +from openerp.tools.safe_eval import safe_eval +from openerp.addons.email_template.email_template import mako_template_env +import copy +from openerp.tools.translate import _ + +class mail_wall_widgets_widget(models.Model): + _name = 'mail.wall.widgets.widget' + _order = "sequence, id" + + _columns = { + 'name': old_fields.char('Name', required=True, translate=True), + 'type': old_fields.selection(string='Type', selection=[ + ('list', 'List'), + ('funnel', 'Funnel'), + ('slice', 'Slice'), + #('', ''), + #('', ''), + #('', ''), + #('', ''), + ], help=''' +Slice - use "domain" for total and "won_domain" for target + '''), + + 'description': old_fields.text('Description', translate=True), + 'group_ids': old_fields.many2many('res.groups', relation='mail_wall_widgets_widget_group', column1='widget_id', column2='group_id', string='Groups', help="User groups to show widget"), + 'model_id': old_fields.many2one('ir.model', string='Model', help='The model object for the field to evaluate'), + 'domain': old_fields.char("Filter Domain", help="Domain for filtering records. General rule, not user depending, e.g. [('state', '=', 'done')]. The expression can contain reference to 'user' which is a browse record of the current user if not in batch mode.", required=True), + 'limit': old_fields.integer('Limit', help='Limit count of records to show'), + 'order': old_fields.char('Order', help='Order of records to show'), + 'value_field_id': old_fields.many2one('ir.model.fields', + string='Value field', + help='The field containing the value of record'), + 'stage_field_id': old_fields.many2one('ir.model.fields', + string='Stage field', + help='Field to split records in funnel. It can be selection type or many2one (the later should have "sequence" field)'), + #'stage_field_domain': old_fields.many2one('ir.model.fields', + # string='Stage field domain', + # help='(for many2one stage_field_id) Domain to find stage objects'), + 'won_domain': old_fields.char('Won domain', + help='Domain to find won objects'), + 'field_date_id': old_fields.many2one('ir.model.fields', + string='Date Field', + help='The date to use for the time period evaluated'), + 'start_date': old_fields.date('Start Date'), + 'end_date': old_fields.date('End Date'), # no start and end = always active + 'content': old_fields.char('Line template', help='Mako template to show content'), + 'value_field_monetary': old_fields.boolean('Value is monetary'), + 'cache': old_fields.boolean('Cache'), + 'active': old_fields.boolean('Active'), + 'sequence': old_fields.integer('Sequence', help='Sequence number for ordering'), + } + _defaults = { + 'active': True, + 'cache': False, + 'limit': None, + 'order': None, + } + + @api.one + def get_data(self, user): + + domain = safe_eval(self.domain, {'user': user}) + won_domain = safe_eval(self.won_domain or '[]', {'user': user}) + + field_date_name = self.field_date_id and self.field_date_id.name + if self.start_date and field_date_name: + domain.append((field_date_name, '>=', self.start_date)) + if self.end_date and field_date_name: + domain.append((field_date_name, '<=', self.end_date)) + + res = { + 'name': self.name, + 'type': self.type, + 'model': self.model_id.model, + 'domain': str(domain), + } + obj = self.env[self.model_id.model] + if self.type == 'list': + res.update({ + 'more': self.limit and self.limit < obj.search_count(domain), + 'lines': [], + }) + for r in obj.search(domain, limit=self.limit, order=self.order): + mako = mako_template_env.from_string(tools.ustr(self.content)) + content = mako.render({'record':r}) + r_json = { + 'id': r.id, + #'fields': dict( (f,getattr(r,f)) for f in fields), + 'display_mode': 'progress', + 'state': 'inprogress', + 'completeness': 0, + 'name': content, + 'description': '', + } + if self.value_field_id: + r_json['current'] = getattr(r, self.value_field_id.name) + res['lines'].append(r_json) + elif self.type == 'funnel': + stage_ids = [] # [key] + for group in obj.read_group(domain, [], [self.stage_field_id.name]): + key = group[self.stage_field_id.name] + if isinstance(key, (list, tuple)): + key = key[0] + stage_ids.append(key) + + stages = [] # [{'name':Name, 'id': key}] + if self.stage_field_id.ttype == 'selection': + d = dict (self.stage_field_id.selection) + stages = [ {'id':id, 'name':d[id]} for id in stage_ids ] + else: # many2one + stage_model = self.stage_field_id.relation + for r in self.env[stage_model].browse(stage_ids): + stages.append({'id': r.id, 'name':r.name_get()[0][1]}) + + value_field_name = self.value_field_id.name + for stage in stages: + d = copy.copy(domain) + d.append( (self.stage_field_id.name, '=', stage['id']) ) + result = obj.read_group(d, [value_field_name], []) + stage['closed_value'] = result and result[0][value_field_name] or 0.0 + stage['domain'] = str(d) + + # won value + d = domain + won_domain + result = obj.read_group(domain, [value_field_name], []) + won = {'name': _('Won'), + 'id':'__won__', + 'closed_value': result and result[0][value_field_name] or 0.0 + } + stages.append(won) + cur = 0 + for stage in reversed(stages): + cur += stage['closed_value'] + stage['abs_value'] = cur + total_value = stages[0]['abs_value'] + precision = 0.1 + for s in stages: + s['rel_value'] = round(100*s['abs_value']/total_value/precision)*precision if total_value else 100 + # dummy fields + s['display_mode'] = 'progress' + s['monetary'] = 1 + + res['stages'] = stages + res['won'] = won + res['conversion_rate'] = stages[-1]['rel_value'] + elif self.type == 'slice': + value_field_name = self.value_field_id.name + for f,d in [('total', domain), ('won', won_domain)]: + result = obj.read_group(d, [value_field_name], []) + res[f] = result and result[0][value_field_name] or 0.0 + + res['domain'] = str(domain) + res['won_domain'] = str(won_domain) + + precision = 10 + total_value = res['total'] + res['slice'] = round(100*res['won']/res['total']/precision)*precision if res['total'] else 100 + # dummy fields + res['display_mode'] = 'progress' + res['monetary'] = self.value_field_monetary + return res + +class mail_wall_widgets_cache(models.Model): + _name = 'mail.wall.widgets.cache' + + cache = fields.Text('Cached data') + res_id = fields.Integer('Resource ID') + res_model = fields.Integer('Resource Model') + user_id = fields.Many2one('res.users') + +class res_users(models.Model): + _inherit = 'res.users' + + @api.v7 + def get_serialised_mail_wall_widgets_summary(self, cr, uid, excluded_categories=None, context=None): + return self._get_serialised_mail_wall_widgets_summary(cr, uid, uid, excluded_categories=excluded_categories, context=context)[0] + + @api.one + def _get_serialised_mail_wall_widgets_summary(self, excluded_categories=None): + """ + [ + { + 'id': ..., + 'model': ..., + 'currency': , + 'data': (depend on model) + }, + ] + """ + user = self.env.user + res = [] + model = 'mail.wall.widgets.widget' + domain = [('group_ids', 'in', user.groups_id.ids), ('active', '=', True)] + for widget in self.env[model].search(domain, order='sequence'): + if widget.cache: + #TODO + continue + res.append({ + 'model': model, + 'id': widget.id, + 'currency': user.company_id.currency_id.id, + 'data': widget.get_data(user)[0], + }) + return res + + #def get_challenge_suggestions(self, cr, uid, context=None): + # """Return the list of challenges suggested to the user""" + # challenge_info = [] + # challenge_obj = self.pool.get('mail_wall_widgets.challenge') + # challenge_ids = challenge_obj.search(cr, uid, [('invited_user_ids', 'in', uid), ('state', '=', 'inprogress')], context=context) + # for challenge in challenge_obj.browse(cr, uid, challenge_ids, context=context): + # values = { + # 'id': challenge.id, + # 'name': challenge.name, + # 'description': challenge.description, + # } + # challenge_info.append(values) + # return challenge_info diff --git a/security/ir.model.access.csv b/security/ir.model.access.csv new file mode 100644 index 0000000..17465e5 --- /dev/null +++ b/security/ir.model.access.csv @@ -0,0 +1,2 @@ +id,name,model_id:id,group_id:id,perm_read,perm_write,perm_create,perm_unlink +access_mail_wall_widgets,mail.wall.widgets.widget,model_mail_wall_widgets_widget,,1,1,1,1 \ No newline at end of file diff --git a/static/src/css/main.css b/static/src/css/main.css new file mode 100644 index 0000000..da328f4 --- /dev/null +++ b/static/src/css/main.css @@ -0,0 +1,23 @@ +.openerp .oe_mail_wall .oe_mail_wall_aside .oe_mail_wall_widgets{ + background-color: #ededf6; +} +.oe_open_record:hover, .oe_open_record_list:hover{ + cursor:pointer; +} +.oe_goal_outer_box .more{ + text-align:center; +} + +.openerp .oe_mail_wall .oe_goal .oe_goals_list.funnel .oe_cell.oe_goal_current { + font-size: 150%; + font-weight: bold; + min-width: 65px; + padding: 0 5px; +} +.openerp .oe_mail_wall .oe_goal .oe_goals_list .oe_cell.oe_goal_current span.small { + font-weight: normal; + font-size: 70%; +} +.oe_open_record:hover, .oe_open_record_list_funnel:hover{ + cursor:pointer; +} diff --git a/static/src/js/main.js b/static/src/js/main.js new file mode 100644 index 0000000..de7edf1 --- /dev/null +++ b/static/src/js/main.js @@ -0,0 +1,115 @@ +openerp.mail_wall_widgets = function(instance) { + var QWeb = instance.web.qweb; + var _t = instance.web._t; + + instance.mail_wall_widgets.Sidebar = instance.web.Widget.extend({ + template: 'mail_wall_widgets.UserWallSidebar', + init: function (parent, action) { + var self = this; + this._super(parent, action); + this.deferred = $.Deferred(); + //$(document).off('keydown.klistener'); + this.widget_templates = { + 'mail.wall.widgets.widget': "mail_wall_widgets.Widget" + } + }, + events: { + 'click .oe_open_record': function(event){ + var $t = $(event.currentTarget); + this.do_action({ + 'name': _t('Details'), + 'type': 'ir.actions.act_window', + 'res_model': $t.parent().attr('data-model'), + 'res_id': parseInt($t.attr('data-id')), + 'target': 'current', + 'views': [[false, 'form'],[false, 'list']], + 'domain': $t.parent().attr('data-domain'), + }) + }, + 'click .oe_open_record_list': function(event){ + var $t = $(event.currentTarget); + this.do_action({ + 'name': _t('More...'), + 'type': 'ir.actions.act_window', + 'res_model': $t.parent().attr('data-model'), + 'target': 'current', + 'views': [[false, 'list'],[false, 'form']], + 'domain': $t.parent().attr('data-domain'), + }) + }, + 'click .oe_open_record_list_funnel': function(event){ + var $t = $(event.currentTarget); + this.do_action({ + 'name': _t('More...'), + 'type': 'ir.actions.act_window', + 'res_model': $t.parent().attr('data-model'), + 'target': 'current', + 'views': [[false, 'list'],[false, 'form']], + 'domain': $t.attr('data-domain'), + }) + }, + }, + start: function() { + var self = this; + this._super.apply(this, arguments); + self.get_widgets_info(); + }, + get_widgets_info: function() { + var self = this; + new instance.web.Model('res.users').call('get_serialised_mail_wall_widgets_summary', []).then(function(result) { + if (result.length === 0) { + self.$el.find(".oe_mail_wall_widgets").hide(); + } else { + self.$el.find(".oe_mail_wall_widgets").empty(); + _.each(result, function(item){ + var $item = $(QWeb.render(self.widget_templates[item.model], {info: item})); + self.render_money_fields($item); + //self.render_user_avatars($item); + self.$el.find('.oe_mail_wall_widgets').append($item); + }); + } + }); + }, + render_money_fields: function(item) { + var self = this; + self.dfm = new instance.web.form.DefaultFieldManager(self); + // Generate a FieldMonetary for each .oe_goal_field_monetary + item.find(".oe_goal_field_monetary").each(function() { + var currency_id = parseInt( $(this).attr('data-id'), 10); + money_field = new instance.web.form.FieldMonetary(self.dfm, { + attrs: { + modifiers: '{"readonly": true}' + } + }); + money_field.set('currency', currency_id); + money_field.get_currency_info(); + money_field.set('value', parseInt($(this).text(), 10)); + money_field.replace($(this)); + }); + }, + render_user_avatars: function(item) { + var self = this; + item.find(".oe_user_avatar").each(function() { + var user_id = parseInt( $(this).attr('data-id'), 10); + var url = instance.session.url('/web/binary/image', {model: 'res.users', field: 'image_small', id: user_id}); + $(this).attr("src", url); + }); + } + }); + + instance.web.WebClient.include({ + to_kitten: function() { + this._super(); + new instance.web.Model('mail_wall_widgets.badge').call('check_progress', []); + } + }); + + instance.mail.Wall.include({ + start: function() { + this._super(); + var sidebar = new instance.mail_wall_widgets.Sidebar(this); + sidebar.appendTo($('.oe_mail_wall_aside')); + }, + }); + +}; diff --git a/static/src/xml/main.xml b/static/src/xml/main.xml new file mode 100644 index 0000000..d43a562 --- /dev/null +++ b/static/src/xml/main.xml @@ -0,0 +1,122 @@ + + +
+
+
+
+ +
+
+ e +

+
+ + +
+
+ +
+
+
+
+
+ +
+
+
+
+
+ More... +
+
+
+
+ +
+
+ +
+
+
%
+
+
+ - + +
+
+
+
+ +
+ +
+ +
+
+
%
+
+
+ from + +
+
+
+
+ + +
+
+
+ +
+
+ + Target: + + + Target: <= + + +
+
+
+
+
+ +
+
+
+ +
+
+
+ + +
+ + + + + + + + + + + + + + + + + + +
+
+
+
+
+
+
+ +
diff --git a/views.xml b/views.xml new file mode 100644 index 0000000..70c3a6e --- /dev/null +++ b/views.xml @@ -0,0 +1,103 @@ + + + + + + + + Widgets + mail.wall.widgets.widget + tree,form + +

+ Click to create a Widget. +

+

+ Widget allows to show some information at the inbox +

+
+
+ + + Widget list view + mail.wall.widgets.widget + + + + + + + + + + + + Widget form view + mail.wall.widgets.widget + +
+ + +
+
+
+ + + Widget Search + mail.wall.widgets.widget + + + + + + + + + + + + + + +
+
From ade5442f0dbf3e2b2fc69b5c889a3ec14f49d29d Mon Sep 17 00:00:00 2001 From: Ivan Yelizariev Date: Thu, 8 Jan 2015 19:06:57 +0200 Subject: [PATCH 016/281] upload "dashboard" modules --- __init__.py | 1 + __openerp__.py | 28 +++++++ data.xml | 210 +++++++++++++++++++++++++++++++++++++++++++++++++ models.py | 13 +++ views.xml | 20 +++++ 5 files changed, 272 insertions(+) create mode 100644 __init__.py create mode 100644 __openerp__.py create mode 100644 data.xml create mode 100644 models.py create mode 100644 views.xml diff --git a/__init__.py b/__init__.py new file mode 100644 index 0000000..bff786c --- /dev/null +++ b/__init__.py @@ -0,0 +1 @@ +import models diff --git a/__openerp__.py b/__openerp__.py new file mode 100644 index 0000000..70b8f40 --- /dev/null +++ b/__openerp__.py @@ -0,0 +1,28 @@ +{ + 'name' : 'Custom mail wall', + 'version' : '1.0.0', + 'author' : 'Ivan Yelizariev', + 'category' : 'Custom', + 'website' : 'https://it-projects.info', + 'description': """ + +Tested on Odoo 8.0 ab7b5d7732a7c222a0aea45bd173742acd47242d + """, + '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': True, +} diff --git a/data.xml b/data.xml new file mode 100644 index 0000000..93bf771 --- /dev/null +++ b/data.xml @@ -0,0 +1,210 @@ + + + + + Average payment time + + + days + avg + progress + + + + lower + [('user_id','=', user.id),('state','in', ['done'])] + + + Average deal time + + + days + avg + progress + + + + lower + [('user_id','=', user.id), ('sales_funnel_type', 'in', ['won']), ('date_closed', '!=', False)] + + + Oldest lead + + + days + max + progress + lower + [('user_id','=', user.id), ('sales_funnel_type', '=', 'lead')] + + + + + Oldest Opportunity + + + days + max + progress + lower + [('user_id','=', user.id), '|', ('sales_funnel_type', '=', 'quotation'),('sales_funnel_type', '=', 'negotiation')] + + + + + + Personal comission + + + python + progress + higher + [('state','!=','cancel'),('user_id','=',user.id),('type','=','out_invoice')] + + + + +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 + + + + + Team bonus + Monthly team bonus + + python + progress + higher + [('state','!=','cancel'),('section_id','=',user.default_section_id.id),('type','=','out_invoice')] + + + + + + + + + + dashboard_sales_person + + Shows dashboard to salesperson + + + dashboard_managment + + Shows management dashboard + + + + Outstanding sale orders + + [('user_id', '=', user.id),('state', 'in', ['progress', 'manual'])] + + ${record.partner_id.name}, ${record.date_order}]]> + + list + + + + + Sent invoices + + [('user_id', '=', user.id),('type', 'in', ['out_invoice']),('state','in',['open'])] + + ${record.partner_id.name}, ${record.date_invoice}]]> + + list + + + + + Received invoices + + [('user_id', '=', user.id),('type', 'in', ['in_invoice']),('state','in',['open'])] + + ${record.partner_id.name}, ${record.date_invoice}]]> + + list + + + + + Calls + + [('user_id', '=', user.id),('state', 'not in', ['cancel'])] + ${record.partner_id.name}, ${record.date}]]> + + list + 10 + date DESC + + + + + Mettings + + [('partner_ids', 'in', [user.partner_id.id])] + + + list + 10 + start_datetime DESC + + + + + Conversion Rate + + [('user_id','=', user.id), ('sales_funnel_type', 'in', ['won', 'lost'])] + + funnel + + + [('sales_funnel_type', 'in', ['won'])] + + + + + Sales contributed + + [('state', 'in', ['done'])] + [('user_id','=', user.id), ('state', 'in', ['done'])] + + slice + + + + + + + + + diff --git a/models.py b/models.py new file mode 100644 index 0000000..0d8b14f --- /dev/null +++ b/models.py @@ -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%') diff --git a/views.xml b/views.xml new file mode 100644 index 0000000..3cee23b --- /dev/null +++ b/views.xml @@ -0,0 +1,20 @@ + + + + + hr.employee.form + hr.employee + + + + + + + + + + + + + + From 354012203ebbebd17af536ceb9df344e6195c407 Mon Sep 17 00:00:00 2001 From: Ivan Yelizariev Date: Fri, 9 Jan 2015 12:35:12 +0200 Subject: [PATCH 017/281] [IMP] show count of records --- models.py | 4 +++- static/src/xml/main.xml | 2 +- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/models.py b/models.py index 8a40d0d..4223ad8 100644 --- a/models.py +++ b/models.py @@ -78,8 +78,10 @@ Slice - use "domain" for total and "won_domain" for target } obj = self.env[self.model_id.model] if self.type == 'list': + total_count = obj.search_count(domain) res.update({ - 'more': self.limit and self.limit < obj.search_count(domain), + 'more': self.limit and self.limit < total_count, + 'total_count': total_count, 'lines': [], }) for r in obj.search(domain, limit=self.limit, order=self.order): diff --git a/static/src/xml/main.xml b/static/src/xml/main.xml index d43a562..c4c1f3e 100644 --- a/static/src/xml/main.xml +++ b/static/src/xml/main.xml @@ -8,7 +8,7 @@
e -

+

()

From d0dba4cd6a0c51e9e224ac270e2d701208033488 Mon Sep 17 00:00:00 2001 From: Ivan Yelizariev Date: Fri, 9 Jan 2015 18:10:47 +0200 Subject: [PATCH 018/281] upload mail_wall_menu --- __init__.py | 1 + __openerp__.py | 17 ++++++++++++++++ mail_wall_menu_views.xml | 32 +++++++++++++++++++++++++++++++ models.py | 1 + static/src/css/mail_wall_menu.css | 21 ++++++++++++++++++++ static/src/js/mail_wall_menu.js | 14 ++++++++++++++ 6 files changed, 86 insertions(+) create mode 100644 __init__.py create mode 100644 __openerp__.py create mode 100644 mail_wall_menu_views.xml create mode 100644 models.py create mode 100644 static/src/css/mail_wall_menu.css create mode 100644 static/src/js/mail_wall_menu.js diff --git a/__init__.py b/__init__.py new file mode 100644 index 0000000..bff786c --- /dev/null +++ b/__init__.py @@ -0,0 +1 @@ +import models diff --git a/__openerp__.py b/__openerp__.py new file mode 100644 index 0000000..7b4029b --- /dev/null +++ b/__openerp__.py @@ -0,0 +1,17 @@ +{ + 'name' : 'Menu for widgets at Messaging section', + 'version' : '1.0.0', + 'author' : 'Ivan Yelizariev', + 'category' : 'Sale', + 'website' : 'https://it-projects.info', + 'description': """ +Module creates special menu at Messaging section to show only gamification-like blocks there. + +Tested on Odoo 8.0 ab7b5d7732a7c222a0aea45bd173742acd47242d + """, + 'depends' : ['mail'], + 'data':[ + 'mail_wall_menu_views.xml', + ], + 'installable': True +} diff --git a/mail_wall_menu_views.xml b/mail_wall_menu_views.xml new file mode 100644 index 0000000..5511b6f --- /dev/null +++ b/mail_wall_menu_views.xml @@ -0,0 +1,32 @@ + + + + + + + + Dashboard + mail.wall + { + 'default_model': 'res.users', + 'default_res_id': uid, + 'thread_model': 'res.partner', + 'needaction_menu_ref': ['mail.mail_tomefeeds', 'mail.mail_starfeeds'] + } + + + + + + + + + + diff --git a/models.py b/models.py new file mode 100644 index 0000000..d7d73c9 --- /dev/null +++ b/models.py @@ -0,0 +1 @@ +from openerp import api,models,fields diff --git a/static/src/css/mail_wall_menu.css b/static/src/css/mail_wall_menu.css new file mode 100644 index 0000000..011a409 --- /dev/null +++ b/static/src/css/mail_wall_menu.css @@ -0,0 +1,21 @@ +.openerp .oe_mail_wall .dashboard_only .oe_mail{ + display:none; +} + +.openerp .oe_mail_wall .dashboard_only .oe_mail_wall_aside{ + margin:0; + padding: 16px; + display: block; + position: static; + width:100%; +} + +.openerp .oe_mail_wall .dashboard_only .oe_mail_wall_aside .oe_gamification_challenge_list { + background-color:inherit; +} +.openerp .oe_mail_wall .dashboard_only .oe_goal{ + background-color: #ededf6; + width:280px; + float:left; + margin:5px; +} diff --git a/static/src/js/mail_wall_menu.js b/static/src/js/mail_wall_menu.js new file mode 100644 index 0000000..8d8046a --- /dev/null +++ b/static/src/js/mail_wall_menu.js @@ -0,0 +1,14 @@ +openerp.mail_wall_menu = function(instance){ + var QWeb = instance.web.qweb; + var _t = instance.web._t; + + instance.mail.Widget.include({ + start: function(){ + if (this.action.params.disable_thread){ + $('.oe_view_manager_body').addClass('dashboard_only'); + return; + } + this._super.apply(this, arguments) + } + }) +} \ No newline at end of file From 3949bad8a6ddd38d5edd36ca7c5d0e4800adc2aa Mon Sep 17 00:00:00 2001 From: Ivan Yelizariev Date: Mon, 12 Jan 2015 11:49:57 +0200 Subject: [PATCH 019/281] [FIX] spelling --- data.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/data.xml b/data.xml index 93bf771..fa323da 100644 --- a/data.xml +++ b/data.xml @@ -156,7 +156,7 @@ else: - Mettings + Meetings [('partner_ids', 'in', [user.partner_id.id])] From 2f049b28786405ee592885e22cfcb26b016efc3f Mon Sep 17 00:00:00 2001 From: Ivan Yelizariev Date: Mon, 19 Jan 2015 17:25:20 +0200 Subject: [PATCH 020/281] [IMP] upload mail_fix_empty_body --- __init__.py | 1 + __openerp__.py | 15 +++++++++++++++ models.py | 10 ++++++++++ 3 files changed, 26 insertions(+) create mode 100644 __init__.py create mode 100644 __openerp__.py create mode 100644 models.py diff --git a/__init__.py b/__init__.py new file mode 100644 index 0000000..bff786c --- /dev/null +++ b/__init__.py @@ -0,0 +1 @@ +import models diff --git a/__openerp__.py b/__openerp__.py new file mode 100644 index 0000000..8948816 --- /dev/null +++ b/__openerp__.py @@ -0,0 +1,15 @@ +{ + 'name' : 'Fix "False" in empty email', + 'version' : '1.0.0', + 'author' : 'Ivan Yelizariev', + 'category' : 'Sale', + 'website' : 'https://it-projects.info', + 'description': """ + +Tested on Odoo 8.0 ab7b5d7732a7c222a0aea45bd173742acd47242d + """, + 'depends' : ['mail'], + 'data':[ + ], + 'installable': True +} diff --git a/models.py b/models.py new file mode 100644 index 0000000..0d45e2d --- /dev/null +++ b/models.py @@ -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 From 4835ebce5382f9f58e1f73304ea34311eb49f344 Mon Sep 17 00:00:00 2001 From: Ivan Yelizariev Date: Mon, 19 Jan 2015 20:01:17 +0200 Subject: [PATCH 021/281] [IMP] hide blocks in inbox --- mail_wall_menu_views.xml | 2 +- static/src/css/mail_wall_menu.css | 4 ++++ 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/mail_wall_menu_views.xml b/mail_wall_menu_views.xml index 5511b6f..55496d5 100644 --- a/mail_wall_menu_views.xml +++ b/mail_wall_menu_views.xml @@ -1,7 +1,7 @@ -