diff --git a/addons/account_voucher/account_voucher.py b/addons/account_voucher/account_voucher.py index 597ed01aa1b..ef4c9379c82 100644 --- a/addons/account_voucher/account_voucher.py +++ b/addons/account_voucher/account_voucher.py @@ -884,6 +884,8 @@ class account_voucher(osv.osv): return res def onchange_journal(self, cr, uid, ids, journal_id, line_ids, tax_id, partner_id, date, amount, ttype, company_id, context=None): + if context is None: + context = {} if not journal_id: return False journal_pool = self.pool.get('account.journal') diff --git a/addons/analytic_user_function/analytic_user_function.py b/addons/analytic_user_function/analytic_user_function.py index 1adefb2d59d..2671fc92581 100644 --- a/addons/analytic_user_function/analytic_user_function.py +++ b/addons/analytic_user_function/analytic_user_function.py @@ -42,6 +42,7 @@ class analytic_user_funct_grid(osv.osv): return {} value = {} + prod = False if product_id: prod = self.pool.get('product.product').browse(cr, uid, product_id, context=context) emp = emp_obj.browse(cr, uid, emp_id[0], context=context) diff --git a/addons/lunch/lunch.py b/addons/lunch/lunch.py index efad6fd6944..fbd193709ac 100644 --- a/addons/lunch/lunch.py +++ b/addons/lunch/lunch.py @@ -35,6 +35,16 @@ class lunch_order(osv.Model): _description = 'Lunch Order' _order = 'date desc' + def name_get(self, cr, uid, ids, context=None): + if not ids: + return [] + res = [] + for elmt in self.browse(cr, uid, ids, context=context): + name = _("Lunch Order") + name = name + ' ' + str(elmt.id) + res.append((elmt.id, name)) + return res + def _price_get(self, cr, uid, ids, name, arg, context=None): """ get and sum the order lines' price diff --git a/addons/lunch/lunch_view.xml b/addons/lunch/lunch_view.xml index 2f3af2f461d..a2f22d4dd0a 100644 --- a/addons/lunch/lunch_view.xml +++ b/addons/lunch/lunch_view.xml @@ -7,7 +7,7 @@ - + Search @@ -85,6 +85,40 @@ + + + cashmove tree + lunch.cashmove + + + + + + + + + + + + cashmove form + lunch.cashmove + +
+ + + + + + + +
+
+
+ + New Order @@ -111,6 +145,19 @@ + + + cashmove tree + lunch.cashmove + + + + + + + + + @@ -119,6 +166,7 @@ tree {"search_default_is_mine_group":1} +

Here you can see your cash moves.
A cash moves can be either an expense or a payment. @@ -175,6 +223,7 @@ tree,form {"search_default_group_by_user":1} +

Click to create a new payment. @@ -196,6 +245,7 @@ tree,form {"search_default_is_payment":1} +

Click to create a payment. @@ -389,39 +439,6 @@ - - - cashmove tree - lunch.cashmove - - - - - - - - - - - - cashmove form - lunch.cashmove - -

- - - - - - - -
-
-
- alert tree diff --git a/addons/mail/data/mail_data.xml b/addons/mail/data/mail_data.xml index 02cfd626768..8af808e77c7 100644 --- a/addons/mail/data/mail_data.xml +++ b/addons/mail/data/mail_data.xml @@ -40,6 +40,11 @@ () + + + none + + Discussions diff --git a/addons/mail/mail_message.py b/addons/mail/mail_message.py index a76807bb5e8..46d298aedb5 100644 --- a/addons/mail/mail_message.py +++ b/addons/mail/mail_message.py @@ -658,14 +658,14 @@ class mail_message(osv.Model): - no model, no res_id, I create a private message OR - pid in message_follower_ids if model, res_id OR - mail_notification (parent_id.id, pid) exists, uid has been notified of the parent, OR - - uid have write access on the related document if model, res_id, OR + - uid have write or create access on the related document if model, res_id, OR - otherwise: raise - write: if - author_id == pid, uid is the author, OR - - uid has write access on the related document if model, res_id + - uid has write or create access on the related document if model, res_id - otherwise: raise - unlink: if - - uid has write access on the related document if model, res_id + - uid has write or create access on the related document if model, res_id - otherwise: raise """ def _generate_model_record_ids(msg_val, msg_ids=[]): diff --git a/addons/mail/mail_thread.py b/addons/mail/mail_thread.py index d586ddda768..0f3158dfc20 100644 --- a/addons/mail/mail_thread.py +++ b/addons/mail/mail_thread.py @@ -473,7 +473,8 @@ class mail_thread(osv.AbstractModel): if not model_obj: model_obj = self if operation in ['create', 'write', 'unlink']: - model_obj.check_access_rights(cr, uid, 'write') + if not model_obj.check_access_rights(cr, uid, 'write', raise_exception=False): + model_obj.check_access_rights(cr, uid, 'create') model_obj.check_access_rule(cr, uid, mids, 'write', context=context) else: model_obj.check_access_rights(cr, uid, operation) @@ -814,8 +815,10 @@ class mail_thread(osv.AbstractModel): # text/plain ->
                 body = tools.append_content_to_html(u'', body, preserve=True)
         else:
-            alternative = (message.get_content_type() == 'multipart/alternative')
+            alternative = False
             for part in message.walk():
+                if part.get_content_type() == 'multipart/alternative':
+                    alternative = True
                 if part.get_content_maintype() == 'multipart':
                     continue  # skip container
                 filename = part.get_filename()  # None if normal part
diff --git a/addons/mail/tests/test_mail_features.py b/addons/mail/tests/test_mail_features.py
index e1bef837010..ef6e54d1a40 100644
--- a/addons/mail/tests/test_mail_features.py
+++ b/addons/mail/tests/test_mail_features.py
@@ -234,7 +234,7 @@ class test_mail(TestMailBase):
         # Data creation
         # --------------------------------------------------
         # 0 - Update existing users-partners
-        self.res_users.write(cr, uid, [uid], {'email': 'a@a'})
+        self.res_users.write(cr, uid, [uid], {'email': 'a@a', 'notification_email_send': 'comment'})
         self.res_users.write(cr, uid, [self.user_raoul_id], {'email': 'r@r'})
         # 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'})
diff --git a/addons/mail/tests/test_mail_gateway.py b/addons/mail/tests/test_mail_gateway.py
index 51d6d8add30..93e6479c611 100644
--- a/addons/mail/tests/test_mail_gateway.py
+++ b/addons/mail/tests/test_mail_gateway.py
@@ -81,6 +81,67 @@ Please call me as soon as possible this afternoon!
 Sylvie
 """
 
+MAIL_MULTIPART_MIXED = """Return-Path: 
+X-Original-To: raoul@grosbedon.fr
+Delivered-To: raoul@grosbedon.fr
+Received: by mail1.grosbedon.com (Postfix, from userid 10002)
+    id E8166BFACA; Fri, 23 Aug 2013 13:18:01 +0200 (CEST)
+X-Spam-Checker-Version: SpamAssassin 3.3.1 (2010-03-16) on mail1.grosbedon.com
+X-Spam-Level: 
+X-Spam-Status: No, score=-2.6 required=5.0 tests=BAYES_00,FREEMAIL_FROM,
+    HTML_MESSAGE,RCVD_IN_DNSWL_LOW autolearn=unavailable version=3.3.1
+Received: from mail-ie0-f173.google.com (mail-ie0-f173.google.com [209.85.223.173])
+    by mail1.grosbedon.com (Postfix) with ESMTPS id 9BBD7BFAAA
+    for ; Fri, 23 Aug 2013 13:17:55 +0200 (CEST)
+Received: by mail-ie0-f173.google.com with SMTP id qd12so575130ieb.4
+        for ; Fri, 23 Aug 2013 04:17:54 -0700 (PDT)
+DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed;
+        d=gmail.com; s=20120113;
+        h=mime-version:date:message-id:subject:from:to:content-type;
+        bh=dMNHV52EC7GAa7+9a9tqwT9joy9z+1950J/3A6/M/hU=;
+        b=DGuv0VjegdSrEe36ADC8XZ9Inrb3Iu+3/52Bm+caltddXFH9yewTr0JkCRQaJgMwG9
+         qXTQgP8qu/VFEbCh6scu5ZgU1hknzlNCYr3LT+Ih7dAZVUEHUJdwjzUU1LFV95G2RaCd
+         /Lwff6CibuUvrA+0CBO7IRKW0Sn5j0mukYu8dbaKsm6ou6HqS8Nuj85fcXJfHSHp6Y9u
+         dmE8jBh3fHCHF/nAvU+8aBNSIzl1FGfiBYb2jCoapIuVFitKR4q5cuoodpkH9XqqtOdH
+         DG+YjEyi8L7uvdOfN16eMr7hfUkQei1yQgvGu9/5kXoHg9+Gx6VsZIycn4zoaXTV3Nhn
+         nu4g==
+MIME-Version: 1.0
+X-Received: by 10.50.124.65 with SMTP id mg1mr1144467igb.43.1377256674216;
+ Fri, 23 Aug 2013 04:17:54 -0700 (PDT)
+Received: by 10.43.99.71 with HTTP; Fri, 23 Aug 2013 04:17:54 -0700 (PDT)
+Date: Fri, 23 Aug 2013 13:17:54 +0200
+Message-ID: 
+Subject: Test mail multipart/mixed
+From: =?ISO-8859-1?Q?Raoul Grosbedon=E9e?= 
+To: Followers of ASUSTeK-Joseph-Walters 
+Content-Type: multipart/mixed; boundary=089e01536c4ed4d17204e49b8e96
+
+--089e01536c4ed4d17204e49b8e96
+Content-Type: multipart/alternative; boundary=089e01536c4ed4d16d04e49b8e94
+
+--089e01536c4ed4d16d04e49b8e94
+Content-Type: text/plain; charset=ISO-8859-1
+
+Should create a multipart/mixed: from gmail, *bold*, with attachment.
+
+-- 
+Marcel Boitempoils.
+
+--089e01536c4ed4d16d04e49b8e94
+Content-Type: text/html; charset=ISO-8859-1
+
+
Should create a multipart/mixed: from gmail, bold, with attachment.

--
Marcel Boitempoils.
+ +--089e01536c4ed4d16d04e49b8e94-- +--089e01536c4ed4d17204e49b8e96 +Content-Type: text/plain; charset=US-ASCII; name="test.txt" +Content-Disposition: attachment; filename="test.txt" +Content-Transfer-Encoding: base64 +X-Attachment-Id: f_hkpb27k00 + +dGVzdAo= +--089e01536c4ed4d17204e49b8e96--""" + class TestMailgateway(TestMailBase): @@ -220,6 +281,24 @@ class TestMailgateway(TestMailBase): self.assertEqual(mail.reply_to, msg.email_from, 'mail_mail: incorrect reply_to: should be message email_from') + def test_09_message_parse(self): + """ Testing incoming emails parsing """ + cr, uid = self.cr, self.uid + + res = self.mail_thread.message_parse(cr, uid, MAIL_TEMPLATE_PLAINTEXT) + self.assertIn('Please call me as soon as possible this afternoon!', res.get('body', ''), + 'message_parse: missing text in text/plain body after parsing') + + res = self.mail_thread.message_parse(cr, uid, MAIL_TEMPLATE) + self.assertIn('

Please call me as soon as possible this afternoon!

', res.get('body', ''), + 'message_parse: missing html in multipart/alternative body after parsing') + + res = self.mail_thread.message_parse(cr, uid, MAIL_MULTIPART_MIXED) + self.assertNotIn('Should create a multipart/mixed: from gmail, *bold*, with attachment', res.get('body', ''), + 'message_parse: text version should not be in body after parsing multipart/mixed') + self.assertIn('
Should create a multipart/mixed: from gmail, bold, with attachment.

', res.get('body', ''), + 'message_parse: html version should be in body after parsing multipart/mixed') + def test_10_message_process(self): """ Testing incoming emails processing. """ cr, uid, user_raoul = self.cr, self.uid, self.user_raoul @@ -459,7 +538,7 @@ class TestMailgateway(TestMailBase): frog_group = self.mail_group.browse(cr, uid, frog_groups[0]) msg = frog_group.message_ids[0] # Test: plain text content should be wrapped and stored as html - self.assertEqual(msg.body, '
\nPlease call me as soon as possible this afternoon!\n\n--\nSylvie\n
', + self.assertIn('
\nPlease call me as soon as possible this afternoon!\n\n--\nSylvie\n
', msg.body, 'message_process: plaintext incoming email incorrectly parsed') def test_20_thread_parent_resolution(self): diff --git a/addons/mrp/mrp_view.xml b/addons/mrp/mrp_view.xml index a09c2ef4e17..bad2e9211a7 100644 --- a/addons/mrp/mrp_view.xml +++ b/addons/mrp/mrp_view.xml @@ -189,7 +189,7 @@ - + @@ -248,7 +248,7 @@ mrp.routing.workcenter
- + @@ -280,7 +280,7 @@ - + @@ -422,6 +422,7 @@ + @@ -513,7 +514,18 @@

- + + product.search.bom + product.product + + + + + + + + + Products product.product @@ -537,15 +549,15 @@ action="mrp_bom_form_action2" id="menu_mrp_bom_form_action2" parent="menu_mrp_configuration" - groups="base.group_no_one" sequence="20"/> Bill of Materials Structure ir.actions.act_window mrp.bom - [('bom_id', '=',active_ids)] + [('id', 'in', active_ids)] + tree @@ -693,7 +705,7 @@
- + @@ -709,7 +721,7 @@ - + @@ -723,7 +735,7 @@ - + @@ -974,7 +986,7 @@ - Work Center + Work Centers ir.actions.act_window mrp.workcenter form @@ -1016,6 +1028,7 @@ Bill of Materials [('bom_id','=',False)] mrp.bom + tree {'search_default_product_id': [active_id]} diff --git a/addons/mrp/res_config.py b/addons/mrp/res_config.py index 22a14addcd1..7343990a32f 100644 --- a/addons/mrp/res_config.py +++ b/addons/mrp/res_config.py @@ -36,7 +36,7 @@ class mrp_config_settings(osv.osv_memory): * Repair quotation report * Notes for the technician and for the final customer. This installs the module mrp_repair."""), - 'module_mrp_operations': fields.boolean("Allow detailed planning of work order", + 'module_mrp_operations': fields.boolean("Allow detailed planning of work orders", help="""This allows to add state, date_start,date_stop in production order operation lines (in the "Work Centers" tab). This installs the module mrp_operations."""), 'module_mrp_byproduct': fields.boolean("Produce several products from one manufacturing order", @@ -60,7 +60,7 @@ class mrp_config_settings(osv.osv_memory): help="""Routings allow you to create and manage the manufacturing operations that should be followed within your work centers in order to produce a product. They are attached to bills of materials that will define the required raw materials."""), - 'group_mrp_properties': fields.boolean("Allow several bill of materials per products using properties", + 'group_mrp_properties': fields.boolean("Allow several bill of materials per product using properties", implied_group='product.group_mrp_properties', help="""The selection of the right Bill of Material to use will depend on the properties specified on the sales order and the Bill of Material."""), 'module_product_manufacturer': fields.boolean("Define manufacturers on products ", diff --git a/addons/mrp/tests/__init__.py b/addons/mrp/tests/__init__.py new file mode 100644 index 00000000000..39ebd5e451f --- /dev/null +++ b/addons/mrp/tests/__init__.py @@ -0,0 +1,28 @@ +# -*- 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_multicompany + +checks = [ + test_multicompany, +] + +# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4: diff --git a/addons/mrp/tests/test_multicompany.py b/addons/mrp/tests/test_multicompany.py new file mode 100644 index 00000000000..78986bd41b2 --- /dev/null +++ b/addons/mrp/tests/test_multicompany.py @@ -0,0 +1,50 @@ +# -*- 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 TestMrpMulticompany(common.TransactionCase): + + def setUp(self): + super(TestMrpMulticompany, self).setUp() + cr, uid = self.cr, self.uid + + # Usefull models + self.ir_model_data = self.registry('ir.model.data') + self.res_users = self.registry('res.users') + self.stock_location = self.registry('stock.location') + + model, self.multicompany_user_id = self.ir_model_data.get_object_reference(cr, uid, 'stock', 'multicompany_user') + + + def test_00_multicompany_user(self): + """check no error on getting default mrp.production values in multicompany setting""" + cr, uid, context = self.cr, self.multicompany_user_id, {} + fields = ['location_src_id', 'location_dest_id'] + defaults = self.stock_location.default_get(cr, uid, ['location_id', 'location_dest_id', 'type'], context) + for field in fields: + print field, uid, defaults + if defaults.get(field): + try: + self.stock_location.check_access_rule(cr, uid, [defaults[field]], 'read', context) + except Exception, exc: + assert False, "unreadable location %s: %s" % (field, exc) diff --git a/addons/mrp_repair/mrp_repair_view.xml b/addons/mrp_repair/mrp_repair_view.xml index a4526b5b581..b7a667ce308 100644 --- a/addons/mrp_repair/mrp_repair_view.xml +++ b/addons/mrp_repair/mrp_repair_view.xml @@ -31,7 +31,7 @@