From c4476c4ad3d5953c5677127aa542f1ce61816f53 Mon Sep 17 00:00:00 2001 From: "Turkesh Patel (Open ERP)" Date: Mon, 2 Jul 2012 17:46:46 +0530 Subject: [PATCH 001/232] [FIX] plugin:when create the partner from the thunderbird at that time create partner two times. bzr revid: tpa@tinyerp.com-20120702121646-bzom7r2buerwj5n7 --- addons/plugin/plugin_handler.py | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/addons/plugin/plugin_handler.py b/addons/plugin/plugin_handler.py index 700b9648237..2de179f6744 100644 --- a/addons/plugin/plugin_handler.py +++ b/addons/plugin/plugin_handler.py @@ -126,9 +126,10 @@ class plugin_handler(osv.osv_memory): partner_obj = self.pool.get('res.partner') dictcreate = dict(data) if partner_id == 0: - partner_id = partner_obj.create(cr, uid, {'name':dictcreate.get('name')}) - dictcreate['partner_id'] = partner_id - self.pool.get('res.partner').create(cr, uid, dictcreate) + partner_id = partner_obj.create(cr, uid, dictcreate) + else: + dictcreate['parent_id'] = partner_id + partner_obj.create(cr, uid, dictcreate) url = self._make_url(cr, uid, partner_id, 'res.partner') return ('res.partner', partner_id, url) From edfac27fac7696f61d616919ab74fd59dd9488d2 Mon Sep 17 00:00:00 2001 From: "Turkesh Patel (Open ERP)" Date: Mon, 2 Jul 2012 18:29:26 +0530 Subject: [PATCH 002/232] [IMP] improved code bzr revid: tpa@tinyerp.com-20120702125926-r9mtwclvb9mrm5f7 --- addons/plugin/plugin_handler.py | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/addons/plugin/plugin_handler.py b/addons/plugin/plugin_handler.py index 2de179f6744..a026eb61d4b 100644 --- a/addons/plugin/plugin_handler.py +++ b/addons/plugin/plugin_handler.py @@ -125,11 +125,9 @@ class plugin_handler(osv.osv_memory): """ partner_obj = self.pool.get('res.partner') dictcreate = dict(data) - if partner_id == 0: - partner_id = partner_obj.create(cr, uid, dictcreate) - else: + if partner_id: dictcreate['parent_id'] = partner_id - partner_obj.create(cr, uid, dictcreate) + partner_id = partner_obj.create(cr, uid, dictcreate) url = self._make_url(cr, uid, partner_id, 'res.partner') return ('res.partner', partner_id, url) From ee941671d2c391d085f153f6496bc755a900813d Mon Sep 17 00:00:00 2001 From: "Amit Bhavsar (Open ERP)" Date: Thu, 19 Jul 2012 14:15:42 +0530 Subject: [PATCH 003/232] [FIX] account : Improve the code. lp bug: https://launchpad.net/bugs/1026434 fixed bzr revid: amb@tinyerp.com-20120719084542-la8jx5qxiefwoojr --- addons/account/wizard/account_move_journal.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/addons/account/wizard/account_move_journal.py b/addons/account/wizard/account_move_journal.py index 66f634bd59f..a84c7f8eabb 100644 --- a/addons/account/wizard/account_move_journal.py +++ b/addons/account/wizard/account_move_journal.py @@ -99,7 +99,7 @@ class account_move_journal(osv.osv_memory): period = False if period_id: - period = period_pool.browse(cr, uid, [period_id], ['name'])[0]['name'] + period = period_pool.browse(cr, uid, [period_id], context=None)[0]['name'] period_string = _("Period: %s") % tools.ustr(period) open_string = _("Open") From 5c31f52c47c8205703a4da666a01916f9971b522 Mon Sep 17 00:00:00 2001 From: "Cristian Salamea (Gnuthink)" <> Date: Thu, 19 Jul 2012 17:49:45 +0530 Subject: [PATCH 004/232] [FIX]l10n_ec: Change The name of Author lp bug: https://launchpad.net/bugs/1026085 fixed bzr revid: dsi@tinyerp.com-20120719121945-nmad9m98t3q77tet --- addons/l10n_ec/__openerp__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/addons/l10n_ec/__openerp__.py b/addons/l10n_ec/__openerp__.py index 9dd81606036..13d11f03b9d 100644 --- a/addons/l10n_ec/__openerp__.py +++ b/addons/l10n_ec/__openerp__.py @@ -27,7 +27,7 @@ This is the base module to manage the accounting chart for Ecuador in OpenERP. Accounting chart and localization for Ecuador. """, - 'author': 'OpenERP SA', + 'author': 'Gnuthink Co.Ltd.', 'depends': [ 'account', 'base_vat', From dd55d30961d370546267deafefc0ba614e728a0e Mon Sep 17 00:00:00 2001 From: "Amit Bhavsar (Open ERP)" Date: Fri, 20 Jul 2012 11:35:06 +0530 Subject: [PATCH 005/232] [FIX] account : Improve the code lp bug: https://launchpad.net/bugs/1026434 fixed bzr revid: amb@tinyerp.com-20120720060506-clb6z56xa5tqzyy3 --- addons/account/wizard/account_move_journal.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/addons/account/wizard/account_move_journal.py b/addons/account/wizard/account_move_journal.py index a84c7f8eabb..ea2244edb15 100644 --- a/addons/account/wizard/account_move_journal.py +++ b/addons/account/wizard/account_move_journal.py @@ -99,7 +99,7 @@ class account_move_journal(osv.osv_memory): period = False if period_id: - period = period_pool.browse(cr, uid, [period_id], context=None)[0]['name'] + period = period_pool.browse(cr, uid, [period_id], context=context)[0]['name'] period_string = _("Period: %s") % tools.ustr(period) open_string = _("Open") From 7488a675b4d9a8e99cf2b9b25c194e114babcafb Mon Sep 17 00:00:00 2001 From: "Ravish Murari (OpenERP)" Date: Mon, 23 Jul 2012 16:56:17 +0530 Subject: [PATCH 006/232] [FIX] removed rest of the base_group_extended group from addons lp bug: https://launchpad.net/bugs/1027875 fixed bzr revid: rmu@tinyerp.com-20120723112617-h7um6wkj22martms --- addons/account/account_view.xml | 2 +- addons/crm_fundraising/crm_fundraising_view.xml | 2 +- addons/point_of_sale/point_of_sale_view.xml | 2 +- addons/purchase/report/purchase_report_view.xml | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/addons/account/account_view.xml b/addons/account/account_view.xml index 1e1f7b91851..e117d31212a 100644 --- a/addons/account/account_view.xml +++ b/addons/account/account_view.xml @@ -2593,7 +2593,7 @@ action = pool.get('res.config').next(cr, uid, [], context)
diff --git a/addons/crm_fundraising/crm_fundraising_view.xml b/addons/crm_fundraising/crm_fundraising_view.xml index 9e67a4001a7..961ba2d6d3e 100644 --- a/addons/crm_fundraising/crm_fundraising_view.xml +++ b/addons/crm_fundraising/crm_fundraising_view.xml @@ -66,7 +66,7 @@ + +
From 7392ef4283763dcebc00cd5154f909c58d0368bf Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Thibault=20Delavall=C3=A9e?= Date: Wed, 12 Sep 2012 15:37:11 +0200 Subject: [PATCH 151/232] [CLEAN] mail_thread: no need th have its hack about returning the new value after subscribing or unsubscribing. bzr revid: tde@openerp.com-20120912133711-tlt4pqm1cejjq9pv --- addons/mail/mail_thread.py | 20 ++++---------------- 1 file changed, 4 insertions(+), 16 deletions(-) diff --git a/addons/mail/mail_thread.py b/addons/mail/mail_thread.py index c0d6ac90fe6..d798007275f 100644 --- a/addons/mail/mail_thread.py +++ b/addons/mail/mail_thread.py @@ -623,14 +623,8 @@ class mail_thread(osv.AbstractModel): return self.message_subscribe(cr, uid, ids, partner_ids, context=context) def message_subscribe(self, cr, uid, ids, partner_ids, context=None): - """ Add partners to the records followers. - :param partner_ids: a list of partner_ids to subscribe - :param return: new value of followers if read_back key in context - """ - self.write(cr, uid, ids, {'message_follower_ids': [(4, pid) for pid in partner_ids]}, context=context) - if context and context.get('read_back'): - return [follower.id for thread in self.browse(cr, uid, ids, context=context) for follower in thread.message_follower_ids] - return [] + """ Add partners to the records followers. """ + return self.write(cr, uid, ids, {'message_follower_ids': [(4, pid) for pid in partner_ids]}, context=context) def message_unsubscribe_users(self, cr, uid, ids, user_ids=None, context=None): """ Wrapper on message_subscribe, using users. If user_ids is not @@ -640,14 +634,8 @@ class mail_thread(osv.AbstractModel): return self.message_unsubscribe(cr, uid, ids, partner_ids, context=context) def message_unsubscribe(self, cr, uid, ids, partner_ids, context=None): - """ Remove partners from the records followers. - :param partner_ids: a list of partner_ids to unsubscribe - :param return: new value of followers if read_back key in context - """ - self.write(cr, uid, ids, {'message_follower_ids': [(3, pid) for pid in partner_ids]}, context=context) - if context and context.get('read_back'): - return [follower.id for thread in self.browse(cr, uid, ids, context=context) for follower in thread.message_follower_ids] - return [] + """ Remove partners from the records followers. """ + return self.write(cr, uid, ids, {'message_follower_ids': [(3, pid) for pid in partner_ids]}, context=context) #------------------------------------------------------ # Thread state From 01803926245d4d8a81464d9af4559cfcbd77cb43 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Thibault=20Delavall=C3=A9e?= Date: Wed, 12 Sep 2012 15:37:33 +0200 Subject: [PATCH 152/232] [ADD] mail: added wizard in __openerp__. bzr revid: tde@openerp.com-20120912133733-xh484b24txefyhjm --- addons/mail/__openerp__.py | 1 + 1 file changed, 1 insertion(+) diff --git a/addons/mail/__openerp__.py b/addons/mail/__openerp__.py index 99dd6bda6ac..e55baa9ed75 100644 --- a/addons/mail/__openerp__.py +++ b/addons/mail/__openerp__.py @@ -61,6 +61,7 @@ The main features of the module are: 'website': 'http://www.openerp.com', 'depends': ['base', 'base_tools', 'base_setup'], 'data': [ + 'wizard/invite_view.xml', 'wizard/mail_compose_message_view.xml', 'res_config_view.xml', 'mail_message_view.xml', From a16bf464f5f3492f26eb4958e109f10653b22051 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Thibault=20Delavall=C3=A9e?= Date: Wed, 12 Sep 2012 15:38:43 +0200 Subject: [PATCH 153/232] [IMP] mail: added a new behavior when sending notifications. Each notification can now be customized (subject, body); each partner to notify will receive a different ir_email. It is still the same mail_mail as a basis, but specific content can be send to partners (for example, portal links). Updated the tests as well.L bzr revid: tde@openerp.com-20120912133843-lltf7pzder386e2w --- addons/mail/mail_followers.py | 59 ++++++++++++----------- addons/mail/mail_mail.py | 85 +++++++++++++++++++++++----------- addons/mail/tests/test_mail.py | 57 ++++++++++++++++------- 3 files changed, 132 insertions(+), 69 deletions(-) diff --git a/addons/mail/mail_followers.py b/addons/mail/mail_followers.py index 2aba685c4fa..146dc4b257c 100644 --- a/addons/mail/mail_followers.py +++ b/addons/mail/mail_followers.py @@ -84,16 +84,44 @@ class mail_notification(osv.Model): notif_ids = self.search(cr, uid, [('partner_id', '=', partner_id), ('message_id', '=', msg_id)], context=context) return self.write(cr, uid, notif_ids, {'read': True}, context=context) + def get_partners_to_notify(self, cr, uid, partner_ids, message, context=None): + """ Return the list of partners to notify, based on their preferences. + + :param message: browse_record of a mail.message + """ + notify_pids = [] + for partner in self.pool.get('res.partner').browse(cr, uid, partner_ids, context=context): + # Do not send an email to the writer + if partner.user_ids and partner.user_ids[0].id == uid: + continue + # Do not send to partners without email address defined + if not partner.email: + continue + # Partner does not want to receive any emails + if partner.notification_email_send == 'none': + continue + # Partner wants to receive only emails and comments + if partner.notification_email_send == 'comment' and message.type not in ('email', 'comment'): + continue + # Partner wants to receive only emails + if partner.notification_email_send == 'email' and message.type != 'email': + continue + notify_pids.append(partner.id) + return notify_pids + def notify(self, cr, uid, partner_ids, msg_id, context=None): """ Send by email the notification depending on the user preferences """ context = context or {} # mail_noemail (do not send email) or no partner_ids: do not send, return if context.get('mail_noemail') or not partner_ids: return True - - mail_mail = self.pool.get('mail.mail') msg = self.pool.get('mail.message').browse(cr, uid, msg_id, context=context) + notify_partner_ids = self.get_partners_to_notify(cr, uid, partner_ids, msg, context=context) + if not notify_partner_ids: + return True + + mail_mail = self.pool.get('mail.mail') # add signature body_html = msg.body signature = msg.author_id and msg.author_id.user_ids[0].signature or '' @@ -107,27 +135,6 @@ class mail_notification(osv.Model): 'body_html': body_html, 'state': 'outgoing', } - - for partner in self.pool.get('res.partner').browse(cr, uid, partner_ids, context=context): - # Do not send an email to the writer - if partner.user_ids and partner.user_ids[0].id == uid: - continue - # Do not send to partners without email address defined - if not partner.email: - continue - # Partner does not want to receive any emails - if partner.notification_email_send == 'none': - continue - # Partner wants to receive only emails and comments - if partner.notification_email_send == 'comment' and msg.type not in ('email', 'comment'): - continue - # Partner wants to receive only emails - if partner.notification_email_send == 'email' and msg.type != 'email': - continue - if partner.email not in mail_values['email_to']: - mail_values['email_to'].append(partner.email) - if mail_values['email_to']: - mail_values['email_to'] = ', '.join(mail_values['email_to']) - email_notif_id = mail_mail.create(cr, uid, mail_values, context=context) - mail_mail.send(cr, uid, [email_notif_id], context=context) - return True + mail_values['email_to'] = ', '.join(mail_values['email_to']) + email_notif_id = mail_mail.create(cr, uid, mail_values, context=context) + return mail_mail.send(cr, uid, [email_notif_id], notifier_ids=notify_partner_ids, context=context) diff --git a/addons/mail/mail_mail.py b/addons/mail/mail_mail.py index 8d3f3ab6e01..1cb9efa98e1 100644 --- a/addons/mail/mail_mail.py +++ b/addons/mail/mail_mail.py @@ -137,14 +137,46 @@ class mail_mail(osv.Model): mail.unlink() return True - def _send_get_mail_subject(self, cr, uid, mail, force=False, context=None): + def _send_get_mail_subject(self, cr, uid, mail, force=False, partner=None, context=None): """ if void and related document: ' posted on ' - :param mail: mail.mail browse_record """ + + :param force: force the 'Author posted'... subject + :param mail: mail.mail browse_record + :param partner: browse_record of the specific recipient partner + """ if force or (not mail.subject and mail.model and mail.res_id): return '%s posted on %s' % (mail.author_id.name, mail.record_name) return mail.subject - def send(self, cr, uid, ids, auto_commit=False, context=None): + def _send_get_mail_body(self, cr, uid, mail, partner=None, context=None): + """ Return a specific ir_email body. The main purpose of this method + is to be inherited by Portal, to add a link for signing in, in + each notification email a partner receives. + + :param mail: mail.mail browse_record + :param partner: browse_record of the specific recipient partner + """ + return mail.body_html + + def _send_get_ir_email_dict(self, cr, uid, mail, partner=None, context=None): + """ Return a dictionary for specific ir_email values, depending on a + partner, or generic to the whole recipients given by mail.email_to. + + :param mail: mail.mail browse_record + :param partner: browse_record of the specific recipient partner + """ + body = self._send_get_mail_body(cr, uid, mail, partner=partner, context=context) + subject = self._send_get_mail_subject(cr, uid, mail, partner=partner, context=context) + body_alternative = tools.html2plaintext(body) + email_to = [partner.email] if partner else tools.email_split(mail.email_to) + return { + 'body': body, + 'body_alternative': body_alternative, + 'subject': subject, + 'email_to': email_to, + } + + def send(self, cr, uid, ids, auto_commit=False, notifier_ids=None, 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). @@ -160,35 +192,36 @@ class mail_mail(osv.Model): ir_mail_server = self.pool.get('ir.mail_server') for mail in self.browse(cr, uid, ids, context=context): try: - body = mail.body_html - subject = self._send_get_mail_subject(cr, uid, mail, context=context) - # handle attachments attachments = [] for attach in mail.attachment_ids: attachments.append((attach.datas_fname, base64.b64decode(attach.datas))) - - # use only sanitized html and set its plaintexted version as alternative - body_alternative = tools.html2plaintext(body) - content_subtype_alternative = 'plain' + # specific behavior to customize the send email for notified partners + ir_email_list = [] + if notifier_ids: + for partner in self.pool.get('res.partner').browse(cr, uid, notifier_ids, context=context): + ir_email_list.append(self._send_get_ir_email_dict(cr, uid, mail, partner=partner, context=context)) + else: + ir_email_list.append(self._send_get_ir_email_dict(cr, uid, mail, context=context)) # build an RFC2822 email.message.Message object and send it without queuing - msg = ir_mail_server.build_email( - email_from = mail.email_from, - email_to = tools.email_split(mail.email_to), - subject = subject, - body = body, - body_alternative = 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 = content_subtype_alternative) - res = ir_mail_server.send_email(cr, uid, msg, - mail_server_id=mail.mail_server_id.id, context=context) + for ir_email in ir_email_list: + msg = ir_mail_server.build_email( + email_from = mail.email_from, + email_to = ir_email.get('email_to'), + subject = ir_email.get('subject'), + body = ir_email.get('body'), + body_alternative = ir_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') + 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}) else: diff --git a/addons/mail/tests/test_mail.py b/addons/mail/tests/test_mail.py index f45745f31dc..24d7db67b54 100644 --- a/addons/mail/tests/test_mail.py +++ b/addons/mail/tests/test_mail.py @@ -19,6 +19,8 @@ # ############################################################################## +import tools + from openerp.tests import common from openerp.tools.html_sanitize import html_sanitize @@ -29,7 +31,7 @@ Received: by mail1.openerp.com (Postfix, from userid 10002) From: Sylvie Lelitre Subject: {subject} MIME-Version: 1.0 -Content-Type: multipart/alternative; +Content-Type: multipart/alternative; boundary="----=_Part_4200734_24778174.1344608186754" Date: Fri, 10 Aug 2012 14:16:26 +0000 Message-ID: <1198923581.41972151344608186760.JavaMail@agrolait.com> @@ -52,9 +54,9 @@ Content-Transfer-Encoding: quoted-printable =20 =20 - +

Please call me as soon as possible this afternoon!

- +

--
Sylvie

@@ -88,10 +90,19 @@ class test_mail(common.TransactionCase): return True def _mock_build_email(self, *args, **kwargs): - self._build_email_args = args - self._build_email_kwargs = kwargs + self._build_email_args_list.append(args) + self._build_email_kwargs_list.append(kwargs) return self.build_email_real(*args, **kwargs) + def _init_mock_build_email(self): + self._build_email_args_list = [] + self._build_email_kwargs_list = [] + + def _mock_send_get_mail_body(self, *args, **kwargs): + # def _send_get_mail_body(self, cr, uid, mail, partner=None, context=None) + body = tools.append_content_to_html(args[2].body_html, kwargs.get('partner').name if kwargs.get('partner') else 'No specific partner') + return body + def setUp(self): super(test_mail, self).setUp() self.ir_model = self.registry('ir.model') @@ -106,10 +117,14 @@ class test_mail(common.TransactionCase): self.res_partner = self.registry('res.partner') # Install mock SMTP gateway + self._init_mock_build_email() self.build_email_real = self.registry('ir.mail_server').build_email self.registry('ir.mail_server').build_email = self._mock_build_email self.registry('ir.mail_server').send_email = self._mock_smtp_gateway + # Mock _send_get_mail_body to test its functionality without other addons override + self.registry('mail.mail')._send_get_mail_body = self._mock_send_get_mail_body + # groups@.. will cause the creation of new mail groups self.mail_group_model_id = self.ir_model.search(self.cr, self.uid, [('model', '=', 'mail.group')])[0] self.mail_alias.create(self.cr, self.uid, {'alias_name': 'groups', @@ -153,7 +168,7 @@ class test_mail(common.TransactionCase): test_msg_id = '' mail_text = MAIL_TEMPLATE_PLAINTEXT.format(to='groups@example.com', subject='frogs', extra='', msg_id=test_msg_id) self.mail_thread.message_process(cr, uid, None, mail_text) - new_mail = self.mail_message.browse(cr, uid, self.mail_message.search(cr, uid, [('message_id','=',test_msg_id)])[0]) + new_mail = self.mail_message.browse(cr, uid, self.mail_message.search(cr, uid, [('message_id', '=', test_msg_id)])[0]) self.assertEqual(new_mail.body, '\n

\nPlease call me as soon as possible this afternoon!\n\n--\nSylvie\n
\n', 'plaintext mail incorrectly parsed') @@ -274,18 +289,20 @@ class test_mail(common.TransactionCase): _attachments = [('First', 'My first attachment'), ('Second', 'My second attachment')] # CASE1: post comment, body and subject specified + self._init_mock_build_email() msg_id = self.mail_group.message_post(cr, uid, self.group_pigs_id, body=_body1, subject=_subject, type='comment') message = self.mail_message.browse(cr, uid, msg_id) - sent_email = self._build_email_kwargs + sent_emails = self._build_email_kwargs_list # Test: notifications have been deleted self.assertFalse(self.mail_mail.search(cr, uid, [('mail_message_id', '=', msg_id)]), 'mail.mail notifications should have been auto-deleted!') # Test: mail_message: subject is _subject, body is _body1 (no formatting done) self.assertEqual(message.subject, _subject, 'mail.message subject incorrect') self.assertEqual(message.body, _body1, 'mail.message body incorrect') - # Test: sent_email: email send by server: correct subject, body; body_alternative - self.assertEqual(sent_email['subject'], _subject, 'sent_email subject incorrect') - self.assertEqual(sent_email['body'], _mail_body1, 'sent_email body incorrect') - self.assertEqual(sent_email['body_alternative'], _mail_bodyalt1, 'sent_email body_alternative is incorrect') + # Test: sent_email: email send by server: correct subject, body, body_alternative + for sent_email in sent_emails: + self.assertEqual(sent_email['subject'], _subject, 'sent_email subject incorrect') + self.assertEqual(sent_email['body'], _mail_body1 + '\n
Bert Tartopoils
\n', 'sent_email body incorrect') + self.assertEqual(sent_email['body_alternative'], _mail_bodyalt1 + '\nBert Tartopoils', 'sent_email body_alternative is incorrect') # Test: mail_message: partner_ids = group followers message_pids = set([partner.id for partner in message.partner_ids]) test_pids = set([p_a_id, p_b_id, p_c_id]) @@ -295,14 +312,16 @@ class test_mail(common.TransactionCase): notif_pids = set([notif.partner_id.id for notif in self.mail_notification.browse(cr, uid, notif_ids)]) self.assertEqual(notif_pids, test_pids, 'mail.message notification partners incorrect') # Test: sent_email: email_to should contain b@b, not c@c (pref email), not a@a (writer) - self.assertEqual(sent_email['email_to'], ['b@b'], 'sent_email email_to is incorrect') + for sent_email in sent_emails: + self.assertEqual(sent_email['email_to'], ['b@b'], 'sent_email email_to is incorrect') # CASE2: post an email with attachments, parent_id, partner_ids # TESTS: automatic subject, signature in body_html, attachments propagation + self._init_mock_build_email() msg_id2 = self.mail_group.message_post(cr, uid, self.group_pigs_id, body=_body2, type='email', partner_ids=[(6, 0, [p_d_id])], parent_id=msg_id, attachments=_attachments) message = self.mail_message.browse(cr, uid, msg_id2) - sent_email = self._build_email_kwargs + sent_emails = self._build_email_kwargs_list self.assertFalse(self.mail_mail.search(cr, uid, [('mail_message_id', '=', msg_id2)]), 'mail.mail notifications should have been auto-deleted!') # Test: mail_message: subject is False, body is _body2 (no formatting done), parent_id is msg_id @@ -310,9 +329,11 @@ class test_mail(common.TransactionCase): self.assertEqual(message.body, html_sanitize(_body2), 'mail.message body incorrect') self.assertEqual(message.parent_id.id, msg_id, 'mail.message parent_id incorrect') # Test: sent_email: email send by server: correct subject, body, body_alternative - self.assertEqual(sent_email['subject'], _mail_subject, 'sent_email subject incorrect') - self.assertEqual(sent_email['body'], _mail_body2, 'sent_email body incorrect') - self.assertEqual(sent_email['body_alternative'], _mail_bodyalt2, 'sent_email body_alternative incorrect') + self.assertEqual(len(sent_emails), 2, 'sent_email number of sent emails incorrect') + for sent_email in sent_emails: + self.assertEqual(sent_email['subject'], _mail_subject, 'sent_email subject incorrect') + self.assertIn(_mail_body2, sent_email['body'], 'sent_email body incorrect') + self.assertIn(_mail_bodyalt2, sent_email['body_alternative'], 'sent_email body_alternative incorrect') # Test: mail_message: partner_ids = group followers message_pids = set([partner.id for partner in message.partner_ids]) test_pids = set([p_a_id, p_b_id, p_c_id, p_d_id]) @@ -322,7 +343,8 @@ class test_mail(common.TransactionCase): notif_pids = set([notif.partner_id.id for notif in self.mail_notification.browse(cr, uid, notif_ids)]) self.assertEqual(notif_pids, test_pids, 'mail.message notification partners incorrect') # Test: sent_email: email_to should contain b@b, c@c, not a@a (writer) - self.assertEqual(set(sent_email['email_to']), set(['b@b', 'c@c']), 'sent_email email_to incorrect') + for sent_email in sent_emails: + self.assertTrue(set(sent_email['email_to']).issubset(set(['b@b', 'c@c'])), 'sent_email email_to incorrect') # Test: attachments for attach in message.attachment_ids: self.assertEqual(attach.res_model, 'mail.group', 'mail.message attachment res_model incorrect') @@ -458,6 +480,7 @@ class test_mail(common.TransactionCase): # It will be updated as soon as we have fixed specs ! cr, uid = self.cr, self.uid group_pigs = self.mail_group.browse(cr, uid, self.group_pigs_id) + def _compare_structures(struct1, struct2, n=0): # print '%scompare structure' % ('\t' * n) self.assertEqual(len(struct1), len(struct2), 'message_read structure number of childs incorrect') From 30fb693c20745bb2e656732d7a6893b800ae0351 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Thibault=20Delavall=C3=A9e?= Date: Wed, 12 Sep 2012 17:53:00 +0200 Subject: [PATCH 154/232] [IMP] Portal: override of mail_mail to add the signin link into footer of send notifications. bzr revid: tde@openerp.com-20120912155300-8ilg2g24yl7qsjfg --- addons/mail/mail_mail.py | 14 +++++------ addons/portal/__init__.py | 1 + addons/portal/mail_mail.py | 50 ++++++++++++++++++++++++++++++++++++++ 3 files changed, 58 insertions(+), 7 deletions(-) create mode 100644 addons/portal/mail_mail.py diff --git a/addons/mail/mail_mail.py b/addons/mail/mail_mail.py index 1cb9efa98e1..cf86c39f273 100644 --- a/addons/mail/mail_mail.py +++ b/addons/mail/mail_mail.py @@ -137,7 +137,7 @@ class mail_mail(osv.Model): mail.unlink() return True - def _send_get_mail_subject(self, cr, uid, mail, force=False, partner=None, context=None): + def send_get_mail_subject(self, cr, uid, mail, force=False, partner=None, context=None): """ if void and related document: ' posted on ' :param force: force the 'Author posted'... subject @@ -148,7 +148,7 @@ class mail_mail(osv.Model): return '%s posted on %s' % (mail.author_id.name, mail.record_name) return mail.subject - def _send_get_mail_body(self, cr, uid, mail, partner=None, context=None): + def send_get_mail_body(self, cr, uid, mail, partner=None, context=None): """ Return a specific ir_email body. The main purpose of this method is to be inherited by Portal, to add a link for signing in, in each notification email a partner receives. @@ -158,15 +158,15 @@ class mail_mail(osv.Model): """ return mail.body_html - def _send_get_ir_email_dict(self, cr, uid, mail, partner=None, context=None): + def send_get_ir_email_dict(self, cr, uid, mail, partner=None, context=None): """ Return a dictionary for specific ir_email values, depending on a partner, or generic to the whole recipients given by mail.email_to. :param mail: mail.mail browse_record :param partner: browse_record of the specific recipient partner """ - body = self._send_get_mail_body(cr, uid, mail, partner=partner, context=context) - subject = self._send_get_mail_subject(cr, uid, mail, partner=partner, context=context) + body = self.send_get_mail_body(cr, uid, mail, partner=partner, context=context) + subject = self.send_get_mail_subject(cr, uid, mail, partner=partner, context=context) body_alternative = tools.html2plaintext(body) email_to = [partner.email] if partner else tools.email_split(mail.email_to) return { @@ -200,9 +200,9 @@ class mail_mail(osv.Model): ir_email_list = [] if notifier_ids: for partner in self.pool.get('res.partner').browse(cr, uid, notifier_ids, context=context): - ir_email_list.append(self._send_get_ir_email_dict(cr, uid, mail, partner=partner, context=context)) + ir_email_list.append(self.send_get_ir_email_dict(cr, uid, mail, partner=partner, context=context)) else: - ir_email_list.append(self._send_get_ir_email_dict(cr, uid, mail, context=context)) + ir_email_list.append(self.send_get_ir_email_dict(cr, uid, mail, context=context)) # build an RFC2822 email.message.Message object and send it without queuing for ir_email in ir_email_list: diff --git a/addons/portal/__init__.py b/addons/portal/__init__.py index 9c6cc59be7d..2bfc6df0d96 100644 --- a/addons/portal/__init__.py +++ b/addons/portal/__init__.py @@ -20,6 +20,7 @@ ############################################################################## import portal +import mail_mail import wizard diff --git a/addons/portal/mail_mail.py b/addons/portal/mail_mail.py new file mode 100644 index 00000000000..d7f4d05d5ac --- /dev/null +++ b/addons/portal/mail_mail.py @@ -0,0 +1,50 @@ +# -*- coding: utf-8 -*- +############################################################################## +# +# OpenERP, Open Source Management Solution +# Copyright (C) 2004-2011 OpenERP S.A (). +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU Affero General Public License as +# published by the Free Software Foundation, either version 3 of the +# License, or (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Affero General Public License for more details. +# +# You should have received a copy of the GNU Affero General Public License +# along with this program. If not, see . +# +############################################################################## + +import tools +from osv import osv + + +class mail_mail_portal(osv.Model): + """ Update of mail_mail class, to add the signin URL to notifications. + """ + _name = 'mail.mail' + _inherit = ['mail.mail'] + + def _generate_portal_url(self, cr, uid, partner_id, portal_group_id, key, context=None): + """ . """ + base_url = self.pool.get('ir.config_parameter').get_param(cr, uid, 'web.base.url', default='', context=context) + return base_url + '/login?action=signin&partner_id=%s&group=%s&key=%s' % (partner_id, portal_group_id, key) + + def send_get_mail_body(self, cr, uid, mail, partner=None, context=None): + """ Return a specific ir_email body. The main purpose of this method + is to be inherited by Portal, to add a link for signing in, in + each notification email a partner receives. + + :param mail: mail.mail browse_record + :param partner: browse_record of the specific recipient partner + """ + if partner: + url = self._generate_portal_url(cr, uid, partner.id, 12, 1234, context=context) + body = tools.append_content_to_html(mail.body_html, url) + return body + else: + return super(mail_mail_portal, self).send_get_mail_body(cr, uid, mail, partner=partner, context=context) From 918dc35da53232569e67caeb8256bb6e9f9608bf Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Thibault=20Delavall=C3=A9e?= Date: Wed, 12 Sep 2012 17:53:13 +0200 Subject: [PATCH 155/232] [ADD] Portal: added skeleton files for tests. bzr revid: tde@openerp.com-20120912155313-xhx6hqyfr85t0ncw --- addons/portal/tests/__init__.py | 27 ++++++++++++++ addons/portal/tests/test_portal.py | 59 ++++++++++++++++++++++++++++++ 2 files changed, 86 insertions(+) create mode 100644 addons/portal/tests/__init__.py create mode 100644 addons/portal/tests/test_portal.py diff --git a/addons/portal/tests/__init__.py b/addons/portal/tests/__init__.py new file mode 100644 index 00000000000..c4717f80ebf --- /dev/null +++ b/addons/portal/tests/__init__.py @@ -0,0 +1,27 @@ +# -*- coding: utf-8 -*- +############################################################################## +# +# OpenERP, Open Source Business Applications +# Copyright (c) 2012-TODAY OpenERP S.A. +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU Affero General Public License as +# published by the Free Software Foundation, either version 3 of the +# License, or (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Affero General Public License for more details. +# +# You should have received a copy of the GNU Affero General Public License +# along with this program. If not, see . +# +############################################################################## +from . import test_portal + +checks = [ + test_portal, +] + +# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4: \ No newline at end of file diff --git a/addons/portal/tests/test_portal.py b/addons/portal/tests/test_portal.py new file mode 100644 index 00000000000..93b9d220149 --- /dev/null +++ b/addons/portal/tests/test_portal.py @@ -0,0 +1,59 @@ +# -*- coding: utf-8 -*- +############################################################################## +# +# OpenERP, Open Source Business Applications +# Copyright (c) 2012-TODAY OpenERP S.A. +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU Affero General Public License as +# published by the Free Software Foundation, either version 3 of the +# License, or (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Affero General Public License for more details. +# +# You should have received a copy of the GNU Affero General Public License +# along with this program. If not, see . +# +############################################################################## + +from openerp.tests import common + + +class test_portal(common.TransactionCase): + + def _mock_smtp_gateway(self, *args, **kwargs): + return True + + def _mock_build_email(self, *args, **kwargs): + self._build_email_args_list.append(args) + self._build_email_kwargs_list.append(kwargs) + return self.build_email_real(*args, **kwargs) + + def _init_mock_build_email(self): + self._build_email_args_list = [] + self._build_email_kwargs_list = [] + + def setUp(self): + super(test_portal, self).setUp() + self.ir_model = self.registry('ir.model') + self.mail_group = self.registry('mail.group') + self.mail_mail = self.registry('mail.mail') + self.res_users = self.registry('res.users') + self.res_partner = self.registry('res.partner') + + # Install mock SMTP gateway + self._init_mock_build_email() + self.build_email_real = self.registry('ir.mail_server').build_email + self.registry('ir.mail_server').build_email = self._mock_build_email + self.registry('ir.mail_server').send_email = self._mock_smtp_gateway + + # create a 'pigs' group that will be used through the various tests + self.group_pigs_id = self.mail_group.create(self.cr, self.uid, + {'name': 'Pigs', 'description': 'Fans of Pigs, unite !'}) + + def test_00_mail_invite(self): + cr, uid = self.cr, self.uid + print 'cacaprout' From a5bed8994c913064e2d501de4667536a24c6cb1a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fr=C3=A9d=C3=A9ric=20van=20der=20Essen?= Date: Wed, 12 Sep 2012 18:12:29 +0200 Subject: [PATCH 156/232] [IMP] point_of_sale: debug window bzr revid: fva@openerp.com-20120912161229-4lgcgt4xt2cdzceo --- addons/point_of_sale/static/src/css/pos.css | 75 +++++++++++++ addons/point_of_sale/static/src/js/devices.js | 99 +++++++++-------- addons/point_of_sale/static/src/js/models.js | 2 + addons/point_of_sale/static/src/js/screens.js | 13 +-- addons/point_of_sale/static/src/js/widgets.js | 102 +++++++++++++++--- addons/point_of_sale/static/src/xml/pos.xml | 49 +++++++++ 6 files changed, 270 insertions(+), 70 deletions(-) diff --git a/addons/point_of_sale/static/src/css/pos.css b/addons/point_of_sale/static/src/css/pos.css index dbe79703329..a727986bb1c 100644 --- a/addons/point_of_sale/static/src/css/pos.css +++ b/addons/point_of_sale/static/src/css/pos.css @@ -1102,6 +1102,81 @@ .point-of-sale .pos-actionbar .button.rightalign{ float:right; } +/* ********* The Debug Widget ********* */ + +.point-of-sale .debug-widget{ + z-index:100000; + position: absolute; + right: 10px; + top: 10px; + width: 200px; + font-size: 10px; + + background: rgba(0,0,0,0.82); + color: white; + text-shadow: none; + padding-bottom: 10px; + box-shadow: 0px 3px 20px rgba(0,0,0,0.3); + cursor:move; +} +.point-of-sale .debug-widget .toggle{ + position: absolute; + font-size: 16px; + cursor:pointer; + top:0px; + right:0px; + padding:10px; + padding-right:15px; +} +.point-of-sale .debug-widget .content{ + overflow: hidden; +} +.point-of-sale .debug-widget h1{ + background:black; + padding-top: 10px; + padding-left: 10px; + margin-top:0; + margin-bottom:0; +} +.point-of-sale .debug-widget .category{ + background: black; + padding-left: 10px; + margin: 0px; + font-weight: bold; + padding-top:3px; + padding-bottom:3px; +} +.point-of-sale .debug-widget .button{ + padding: 5px; + padding-left: 15px; + display: block; + cursor:pointer; +} +.point-of-sale .debug-widget .button:hover{ + background: rgba(96,21,177,0.45); +} +.point-of-sale .debug-widget input{ + margin-left:10px; + margin-top:7px; +} +.point-of-sale .debug-widget .status{ + padding: 5px; + padding-left: 15px; + display: block; + cursor:default; +} +.point-of-sale .debug-widget .status.on{ + padding: 5px; + padding-left: 15px; + display: block; + background-color: green; +} +.point-of-sale .debug-widget .status.on{ + padding: 5px; + padding-left: 15px; + display: block; + background-color: #6cd11d; +} /* ********* The PopupWidgets ********* */ diff --git a/addons/point_of_sale/static/src/js/devices.js b/addons/point_of_sale/static/src/js/devices.js index 46d11938128..65a28b5366a 100644 --- a/addons/point_of_sale/static/src/js/devices.js +++ b/addons/point_of_sale/static/src/js/devices.js @@ -1,26 +1,6 @@ function openerp_pos_devices(instance,module){ //module is instance.point_of_sale - var debug_devices = new (instance.web.Class.extend({ - active: false, - payment_status: 'waiting_for_payment', - weight: 0, - activate: function(){ - this.active = true; - }, - deactivate: function(){ - this.active = false; - }, - set_weight: function(weight){ this.activate(); this.weight = weight; }, - accept_payment: function(){ this.activate(); this.payment_status = 'payment_accepted'; }, - reject_payment: function(){ this.activate(); this.payment_status = 'payment_rejected'; }, - delay_payment: function(){ this.activate(); this.payment_status = 'waiting_for_payment'; }, - }))(); - - if(jQuery.deparam(jQuery.param.querystring()).debug !== undefined){ - window.debug_devices = debug_devices; - } - // this object interfaces with the local proxy to communicate to the various hardware devices // connected to the Point of Sale. As the communication only goes from the POS to the proxy, // methods are used both to signal an event, and to fetch information. @@ -38,20 +18,34 @@ function openerp_pos_devices(instance,module){ //module is instance.point_of_sal this.connection = new instance.web.JsonRPC(); this.connection.setup(url); + + this.bypass_proxy = false; + this.notifications = {}; }, message : function(name,params,success_callback, error_callback){ success_callback = success_callback || function(){}; error_callback = error_callback || function(){}; - if(jQuery.deparam(jQuery.param.querystring()).debug !== undefined){ console.log('PROXY:',name,params); } - if(!(debug_devices && debug_devices.active)){ - this.connection.rpc('/pos/'+name, params || {}, success_callback, error_callback); + var callbacks = this.notifications[name] || []; + for(var i = 0; i < callbacks.length; i++){ + callbacks[i](params); } + + this.connection.rpc('/pos/'+name, params || {}, success_callback, error_callback); + }, + + // this allows the client to be notified when a proxy call is made. The notification + // callback will be executed with the same arguments as the proxy call + add_notification: function(name, callback){ + if(!this.notifications[name]){ + this.notifications[name] = []; + } + this.notifications[name].push(callback); }, //a product has been scanned and recognized with success @@ -78,12 +72,12 @@ function openerp_pos_devices(instance,module){ //module is instance.point_of_sal //the client is starting to weight weighting_start: function(){ - this.weight = 0; - if(debug_devices){ - debug_devices.weigth = 0; + if(!this.weighting){ + this.weight = 0; + this.weighting = true; + this.bypass_proxy = false; + this.message('weighting_start'); } - this.weighting = true; - this.message('weighting_start'); }, //returns the weight on the scale. @@ -91,22 +85,29 @@ function openerp_pos_devices(instance,module){ //module is instance.point_of_sal // and a weighting_end() weighting_read_kg: function(){ var self = this; - if(debug_devices && debug_devices.active){ - return debug_devices.weight; + if(this.bypass_proxy){ + return this.weight; }else{ this.message('weighting_read_kg',{},function(weight){ - if(self.weighting){ + if(self.weighting && !self.bypass_proxy){ self.weight = weight; } }); - return self.weight; + return this.weight; } }, + // sets a custom weight, ignoring the proxy returned value until the next weighting_end + debug_set_weight: function(kg){ + this.bypass_proxy = true; + this.weight = kg; + }, + // the client has finished weighting products weighting_end: function(){ this.weight = 0; this.weighting = false; + this.bypass_proxy = false; this.message('weighting_end'); }, @@ -116,9 +117,6 @@ function openerp_pos_devices(instance,module){ //module is instance.point_of_sal payment_request: function(price, method, info){ this.paying = true; this.payment_status = 'waiting_for_payment'; - if(debug_devices){ - debug_devices.payment_status = 'waiting_for_payment'; - } this.message('payment_request',{'price':price,'method':method,'info':info}); }, @@ -127,18 +125,30 @@ function openerp_pos_devices(instance,module){ //module is instance.point_of_sal // returns 'waiting_for_payment' | 'payment_accepted' | 'payment_rejected' is_payment_accepted: function(){ var self = this; - if(debug_devices.active){ - return debug_devices.payment_status; + if(this.bypass_proxy){ + this.bypass_proxy = false; + return this.payment_status; }else{ this.message('is_payment_accepted', {}, function(payment_status){ if(self.paying){ self.payment_status = payment_status; } }); - return self.payment_status; + return this.payment_status; } }, + + // override what the proxy says and accept the payment + debug_accept_payment: function(){ + this.bypass_proxy = true; + this.payment_status = 'payment_accepted'; + }, + // override what the proxy says and reject the payment + debug_reject_payment: function(){ + this.bypass_proxy = true; + this.payment_status = 'payment_rejected'; + }, // the client cancels his payment payment_canceled: function(){ this.paying = false; @@ -241,18 +251,6 @@ function openerp_pos_devices(instance,module){ //module is instance.point_of_sal this.price_prefix_set = attributes.price_prefix_set || {'23':''}; this.cashier_prefix_set = attributes.cashier_prefix_set || {'041':''}; this.client_prefix_set = attributes.client_prefix_set || {'042':''}; - - if(jQuery.deparam(jQuery.param.querystring()).debug !== undefined){ - var self = this; - window.simulate_ean = function(ean,strict){ - ean = ean.toString(); - if(!strict){ - ean = self.sanitize_ean(ean); - } - console.log('SIMULATE EAN: ',ean); - self.on_ean(ean); - } - } }, save_callbacks: function(){ @@ -356,7 +354,6 @@ function openerp_pos_devices(instance,module){ //module is instance.point_of_sal value: 0, unit: 'none', }; - console.log('ean',ean); function match_prefix(prefix_set, type){ for(prefix in prefix_set){ diff --git a/addons/point_of_sale/static/src/js/models.js b/addons/point_of_sale/static/src/js/models.js index 49521377ccb..5fbba5088d4 100644 --- a/addons/point_of_sale/static/src/js/models.js +++ b/addons/point_of_sale/static/src/js/models.js @@ -24,6 +24,8 @@ function openerp_pos_models(instance, module){ //module is instance.point_of_sal this.proxy = new module.ProxyDevice(); // used to communicate to the hardware devices via a local proxy this.db = new module.PosLS(); // a database used to store the products and categories this.db.clear('products','categories'); + this.debug = jQuery.deparam(jQuery.param.querystring()).debug !== undefined; //debug mode + // default attributes values. If null, it will be loaded below. this.set({ diff --git a/addons/point_of_sale/static/src/js/screens.js b/addons/point_of_sale/static/src/js/screens.js index 3b580868e6b..d2c79f19928 100644 --- a/addons/point_of_sale/static/src/js/screens.js +++ b/addons/point_of_sale/static/src/js/screens.js @@ -560,21 +560,21 @@ function openerp_pos_screens(instance, module){ //module is instance.point_of_sa //we get the first cashregister marked as self-checkout var selfCheckoutRegisters = []; - for(var i = 0; i < this.pos.get('cashRegisters').models.length; i++){ - var cashregister = this.pos.get('cashRegisters').models[i]; + for(var i = 0; i < self.pos.get('cashRegisters').models.length; i++){ + var cashregister = self.pos.get('cashRegisters').models[i]; if(cashregister.self_checkout_payment_method){ selfCheckoutRegisters.push(cashregister); } } - var cashregister = selfCheckoutRegisters[0] || this.pos.get('cashRegisters').models[0]; + var cashregister = selfCheckoutRegisters[0] || self.pos.get('cashRegisters').models[0]; currentOrder.addPaymentLine(cashregister); self.pos.push_order(currentOrder.exportAsJSON()) currentOrder.destroy(); self.pos.proxy.transaction_end(); self.pos_widget.screen_selector.set_current_screen(self.next_screen); }else if(payment === 'payment_rejected'){ - clearInterval(this.intervalID); + clearInterval(self.intervalID); //TODO show a tryagain thingie ? } },500); @@ -603,13 +603,14 @@ function openerp_pos_screens(instance, module){ //module is instance.point_of_sa show_numpad: false, show_leftpane: false, barcode_product_action: function(ean){ - self.pos.proxy.transaction_start(); + this.pos.proxy.transaction_start(); this._super(ean); }, barcode_client_action: function(ean){ - self.pos.proxy.transaction_start(); + this.pos.proxy.transaction_start(); this._super(ean); + $('.goodbye-message').hide(); this.pos_widget.screen_selector.show_popup('choose-receipt'); }, diff --git a/addons/point_of_sale/static/src/js/widgets.js b/addons/point_of_sale/static/src/js/widgets.js index 2aaf9098329..c8e03ccff08 100644 --- a/addons/point_of_sale/static/src/js/widgets.js +++ b/addons/point_of_sale/static/src/js/widgets.js @@ -694,6 +694,91 @@ function openerp_pos_widgets(instance, module){ //module is instance.point_of_sa }, }); + module.DebugWidget = module.PosBaseWidget.extend({ + template: "DebugWidget", + eans:{ + admin_badge: '0410300000004', + client_badge: '0420100000005', + invalid_ean: '1232456', + soda_33cl: '5449000000996', + oranges_kg: '2100002031410', + lemon_price: '2301000001560', + unknown_product: '9900000000004', + }, + events:[ + 'scan_item_success', + 'scan_item_error_unrecognized', + 'payment_request', + 'open_cashbox', + 'print_receipt', + 'print_pdf_invoice', + ], + minimized: false, + start: function(){ + var self = this; + + this.$el.draggable(); + this.$('.toggle').click(function(){ + var content = self.$('.content'); + var bg = self.$el; + if(!self.minimized){ + content.animate({'height':'0'},200); + }else{ + content.css({'height':'auto'}); + } + self.minimized = !self.minimized; + }); + this.$('.button.accept_payment').click(function(){ + self.pos.proxy.debug_accept_payment(); + }); + this.$('.button.reject_payment').click(function(){ + self.pos.proxy.debug_reject_payment(); + }); + this.$('.button.set_weight').click(function(){ + var kg = Number(self.$('input.weight').val()); + if(!Number.isNaN(kg)){ + self.pos.proxy.debug_set_weight(kg); + } + }); + this.$('.button.custom_ean').click(function(){ + var ean = self.pos.barcode_reader.sanitize_ean(self.$('input.ean').val() || '0'); + self.$('input.ean').val(ean); + self.pos.barcode_reader.on_ean(ean); + }); + _.each(this.eans, function(ean, name){ + self.$('.button.'+name).click(function(){ + self.$('input.ean').val(ean); + self.pos.barcode_reader.on_ean(ean); + }); + }); + _.each(this.events, function(name){ + self.pos.proxy.add_notification(name,function(){ + console.log('Notification:',name); + self.$('.status.'+name).stop().clearQueue().css({'background-color':'#6CD11D'}); + self.$('.status.'+name).animate({'background-color':'black'},2000); + }); + }); + self.pos.proxy.add_notification('help_needed',function(){ + self.$('.status.help_needed').addClass('on'); + }); + self.pos.proxy.add_notification('help_canceled',function(){ + self.$('.status.help_needed').removeClass('on'); + }); + self.pos.proxy.add_notification('transaction_start',function(){ + self.$('.status.transaction').addClass('on'); + }); + self.pos.proxy.add_notification('transaction_end',function(){ + self.$('.status.transaction').removeClass('on'); + }); + self.pos.proxy.add_notification('weighting_start',function(){ + self.$('.status.weighting').addClass('on'); + }); + self.pos.proxy.add_notification('weighting_end',function(){ + self.$('.status.weighting').removeClass('on'); + }); + }, + }); + // The PosWidget is the main widget that contains all other widgets in the PointOfSale. // It is mainly composed of : // - a header, containing the list of orders @@ -718,14 +803,6 @@ function openerp_pos_widgets(instance, module){ //module is instance.point_of_sa this.leftpane_width = '440px'; this.cashier_controls_visible = true; this.image_cache = new module.ImageCache(); // for faster products image display - - /* - //Epileptic mode - setInterval(function(){ - $('body').css({'-webkit-filter':'hue-rotate('+Math.random()*360+'deg)' }); - },100); - */ - }, start: function() { @@ -769,11 +846,6 @@ function openerp_pos_widgets(instance, module){ //module is instance.point_of_sa self.$('.loader').animate({opacity:0},1500,'swing',function(){self.$('.loader').hide();}); self.$('.loader img').hide(); - if(jQuery.deparam(jQuery.param.querystring()).debug !== undefined){ - window.pos = self.pos; - window.pos_widget = self.pos_widget; - } - },function(){ // error when loading models data from the backend self.$('.loader img').hide(); return new instance.web.Model("ir.model.data").get_func("search_read")([['name', '=', 'action_pos_session_opening']], ['res_id']) @@ -898,6 +970,10 @@ function openerp_pos_widgets(instance, module){ //module is instance.point_of_sa default_mode: this.pos.iface_self_checkout ? 'client' : 'cashier', }); + if(this.pos.debug){ + this.debug_widget = new module.DebugWidget(this); + this.debug_widget.appendTo(this.$('#content')); + } }, changed_pending_operations: function () { diff --git a/addons/point_of_sale/static/src/xml/pos.xml b/addons/point_of_sale/static/src/xml/pos.xml index a0b129da2f9..7c8b18a9bc3 100644 --- a/addons/point_of_sale/static/src/xml/pos.xml +++ b/addons/point_of_sale/static/src/xml/pos.xml @@ -414,6 +414,55 @@
+ +
+

Debug Window

+
+
+

Payment Terminal

+ +
    +
  • Accept Payment
  • +
  • Reject Payment
  • +
+

Electronic Scale

+
    +
  • +
  • Set Weight
  • +
+ +

Barcode Scanner

+
    +
  • +
  • Custom Ean13
  • +
  • Admin Badge
  • +
  • Client Badge
  • +
  • Soda 33cl
  • +
  • 3.141Kg Oranges
  • +
  • 1.54€ Lemon
  • +
  • Unknown Product
  • +
  • Invalid Ean
  • +
+ +

Hardware Status

+
    +
  • Help needed
  • +
  • Weighting
  • +
  • In Transaction
  • +
+

Hardware Events

+
    +
  • Scan Item Success
  • +
  • Scan Item Unrecognized
  • +
  • Payment Request
  • +
  • Open Cashbox
  • + + +
+
+
+
+
  • From a1dec2072a5c02cac601f0c1b1590ccb39fa124e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Thibault=20Delavall=C3=A9e?= Date: Wed, 12 Sep 2012 18:13:49 +0200 Subject: [PATCH 157/232] [FIX] Fixed test mocking wrong method. bzr revid: tde@openerp.com-20120912161349-ya5jvplze9ke3rtr --- addons/mail/tests/test_mail.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/addons/mail/tests/test_mail.py b/addons/mail/tests/test_mail.py index f5a0fa9d592..39f69ff0a68 100644 --- a/addons/mail/tests/test_mail.py +++ b/addons/mail/tests/test_mail.py @@ -122,8 +122,8 @@ class test_mail(common.TransactionCase): self.registry('ir.mail_server').build_email = self._mock_build_email self.registry('ir.mail_server').send_email = self._mock_smtp_gateway - # Mock _send_get_mail_body to test its functionality without other addons override - self.registry('mail.mail')._send_get_mail_body = self._mock_send_get_mail_body + # Mock send_get_mail_body to test its functionality without other addons override + self.registry('mail.mail').send_get_mail_body = self._mock_send_get_mail_body # groups@.. will cause the creation of new mail groups self.mail_group_model_id = self.ir_model.search(self.cr, self.uid, [('model', '=', 'mail.group')])[0] From 9dfca35b19d4e2cbc3ddbc74e36a4b5b0703275f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fr=C3=A9d=C3=A9ric=20van=20der=20Essen?= Date: Wed, 12 Sep 2012 18:26:53 +0200 Subject: [PATCH 158/232] [IMP] point_of_sale: put debug window code in the right place bzr revid: fva@openerp.com-20120912162653-fnbvqwg4l51lveal --- addons/point_of_sale/static/src/js/widgets.js | 58 ++++++++++--------- 1 file changed, 30 insertions(+), 28 deletions(-) diff --git a/addons/point_of_sale/static/src/js/widgets.js b/addons/point_of_sale/static/src/js/widgets.js index c8e03ccff08..0ae127e0b1b 100644 --- a/addons/point_of_sale/static/src/js/widgets.js +++ b/addons/point_of_sale/static/src/js/widgets.js @@ -666,34 +666,8 @@ function openerp_pos_widgets(instance, module){ //module is instance.point_of_sa hide: function(){ this.$el.hide(); }, }); - -// ---------- Main Point of Sale Widget ---------- - - // this is used to notify the user that data is being synchronized on the network - module.SynchNotificationWidget = module.PosBaseWidget.extend({ - template: "SynchNotificationWidget", - init: function(parent, options){ - options = options || {}; - this._super(parent, options); - }, - renderElement: function() { - var self = this; - this._super(); - this.$('.oe_pos_synch-notification-button').click(function(){ - self.pos.flush(); - }); - }, - start: function(){ - var self = this; - this.pos.bind('change:nbr_pending_operations', function(){ - self.renderElement(); - }); - }, - get_nbr_pending: function(){ - return this.pos.get('nbr_pending_operations'); - }, - }); - + // The debug widget lets the user control and monitor the hardware and software status + // without the use of the proxy module.DebugWidget = module.PosBaseWidget.extend({ template: "DebugWidget", eans:{ @@ -779,6 +753,34 @@ function openerp_pos_widgets(instance, module){ //module is instance.point_of_sa }, }); +// ---------- Main Point of Sale Widget ---------- + + // this is used to notify the user that data is being synchronized on the network + module.SynchNotificationWidget = module.PosBaseWidget.extend({ + template: "SynchNotificationWidget", + init: function(parent, options){ + options = options || {}; + this._super(parent, options); + }, + renderElement: function() { + var self = this; + this._super(); + this.$('.oe_pos_synch-notification-button').click(function(){ + self.pos.flush(); + }); + }, + start: function(){ + var self = this; + this.pos.bind('change:nbr_pending_operations', function(){ + self.renderElement(); + }); + }, + get_nbr_pending: function(){ + return this.pos.get('nbr_pending_operations'); + }, + }); + + // The PosWidget is the main widget that contains all other widgets in the PointOfSale. // It is mainly composed of : // - a header, containing the list of orders From e7a24e7350efc7f38ad32df604cfdb813cfe452f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Thibault=20Delavall=C3=A9e?= Date: Wed, 12 Sep 2012 18:36:18 +0200 Subject: [PATCH 159/232] [IMP] mail: invite wizard: added default message; added email send to the newly added followers. bzr revid: tde@openerp.com-20120912163618-rp67taqr24zp2yei --- addons/mail/wizard/invite.py | 24 +++++++++++++++++++++++- 1 file changed, 23 insertions(+), 1 deletion(-) diff --git a/addons/mail/wizard/invite.py b/addons/mail/wizard/invite.py index 71ae23304a8..4644dc1e55b 100644 --- a/addons/mail/wizard/invite.py +++ b/addons/mail/wizard/invite.py @@ -21,6 +21,7 @@ from osv import osv from osv import fields +from tools.translate import _ class invite_wizard(osv.osv_memory): @@ -38,6 +39,13 @@ class invite_wizard(osv.osv_memory): 'message': fields.text('Message'), } + _defaults = { + 'message': lambda s, *a, **k: s._get_default_message(*a, **k), + } + + def _get_default_message(self, cr, uid, context=None): + return _('You have been invited to follow a new document.') + def onchange_partner_ids(self, cr, uid, ids, value, context=None): """ onchange_partner_ids (value format: [[6, 0, [3, 4]]]). The basic purpose of this method is to check that destination partners @@ -52,5 +60,19 @@ class invite_wizard(osv.osv_memory): def add_followers(self, cr, uid, ids, context=None): for wizard in self.browse(cr, uid, ids, context=context): model_obj = self.pool.get(wizard.res_model) - model_obj.message_subscribe(cr, uid, [wizard.res_id], [p.id for p in wizard.partner_ids], context=context) + document = model_obj.browse(cr, uid, wizard.res_id, context=context) + + # filter partner_ids to get the new followers, to avoid sending email to already following partners + new_follower_ids = [p.id for p in wizard.partner_ids if p.id not in document.message_follower_ids] + model_obj.message_subscribe(cr, uid, [wizard.res_id], new_follower_ids, context=context) + + # send an email + if wizard.message: + for follower_id in new_follower_ids: + mail_mail = self.pool.get('mail.mail') + mail_id = mail_mail.create(cr, uid, { + 'body_html': '
    %s
    ' % wizard.message, + 'auto_delete': True, + }, context=context) + mail_mail.send(cr, uid, [mail_id], notifier_ids=[follower_id], context=context) return {'type': 'ir.actions.act_window_close'} From 7a95639544c8a2f698e73289cab2ec86e12d5824 Mon Sep 17 00:00:00 2001 From: "Foram Katharotiya (OpenERP)" Date: Thu, 13 Sep 2012 11:26:33 +0530 Subject: [PATCH 160/232] [IMP] improve code of supply_method in project_mrp bzr revid: fka@tinyerp.com-20120913055633-u2yybpkevxj48jvd --- addons/mrp/mrp_view.xml | 2 +- addons/project_mrp/project_mrp_view.xml | 10 +++++----- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/addons/mrp/mrp_view.xml b/addons/mrp/mrp_view.xml index 8121f6e84d1..34f3bd3909e 100644 --- a/addons/mrp/mrp_view.xml +++ b/addons/mrp/mrp_view.xml @@ -978,7 +978,7 @@ - + product.normal.form.mrp.inherit product.product diff --git a/addons/project_mrp/project_mrp_view.xml b/addons/project_mrp/project_mrp_view.xml index fdfefd10e30..ca1e1f893e7 100644 --- a/addons/project_mrp/project_mrp_view.xml +++ b/addons/project_mrp/project_mrp_view.xml @@ -31,14 +31,14 @@ - - product.form.view + + product.normal.form.project_mrp.inherit product.product - - - + + + From 989ce9204b9d6ade19a609805b7b2ba22c5d5b8b Mon Sep 17 00:00:00 2001 From: "Harry (OpenERP)" Date: Thu, 13 Sep 2012 12:47:24 +0530 Subject: [PATCH 161/232] [FIX] plugin: should call message_process instead of message_new for new message bzr revid: hmo@tinyerp.com-20120913071724-re6w0fy6jspldsjh --- addons/mail/mail_thread.py | 2 +- addons/plugin/plugin_handler.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/addons/mail/mail_thread.py b/addons/mail/mail_thread.py index c0d6ac90fe6..651b064d7aa 100644 --- a/addons/mail/mail_thread.py +++ b/addons/mail/mail_thread.py @@ -378,7 +378,7 @@ class mail_thread(osv.AbstractModel): else: thread_id = model_pool.message_new(cr, user_id, msg, custom_values, context=context) self.message_post(cr, uid, [thread_id], context=context, **msg) - return True + return thread_id def message_new(self, cr, uid, msg_dict, custom_values=None, context=None): """Called by ``message_process`` when a new message is received diff --git a/addons/plugin/plugin_handler.py b/addons/plugin/plugin_handler.py index 8f0fdc1ad8e..dee2cac117d 100644 --- a/addons/plugin/plugin_handler.py +++ b/addons/plugin/plugin_handler.py @@ -103,7 +103,7 @@ class plugin_handler(osv.osv_memory): if model == 'res.partner': notify = 'User the Partner button to create a new partner' else: - res_id = model_obj.message_new(cr, uid, msg) + res_id = model_obj.message_process(cr, uid, model, email) notify = "Mail successfully pushed, a new %s has been created " % model else: model_obj.message_post(cr, uid, [res_id], From a8d177d97acf7abe40a8530dd7734124f05dfbea Mon Sep 17 00:00:00 2001 From: "Harry (OpenERP)" Date: Thu, 13 Sep 2012 13:16:31 +0530 Subject: [PATCH 162/232] [FIX] plugin: fix issue in document_get method bzr revid: hmo@tinyerp.com-20120913074631-vbjgdywt03x8eqbp --- addons/plugin/plugin_handler.py | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/addons/plugin/plugin_handler.py b/addons/plugin/plugin_handler.py index dee2cac117d..3e2f09d8789 100644 --- a/addons/plugin/plugin_handler.py +++ b/addons/plugin/plugin_handler.py @@ -45,13 +45,13 @@ class plugin_handler(osv.osv_memory): url = "" name = "" msg = self.pool.get('mail.thread').message_parse(cr, uid, email) - references = [msg.get('message-id')] - refs = msg.get('references',False) - if refs: - references.extend(refs.split()) - msg_ids = mail_message_obj.search(cr, uid, [('message_id','in',references)]) - if msg_ids: - msg = mail_message_obj.browse(cr, uid, msg_ids[0]) + msg_id = msg.get('parent_id', False) + message_id = msg.get('message_id') + if not msg_id: + msg_ids = mail_message_obj.search(cr, uid, ('message_id','=', message_id)) + msg_id = len(msg_ids) and msg_ids[0] or False + if msg_id: + msg = mail_message_obj.browse(cr, uid, msg_id) res_id = msg.res_id model = msg.model url = self._make_url(cr, uid, res_id, model) @@ -80,7 +80,7 @@ class plugin_handler(osv.osv_memory): @return : the result of name_search a list of tuple [(id, 'name')] """ - return self.pool.get(model).name_search(cr,uid,name) + return self.pool.get(model).name_search(cr, uid, name) def push_message(self, cr, uid, model, email, res_id=0): """ @@ -163,6 +163,6 @@ class plugin_handler(osv.osv_memory): attach_ids.append(ir_attachment_obj.create(cr, uid, vals)) mail_ids = mail_message.search(cr, uid, [('message_id','=',message_id),('res_id','=',res_id),('model','=',model)]) if mail_ids: - ids = mail_message.write(cr, uid,mail_ids[0],{ 'attachment_ids': [(6, 0, attach_ids)],'body':body,'body_html':body_html}) + ids = mail_message.write(cr, uid, mail_ids[0], { 'attachment_ids': [(6, 0, attach_ids)],'body':body,'body_html':body_html}) url = self._make_url(cr, uid, res_id, model) return (model, res_id, url) From fab90cb7d47f2173bb27616bf15094ad92118e2f Mon Sep 17 00:00:00 2001 From: "Harry (OpenERP)" Date: Thu, 13 Sep 2012 13:22:23 +0530 Subject: [PATCH 163/232] [FIX] plugin: fix issue bzr revid: hmo@tinyerp.com-20120913075223-5s0eeg4a7dlqat1m --- addons/plugin/plugin_handler.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/addons/plugin/plugin_handler.py b/addons/plugin/plugin_handler.py index 3e2f09d8789..983f3d60dfc 100644 --- a/addons/plugin/plugin_handler.py +++ b/addons/plugin/plugin_handler.py @@ -48,7 +48,7 @@ class plugin_handler(osv.osv_memory): msg_id = msg.get('parent_id', False) message_id = msg.get('message_id') if not msg_id: - msg_ids = mail_message_obj.search(cr, uid, ('message_id','=', message_id)) + msg_ids = mail_message_obj.search(cr, uid, [('message_id','=', message_id)]) msg_id = len(msg_ids) and msg_ids[0] or False if msg_id: msg = mail_message_obj.browse(cr, uid, msg_id) From 21a6ba351922bd25a75a7e48e8448984d8c87458 Mon Sep 17 00:00:00 2001 From: "Harry (OpenERP)" Date: Thu, 13 Sep 2012 13:55:35 +0530 Subject: [PATCH 164/232] [FIX] plugin: first search message_id than search parent message if not found bzr revid: hmo@tinyerp.com-20120913082535-syibk95l0kmz60nw --- addons/plugin/plugin_handler.py | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/addons/plugin/plugin_handler.py b/addons/plugin/plugin_handler.py index 983f3d60dfc..42754951317 100644 --- a/addons/plugin/plugin_handler.py +++ b/addons/plugin/plugin_handler.py @@ -45,11 +45,14 @@ class plugin_handler(osv.osv_memory): url = "" name = "" msg = self.pool.get('mail.thread').message_parse(cr, uid, email) - msg_id = msg.get('parent_id', False) + parent_id = msg.get('parent_id', False) message_id = msg.get('message_id') - if not msg_id: + msg_id = False + if message_id: msg_ids = mail_message_obj.search(cr, uid, [('message_id','=', message_id)]) msg_id = len(msg_ids) and msg_ids[0] or False + if not msg_id and parent_id: + msg_id = parent_id if msg_id: msg = mail_message_obj.browse(cr, uid, msg_id) res_id = msg.res_id From 13583b36d3cd94cf1687ddd82d2b4bfe7a216dfc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Thibault=20Delavall=C3=A9e?= Date: Thu, 13 Sep 2012 10:41:19 +0200 Subject: [PATCH 165/232] [FIX] mail, portal: fixed tests to remove mockups at tearDown. Otherwise, when using tests, no email could be send because of the mockups installed during tests. bzr revid: tde@openerp.com-20120913084119-6ligp3o864vtzlmz --- addons/mail/tests/test_mail.py | 22 +++++++++++++++------- addons/portal/tests/test_portal.py | 11 +++++++++-- 2 files changed, 24 insertions(+), 9 deletions(-) diff --git a/addons/mail/tests/test_mail.py b/addons/mail/tests/test_mail.py index 39f69ff0a68..bf9520f0e68 100644 --- a/addons/mail/tests/test_mail.py +++ b/addons/mail/tests/test_mail.py @@ -89,15 +89,15 @@ class test_mail(common.TransactionCase): def _mock_smtp_gateway(self, *args, **kwargs): return True - def _mock_build_email(self, *args, **kwargs): - self._build_email_args_list.append(args) - self._build_email_kwargs_list.append(kwargs) - return self.build_email_real(*args, **kwargs) - def _init_mock_build_email(self): self._build_email_args_list = [] self._build_email_kwargs_list = [] + def _mock_build_email(self, *args, **kwargs): + self._build_email_args_list.append(args) + self._build_email_kwargs_list.append(kwargs) + return self._build_email(*args, **kwargs) + def _mock_send_get_mail_body(self, *args, **kwargs): # def _send_get_mail_body(self, cr, uid, mail, partner=None, context=None) body = tools.append_content_to_html(args[2].body_html, kwargs.get('partner').name if kwargs.get('partner') else 'No specific partner') @@ -118,11 +118,12 @@ class test_mail(common.TransactionCase): # Install mock SMTP gateway self._init_mock_build_email() - self.build_email_real = self.registry('ir.mail_server').build_email + self._build_email = self.registry('ir.mail_server').build_email self.registry('ir.mail_server').build_email = self._mock_build_email + self._send_email = self.registry('ir.mail_server').send_email self.registry('ir.mail_server').send_email = self._mock_smtp_gateway - # Mock send_get_mail_body to test its functionality without other addons override + self._send_get_mail_body = self.registry('mail.mail').send_get_mail_body self.registry('mail.mail').send_get_mail_body = self._mock_send_get_mail_body # groups@.. will cause the creation of new mail groups @@ -133,6 +134,13 @@ class test_mail(common.TransactionCase): self.group_pigs_id = self.mail_group.create(self.cr, self.uid, {'name': 'Pigs', 'description': 'Fans of Pigs, unite !'}) + def tearDown(self): + # Remove mocks + self.registry('ir.mail_server').build_email = self._build_email + self.registry('ir.mail_server').send_email = self._send_email + self.registry('mail.mail').send_get_mail_body = self._send_get_mail_body + super(test_mail, self).tearDown() + def test_00_message_process(self): cr, uid = self.cr, self.uid # Incoming mail creates a new mail_group "frogs" diff --git a/addons/portal/tests/test_portal.py b/addons/portal/tests/test_portal.py index 93b9d220149..b98614de5e9 100644 --- a/addons/portal/tests/test_portal.py +++ b/addons/portal/tests/test_portal.py @@ -30,7 +30,7 @@ class test_portal(common.TransactionCase): def _mock_build_email(self, *args, **kwargs): self._build_email_args_list.append(args) self._build_email_kwargs_list.append(kwargs) - return self.build_email_real(*args, **kwargs) + return self._build_email(*args, **kwargs) def _init_mock_build_email(self): self._build_email_args_list = [] @@ -46,14 +46,21 @@ class test_portal(common.TransactionCase): # Install mock SMTP gateway self._init_mock_build_email() - self.build_email_real = self.registry('ir.mail_server').build_email + self._build_email = self.registry('ir.mail_server').build_email self.registry('ir.mail_server').build_email = self._mock_build_email + self._send_email = self.registry('ir.mail_server').send_email self.registry('ir.mail_server').send_email = self._mock_smtp_gateway # create a 'pigs' group that will be used through the various tests self.group_pigs_id = self.mail_group.create(self.cr, self.uid, {'name': 'Pigs', 'description': 'Fans of Pigs, unite !'}) + def tearDown(self): + # Remove mocks + self.registry('ir.mail_server').build_email = self._build_email + self.registry('ir.mail_server').send_email = self._send_email + super(test_portal, self).tearDown() + def test_00_mail_invite(self): cr, uid = self.cr, self.uid print 'cacaprout' From 02668c368230b420159ec7c67e631bba2c6cbb84 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Thibault=20Delavall=C3=A9e?= Date: Thu, 13 Sep 2012 11:32:46 +0200 Subject: [PATCH 166/232] [IMP] mail_group: when deleting a mail_group, delete also the related menu entry. bzr revid: tde@openerp.com-20120913093246-kkxjqspupbq7j7z8 --- addons/mail/mail_group.py | 16 +++++++++------- 1 file changed, 9 insertions(+), 7 deletions(-) diff --git a/addons/mail/mail_group.py b/addons/mail/mail_group.py index 175ce650f06..fe0cc3950ae 100644 --- a/addons/mail/mail_group.py +++ b/addons/mail/mail_group.py @@ -19,13 +19,11 @@ # ############################################################################## -import datetime as DT import openerp import openerp.tools as tools -from operator import itemgetter from osv import osv from osv import fields -from tools.translate import _ + class mail_group(osv.Model): """ A mail_group is a collection of users sharing messages in a discussion @@ -47,7 +45,7 @@ class mail_group(osv.Model): _columns = { 'description': fields.text('Description'), 'menu_id': fields.many2one('ir.ui.menu', string='Related Menu', required=True, ondelete="cascade"), - 'public': fields.selection([('public','Public'),('private','Private'),('groups','Selected Group Only')], 'Privacy', required=True, + 'public': fields.selection([('public', 'Public'), ('private', 'Private'), ('groups', 'Selected Group Only')], 'Privacy', required=True, help='This group is visible by non members. \ Invisible groups can add members through the invite button.'), 'group_public_id': fields.many2one('res.groups', string='Authorized Group'), @@ -126,14 +124,14 @@ class mail_group(osv.Model): search_ref = self.pool.get('ir.model.data').get_object_reference(cr, uid, 'mail', 'view_message_search') params = { 'search_view_id': search_ref and search_ref[1] or False, - 'domain': [('model','=','mail.group'), ('res_id','=',mail_group_id)], + 'domain': [('model', '=', 'mail.group'), ('res_id', '=', mail_group_id)], 'context': {'default_model': 'mail.group', 'default_res_id': mail_group_id}, 'res_model': 'mail.message', 'thread_level': 1, } cobj = self.pool.get('ir.actions.client') newref = cobj.copy(cr, uid, ref[1], default={'params': str(params), 'name': vals['name']}, context=context) - self.write(cr, uid, [mail_group_id], {'action': 'ir.actions.client,'+str(newref), 'mail_group_id': mail_group_id}, context=context) + self.write(cr, uid, [mail_group_id], {'action': 'ir.actions.client,' + str(newref), 'mail_group_id': mail_group_id}, context=context) mail_alias.write(cr, uid, [vals['alias_id']], {"alias_force_thread_id": mail_group_id}, context) @@ -142,9 +140,13 @@ class mail_group(osv.Model): return mail_group_id def unlink(self, cr, uid, ids, context=None): + groups = self.browse(cr, uid, ids, context=context) # Cascade-delete mail aliases as well, as they should not exist without the mail group. mail_alias = self.pool.get('mail.alias') - alias_ids = [group.alias_id.id for group in self.browse(cr, uid, ids, context=context) if group.alias_id] + alias_ids = [group.alias_id.id for group in groups if group.alias_id] + # Cascade-delete menu entries as well + self.pool.get('ir.ui.menu').unlink(cr, uid, [group.menu_id.id for group in groups if group.menu_id], context=context) + # Delete mail_group res = super(mail_group, self).unlink(cr, uid, ids, context=context) mail_alias.unlink(cr, uid, alias_ids, context=context) return res From b1839623476824bd0f2e848039e1ff845da59092 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Thibault=20Delavall=C3=A9e?= Date: Thu, 13 Sep 2012 11:33:20 +0200 Subject: [PATCH 167/232] [IMP] mail_wizard_invite: updated default message. An override of default_get allows to have a more precise message. bzr revid: tde@openerp.com-20120913093320-mhwp43kkgoqd0h33 --- addons/mail/wizard/invite.py | 17 ++++++++++------- 1 file changed, 10 insertions(+), 7 deletions(-) diff --git a/addons/mail/wizard/invite.py b/addons/mail/wizard/invite.py index 4644dc1e55b..a63608d5a23 100644 --- a/addons/mail/wizard/invite.py +++ b/addons/mail/wizard/invite.py @@ -29,6 +29,16 @@ class invite_wizard(osv.osv_memory): _name = 'mail.wizard.invite' _description = 'Invite wizard' + def default_get(self, cr, uid, fields, context=None): + result = super(invite_wizard, self).default_get(cr, uid, fields, context=context) + if 'message' in fields and result.get('res_model') and result.get('res_id'): + document_name = self.pool.get(result.get('res_model')).name_get(cr, uid, [result.get('res_id')], context=context)[0][1] + message = _('You have been invited to follow %s.' % document_name) + result['message'] = message + elif 'message' in fields: + result['message'] = _('You have been invited to follow a new document.') + return result + _columns = { 'res_model': fields.char('Related Document Model', size=128, required=True, select=1, @@ -39,13 +49,6 @@ class invite_wizard(osv.osv_memory): 'message': fields.text('Message'), } - _defaults = { - 'message': lambda s, *a, **k: s._get_default_message(*a, **k), - } - - def _get_default_message(self, cr, uid, context=None): - return _('You have been invited to follow a new document.') - def onchange_partner_ids(self, cr, uid, ids, value, context=None): """ onchange_partner_ids (value format: [[6, 0, [3, 4]]]). The basic purpose of this method is to check that destination partners From ec11502e3c943fc8f85d96bc269f345537cda2cc Mon Sep 17 00:00:00 2001 From: "Pinakin Nayi (OpenERP)" Date: Thu, 13 Sep 2012 15:07:17 +0530 Subject: [PATCH 168/232] [IMP]send mail bzr revid: pna@tinyerp.com-20120913093717-8lh5k3ntmzof2fnd --- addons/import_base/import_framework.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/addons/import_base/import_framework.py b/addons/import_base/import_framework.py index 7e0a055d95b..559f42c8974 100644 --- a/addons/import_base/import_framework.py +++ b/addons/import_base/import_framework.py @@ -421,11 +421,11 @@ class import_framework(Thread): def _send_notification_email(self, result, error): if not self.email: return False - email_obj = self.obj.pool.get('mail.message') + email_obj = self.obj.pool.get('mail.mail') email_id = email_obj.create(self.cr, self.uid, { 'email_from' : 'import@module.openerp', 'email_to' : self.email, - 'body' : self.get_email_body(result, error), + 'body_html' : self.get_email_body(result, error), 'subject' : self.get_email_subject(result, error), 'auto_delete' : True}) email_obj.send(self.cr, self.uid, [email_id]) From 8868fa2810ea449f7fb81e72ccb0cd2219838ea0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Thibault=20Delavall=C3=A9e?= Date: Thu, 13 Sep 2012 12:33:19 +0200 Subject: [PATCH 169/232] [IMP] mail_wizard_invite: message is now an html field, and default values embedded inside a div. Added a subject to email sent to newly added followers. bzr revid: tde@openerp.com-20120913103319-6jg5moa0tl9p6ujn --- addons/mail/wizard/invite.py | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/addons/mail/wizard/invite.py b/addons/mail/wizard/invite.py index a63608d5a23..9734a595d7a 100644 --- a/addons/mail/wizard/invite.py +++ b/addons/mail/wizard/invite.py @@ -33,10 +33,10 @@ class invite_wizard(osv.osv_memory): result = super(invite_wizard, self).default_get(cr, uid, fields, context=context) if 'message' in fields and result.get('res_model') and result.get('res_id'): document_name = self.pool.get(result.get('res_model')).name_get(cr, uid, [result.get('res_id')], context=context)[0][1] - message = _('You have been invited to follow %s.' % document_name) + message = _('
    You have been invited to follow %s.
    ' % document_name) result['message'] = message elif 'message' in fields: - result['message'] = _('You have been invited to follow a new document.') + result['message'] = _('
    You have been invited to follow a new document.
    ') return result _columns = { @@ -46,7 +46,7 @@ class invite_wizard(osv.osv_memory): 'res_id': fields.integer('Related Document ID', select=1, help='Id of the followed resource'), 'partner_ids': fields.many2many('res.partner', string='Partners'), - 'message': fields.text('Message'), + 'message': fields.html('Message'), } def onchange_partner_ids(self, cr, uid, ids, value, context=None): @@ -74,7 +74,8 @@ class invite_wizard(osv.osv_memory): for follower_id in new_follower_ids: mail_mail = self.pool.get('mail.mail') mail_id = mail_mail.create(cr, uid, { - 'body_html': '
    %s
    ' % wizard.message, + 'subject': 'Invitation to follow %s' % document.name_get()[0][1], + 'body_html': '%s' % wizard.message, 'auto_delete': True, }, context=context) mail_mail.send(cr, uid, [mail_id], notifier_ids=[follower_id], context=context) From 01f6ac8d16080258fdd403b33c0d2147af1750f4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Thibault=20Delavall=C3=A9e?= Date: Thu, 13 Sep 2012 12:34:01 +0200 Subject: [PATCH 170/232] [FIX] portal invite wizard: generated URL improvement (partner_id and portal_group_id should be correct now). bzr revid: tde@openerp.com-20120913103401-waepkmjq8flsu13j --- addons/portal/mail_mail.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/addons/portal/mail_mail.py b/addons/portal/mail_mail.py index d7f4d05d5ac..813e31545e8 100644 --- a/addons/portal/mail_mail.py +++ b/addons/portal/mail_mail.py @@ -43,7 +43,9 @@ class mail_mail_portal(osv.Model): :param partner: browse_record of the specific recipient partner """ if partner: - url = self._generate_portal_url(cr, uid, partner.id, 12, 1234, context=context) + portal_ref = self.pool.get('ir.model.data').get_object_reference(cr, uid, 'portal', 'portal') + portal_id = portal_ref and portal_ref[1] or False + url = self._generate_portal_url(cr, uid, partner.id, portal_id, 1234, context=context) body = tools.append_content_to_html(mail.body_html, url) return body else: From 25734f2cfd0bb270e4834a2d0c77511795550fe6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Thibault=20Delavall=C3=A9e?= Date: Thu, 13 Sep 2012 12:35:29 +0200 Subject: [PATCH 171/232] [IMP] mail, portal: improved tests. A new TestMailMockup class has been defined, holding the various mockups for the mail gateway, to avoid having duplicate code in tests using emails. Added tests in portal about the invite mechanism. bzr revid: tde@openerp.com-20120913103529-410g9sk32sbbmkgn --- addons/mail/tests/test_mail.py | 28 ++++++++----- addons/portal/tests/test_portal.py | 65 +++++++++++++++++------------- 2 files changed, 56 insertions(+), 37 deletions(-) diff --git a/addons/mail/tests/test_mail.py b/addons/mail/tests/test_mail.py index bf9520f0e68..1fab5dd1732 100644 --- a/addons/mail/tests/test_mail.py +++ b/addons/mail/tests/test_mail.py @@ -84,7 +84,7 @@ Sylvie """ -class test_mail(common.TransactionCase): +class TestMailMockups(common.TransactionCase): def _mock_smtp_gateway(self, *args, **kwargs): return True @@ -98,6 +98,24 @@ class test_mail(common.TransactionCase): self._build_email_kwargs_list.append(kwargs) return self._build_email(*args, **kwargs) + def setUp(self): + super(TestMailMockups, self).setUp() + # Install mock SMTP gateway + self._init_mock_build_email() + self._build_email = self.registry('ir.mail_server').build_email + self.registry('ir.mail_server').build_email = self._mock_build_email + self._send_email = self.registry('ir.mail_server').send_email + self.registry('ir.mail_server').send_email = self._mock_smtp_gateway + + def tearDown(self): + # Remove mocks + self.registry('ir.mail_server').build_email = self._build_email + self.registry('ir.mail_server').send_email = self._send_email + super(TestMailMockups, self).tearDown() + + +class test_mail(TestMailMockups): + def _mock_send_get_mail_body(self, *args, **kwargs): # def _send_get_mail_body(self, cr, uid, mail, partner=None, context=None) body = tools.append_content_to_html(args[2].body_html, kwargs.get('partner').name if kwargs.get('partner') else 'No specific partner') @@ -116,12 +134,6 @@ class test_mail(common.TransactionCase): self.res_users = self.registry('res.users') self.res_partner = self.registry('res.partner') - # Install mock SMTP gateway - self._init_mock_build_email() - self._build_email = self.registry('ir.mail_server').build_email - self.registry('ir.mail_server').build_email = self._mock_build_email - self._send_email = self.registry('ir.mail_server').send_email - self.registry('ir.mail_server').send_email = self._mock_smtp_gateway # Mock send_get_mail_body to test its functionality without other addons override self._send_get_mail_body = self.registry('mail.mail').send_get_mail_body self.registry('mail.mail').send_get_mail_body = self._mock_send_get_mail_body @@ -136,8 +148,6 @@ class test_mail(common.TransactionCase): def tearDown(self): # Remove mocks - self.registry('ir.mail_server').build_email = self._build_email - self.registry('ir.mail_server').send_email = self._send_email self.registry('mail.mail').send_get_mail_body = self._send_get_mail_body super(test_mail, self).tearDown() diff --git a/addons/portal/tests/test_portal.py b/addons/portal/tests/test_portal.py index b98614de5e9..5f2875711df 100644 --- a/addons/portal/tests/test_portal.py +++ b/addons/portal/tests/test_portal.py @@ -19,22 +19,12 @@ # ############################################################################## -from openerp.tests import common +from openerp.addons.mail.tests import test_mail +# from openerp.tests import common +from openerp.tools import append_content_to_html -class test_portal(common.TransactionCase): - - def _mock_smtp_gateway(self, *args, **kwargs): - return True - - def _mock_build_email(self, *args, **kwargs): - self._build_email_args_list.append(args) - self._build_email_kwargs_list.append(kwargs) - return self._build_email(*args, **kwargs) - - def _init_mock_build_email(self): - self._build_email_args_list = [] - self._build_email_kwargs_list = [] +class test_portal(test_mail.TestMailMockups): def setUp(self): super(test_portal, self).setUp() @@ -44,23 +34,42 @@ class test_portal(common.TransactionCase): self.res_users = self.registry('res.users') self.res_partner = self.registry('res.partner') - # Install mock SMTP gateway - self._init_mock_build_email() - self._build_email = self.registry('ir.mail_server').build_email - self.registry('ir.mail_server').build_email = self._mock_build_email - self._send_email = self.registry('ir.mail_server').send_email - self.registry('ir.mail_server').send_email = self._mock_smtp_gateway - # create a 'pigs' group that will be used through the various tests self.group_pigs_id = self.mail_group.create(self.cr, self.uid, {'name': 'Pigs', 'description': 'Fans of Pigs, unite !'}) - def tearDown(self): - # Remove mocks - self.registry('ir.mail_server').build_email = self._build_email - self.registry('ir.mail_server').send_email = self._send_email - super(test_portal, self).tearDown() - def test_00_mail_invite(self): cr, uid = self.cr, self.uid - print 'cacaprout' + user_admin = self.res_users.browse(cr, uid, uid) + self.mail_invite = self.registry('mail.wizard.invite') + base_url = self.registry('ir.config_parameter').get_param(cr, uid, 'web.base.url', default='') + portal_ref = self.registry('ir.model.data').get_object_reference(cr, uid, 'portal', 'portal') + portal_id = portal_ref and portal_ref[1] or False + + # 0 - Admin + p_a_id = user_admin.partner_id.id + # 1 - Bert Tartopoils, with email, should receive emails for comments and emails + p_b_id = self.res_partner.create(cr, uid, {'name': 'Bert Tartopoils', 'email': 'b@b'}) + + # ---------------------------------------- + # CASE1: invite Bert + # ---------------------------------------- + + _sent_email_subject = 'Invitation to follow Pigs' + _sent_email_body = append_content_to_html('
    You have been invited to follow Pigs.
    ', + base_url + '/login?action=signin&partner_id=%s&group=%s&key=%s' % (p_b_id, portal_id, 1234)) + + # Do: create a mail_wizard_invite, validate it + self._init_mock_build_email() + mail_invite_id = self.mail_invite.create(cr, uid, {'partner_ids': [(4, p_b_id)]}, {'default_res_model': 'mail.group', 'default_res_id': self.group_pigs_id}) + self.mail_invite.add_followers(cr, uid, [mail_invite_id]) + group_pigs = self.mail_group.browse(cr, uid, self.group_pigs_id) + + # Test: Pigs followers should contain Admin and Bert + follower_ids = [follower.id for follower in group_pigs.message_follower_ids] + self.assertEqual(set(follower_ids), set([p_a_id, p_b_id]), 'Pigs followers after invite is incorrect') + # Test: sent email subject, body + self.assertEqual(len(self._build_email_kwargs_list), 1, 'sent email number incorrect, should be only for Bert') + for sent_email in self._build_email_kwargs_list: + self.assertEqual(sent_email.get('subject'), _sent_email_subject, 'sent email subject incorrect') + self.assertEqual(sent_email.get('body'), _sent_email_body, 'sent email body incorrect') From b7d56a0b043b7a9e6e1fcec49873d9574538d9a3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fr=C3=A9d=C3=A9ric=20van=20der=20Essen?= Date: Thu, 13 Sep 2012 12:46:21 +0200 Subject: [PATCH 172/232] [IMP] point_of_sale: some improvements to the debug window bzr revid: fva@openerp.com-20120913104621-zryga82o3d4vq174 --- addons/point_of_sale/static/src/css/pos.css | 16 +++++++--------- addons/point_of_sale/static/src/js/db.js | 8 ++++++++ addons/point_of_sale/static/src/js/devices.js | 4 ---- addons/point_of_sale/static/src/js/widgets.js | 7 ++++--- addons/point_of_sale/static/src/xml/pos.xml | 14 ++++++++------ 5 files changed, 27 insertions(+), 22 deletions(-) diff --git a/addons/point_of_sale/static/src/css/pos.css b/addons/point_of_sale/static/src/css/pos.css index a727986bb1c..8213df7fc44 100644 --- a/addons/point_of_sale/static/src/css/pos.css +++ b/addons/point_of_sale/static/src/css/pos.css @@ -1166,17 +1166,15 @@ cursor:default; } .point-of-sale .debug-widget .status.on{ - padding: 5px; - padding-left: 15px; - display: block; - background-color: green; -} -.point-of-sale .debug-widget .status.on{ - padding: 5px; - padding-left: 15px; - display: block; background-color: #6cd11d; } +.point-of-sale .debug-widget .event{ + padding: 5px; + padding-left: 15px; + display: block; + cursor:default; + background-color: #1E1E1E; +} /* ********* The PopupWidgets ********* */ diff --git a/addons/point_of_sale/static/src/js/db.js b/addons/point_of_sale/static/src/js/db.js index 58ffa3373d6..66fce2e738e 100644 --- a/addons/point_of_sale/static/src/js/db.js +++ b/addons/point_of_sale/static/src/js/db.js @@ -21,6 +21,8 @@ function openerp_pos_db(instance, module){ this.category_childs = {}; this.category_parent = {}; this.category_search_string = {}; + this.packagings_by_product_id = {}; + this.packagings_by_product_id = {}; }, /* returns the category object from its id. If you pass a list of id as parameters, you get * a list of category objects. @@ -158,6 +160,12 @@ function openerp_pos_db(instance, module){ this.save('products',stored_products); this.save('categories',stored_categories); }, + add_packagings: function(packagings){ + this.packagings_by_product_id + for(var i = 0, len = packagings.length; i < len; i++){ + var pack = packagings[i]; + } + }, /* removes all the data from the database. TODO : being able to selectively remove data */ clear: function(stores){ for(var i = 0, len = arguments.length; i < len; i++){ diff --git a/addons/point_of_sale/static/src/js/devices.js b/addons/point_of_sale/static/src/js/devices.js index 65a28b5366a..a26d8ca7154 100644 --- a/addons/point_of_sale/static/src/js/devices.js +++ b/addons/point_of_sale/static/src/js/devices.js @@ -27,10 +27,6 @@ function openerp_pos_devices(instance,module){ //module is instance.point_of_sal success_callback = success_callback || function(){}; error_callback = error_callback || function(){}; - if(jQuery.deparam(jQuery.param.querystring()).debug !== undefined){ - console.log('PROXY:',name,params); - } - var callbacks = this.notifications[name] || []; for(var i = 0; i < callbacks.length; i++){ callbacks[i](params); diff --git a/addons/point_of_sale/static/src/js/widgets.js b/addons/point_of_sale/static/src/js/widgets.js index 0ae127e0b1b..f7746cbbd61 100644 --- a/addons/point_of_sale/static/src/js/widgets.js +++ b/addons/point_of_sale/static/src/js/widgets.js @@ -686,6 +686,8 @@ function openerp_pos_widgets(instance, module){ //module is instance.point_of_sa 'open_cashbox', 'print_receipt', 'print_pdf_invoice', + 'weighting_read_kg', + 'is_payment_accepted', ], minimized: false, start: function(){ @@ -727,9 +729,8 @@ function openerp_pos_widgets(instance, module){ //module is instance.point_of_sa }); _.each(this.events, function(name){ self.pos.proxy.add_notification(name,function(){ - console.log('Notification:',name); - self.$('.status.'+name).stop().clearQueue().css({'background-color':'#6CD11D'}); - self.$('.status.'+name).animate({'background-color':'black'},2000); + self.$('.event.'+name).stop().clearQueue().css({'background-color':'#6CD11D'}); + self.$('.event.'+name).animate({'background-color':'#1E1E1E'},2000); }); }); self.pos.proxy.add_notification('help_needed',function(){ diff --git a/addons/point_of_sale/static/src/xml/pos.xml b/addons/point_of_sale/static/src/xml/pos.xml index 7c8b18a9bc3..7af3c2c2255 100644 --- a/addons/point_of_sale/static/src/xml/pos.xml +++ b/addons/point_of_sale/static/src/xml/pos.xml @@ -452,12 +452,14 @@

    Hardware Events

      -
    • Scan Item Success
    • -
    • Scan Item Unrecognized
    • -
    • Payment Request
    • -
    • Open Cashbox
    • - - +
    • Scan Item Success
    • +
    • Scan Item Unrecognized
    • +
    • Payment Request
    • +
    • Open Cashbox
    • + + +
    • Read Weighting Scale
    • +
    • Check Payment
    From a5d604465175d0138e7003959d7a36101ed352a8 Mon Sep 17 00:00:00 2001 From: Raphael Collet Date: Thu, 13 Sep 2012 13:44:03 +0200 Subject: [PATCH 173/232] [IMP] res.partner.bank: change _prepare_name_get() to make it usable by onchange methods bzr revid: rco@openerp.com-20120913114403-ny6h6vpijymj6o4s --- openerp/addons/base/res/res_bank.py | 50 ++++++++++++++--------------- 1 file changed, 24 insertions(+), 26 deletions(-) diff --git a/openerp/addons/base/res/res_bank.py b/openerp/addons/base/res/res_bank.py index 3bfbd822dae..8f5d8aa8131 100644 --- a/openerp/addons/base/res/res_bank.py +++ b/openerp/addons/base/res/res_bank.py @@ -172,37 +172,35 @@ class res_partner_bank(osv.osv): ('required', field.required)] return res - def _prepare_name_get(self, cr, uid, bank_type_obj, bank_obj, context=None): + def _prepare_name_get(self, cr, uid, bank_dicts, context=None): + """ Format the name of a res.partner.bank. + This function is designed to be inherited to add replacement fields. + :param bank_dicts: a list of res.partner.bank dicts, as returned by the method read() + :return: [(id, name), ...], as returned by the method name_get() """ - Format the name of a res.partner.bank. - This function is designed to be inherited to add replacement fields. - :param browse_record bank_type_obj: res.partner.bank.type object - :param browse_record bank_obj: res.partner.bank object - :rtype: str - :return: formatted name of a res.partner.bank record - """ - return bank_type_obj.format_layout % bank_obj._data[bank_obj.id] + # prepare a mapping {code: format_layout} for all bank types + bank_type_obj = self.pool.get('res.partner.bank.type') + bank_types = bank_type_obj.browse(cr, uid, bank_type_obj.search(cr, uid, []), context=context) + bank_code_format = dict((bt.code, bt.format_layout) for bt in bank_types) + + res = [] + for data in bank_dicts: + name = data['acc_number'] + if data['state'] and bank_code_format.get(data['state']): + try: + if not data.get('bank_name'): + data['bank_name'] = _('BANK') + name = bank_code_format[data['state']] % data + except Exception: + raise osv.except_osv(_("Formating Error"), _("Invalid Bank Account Type Name format.")) + res.append((data['id'], name)) + return res def name_get(self, cr, uid, ids, context=None): if not len(ids): return [] - bank_type_obj = self.pool.get('res.partner.bank.type') - res = [] - for val in self.browse(cr, uid, ids, context=context): - result = val.acc_number - if val.state: - type_ids = bank_type_obj.search(cr, uid, [('code','=',val.state)]) - if type_ids: - t = bank_type_obj.browse(cr, uid, type_ids[0], context=context) - try: - # avoid the default format_layout to result in "False: ..." - if not val._data[val.id]['bank_name']: - val._data[val.id]['bank_name'] = _('BANK') - result = self._prepare_name_get(cr, uid, t, val, context=context) - except Exception: - raise osv.except_osv(_("Formating Error"), _("Invalid Bank Account Type Name format.")) - res.append((val.id, result)) - return res + bank_dicts = self.read(cr, uid, ids, context=context) + return self._prepare_name_get(cr, uid, bank_dicts, context=context) def onchange_company_id(self, cr, uid, ids, company_id, context=None): result = {} From a44eb10af458b04b02f76e1a657f59280d78db13 Mon Sep 17 00:00:00 2001 From: Raphael Collet Date: Thu, 13 Sep 2012 13:46:09 +0200 Subject: [PATCH 174/232] [IMP] account: adapt _prepare_name_get() of res.partner.bank to its new api bzr revid: rco@openerp.com-20120913114609-pff1x0spvb0gja2u --- addons/account/account_bank.py | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) diff --git a/addons/account/account_bank.py b/addons/account/account_bank.py index ef146128a67..e44d2acd163 100644 --- a/addons/account/account_bank.py +++ b/addons/account/account_bank.py @@ -43,11 +43,15 @@ class bank(osv.osv): "Return the name to use when creating a bank journal" return (bank.bank_name or '') + ' ' + bank.acc_number - def _prepare_name_get(self, cr, uid, bank_type_obj, bank_obj, context=None): - """Add ability to have %(currency_name)s in the format_layout of - res.partner.bank.type""" - bank_obj._data[bank_obj.id]['currency_name'] = bank_obj.currency_id and bank_obj.currency_id.name or '' - return super(bank, self)._prepare_name_get(cr, uid, bank_type_obj, bank_obj, context=context) + def _prepare_name_get(self, cr, uid, bank_dicts, context=None): + """Add ability to have %(currency_name)s in the format_layout of res.partner.bank.type""" + currency_ids = list(set(data['currency_id'][0] for data in bank_dicts if data['currency_id'])) + currencies = self.pool.get('res.currency').browse(cr, uid, currency_ids, context=context) + currency_name = dict((currency.id, currency.name) for currency in currencies) + + for data in bank_dicts: + data['currency_name'] = data['currency_id'] and currency_name[data['currency_id'][0]] or '' + return super(bank, self)._prepare_name_get(cr, uid, bank_dicts, context=context) def post_write(self, cr, uid, ids, context={}): if isinstance(ids, (int, long)): From 0e4422809ba411a629b03baf705a3a7dd781302f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Thibault=20Delavall=C3=A9e?= Date: Thu, 13 Sep 2012 13:49:17 +0200 Subject: [PATCH 175/232] [IMP] email_template: now inherit from TestMailMockups test class. bzr revid: tde@openerp.com-20120913114917-na341w0ja7n0ditu --- addons/email_template/tests/test_mail.py | 16 ++-------------- addons/portal/tests/test_portal.py | 1 - 2 files changed, 2 insertions(+), 15 deletions(-) diff --git a/addons/email_template/tests/test_mail.py b/addons/email_template/tests/test_mail.py index 85ab3afc902..1091c34ad5c 100644 --- a/addons/email_template/tests/test_mail.py +++ b/addons/email_template/tests/test_mail.py @@ -20,17 +20,10 @@ ############################################################################## import base64 -from openerp.tests import common +from openerp.addons.mail.tests import test_mail -class test_message_compose(common.TransactionCase): - def _mock_smtp_gateway(self, *args, **kwargs): - return True - - def _mock_build_email(self, *args, **kwargs): - self._build_email_args = args - self._build_email_kwargs = kwargs - return self.build_email_real(*args, **kwargs) +class test_message_compose(test_mail.TestMailMockups): def setUp(self): super(test_message_compose, self).setUp() @@ -40,11 +33,6 @@ class test_message_compose(common.TransactionCase): self.res_users = self.registry('res.users') self.res_partner = self.registry('res.partner') - # Install mock SMTP gateway - self.build_email_real = self.registry('ir.mail_server').build_email - self.registry('ir.mail_server').build_email = self._mock_build_email - self.registry('ir.mail_server').send_email = self._mock_smtp_gateway - # create a 'pigs' and 'bird' groups that will be used through the various tests self.group_pigs_id = self.mail_group.create(self.cr, self.uid, {'name': 'Pigs', 'description': 'Fans of Pigs, unite !'}) diff --git a/addons/portal/tests/test_portal.py b/addons/portal/tests/test_portal.py index 5f2875711df..1f709ed3fa9 100644 --- a/addons/portal/tests/test_portal.py +++ b/addons/portal/tests/test_portal.py @@ -20,7 +20,6 @@ ############################################################################## from openerp.addons.mail.tests import test_mail -# from openerp.tests import common from openerp.tools import append_content_to_html From b2c79d266b7a2752f26782ee673e52b647825f74 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Thibault=20Delavall=C3=A9e?= Date: Thu, 13 Sep 2012 14:11:10 +0200 Subject: [PATCH 176/232] [FIX] mail_message: fixed record_name, because we could call a name_get on a document we can not see (if we are in the recipints of a document we can not access). name_get is therefore inside a try/except statement, getting a void string as result if AccessDenied is raised. Updated JS to avoid displaying the record name on a void string. bzr revid: tde@openerp.com-20120913121110-bd0syf813h0e39gk --- addons/mail/mail_message.py | 6 +++++- addons/mail/static/src/xml/mail.xml | 4 ++-- 2 files changed, 7 insertions(+), 3 deletions(-) diff --git a/addons/mail/mail_message.py b/addons/mail/mail_message.py index 10f02805ab3..234c0262f6a 100644 --- a/addons/mail/mail_message.py +++ b/addons/mail/mail_message.py @@ -20,6 +20,7 @@ ############################################################################## import logging +import openerp import tools from email.header import decode_header @@ -58,7 +59,10 @@ class mail_message(osv.Model): for message in self.browse(cr, uid, ids, context=context): if not message.model or not message.res_id: continue - result[message.id] = self._shorten_name(self.pool.get(message.model).name_get(cr, uid, [message.res_id], context=context)[0][1]) + try: + result[message.id] = self._shorten_name(self.pool.get(message.model).name_get(cr, uid, [message.res_id], context=context)[0][1]) + except openerp.exceptions.AccessDenied, e: + pass return result def _get_unread(self, cr, uid, ids, name, arg, context=None): diff --git a/addons/mail/static/src/xml/mail.xml b/addons/mail/static/src/xml/mail.xml index 4d3b56cf62f..10aa7c07ec7 100644 --- a/addons/mail/static/src/xml/mail.xml +++ b/addons/mail/static/src/xml/mail.xml @@ -130,14 +130,14 @@
    - +