diff --git a/addons/account/__openerp__.py b/addons/account/__openerp__.py index 343512dc271..38391993d38 100644 --- a/addons/account/__openerp__.py +++ b/addons/account/__openerp__.py @@ -159,6 +159,7 @@ for a particular financial year and for preparation of vouchers there is a modul #'test/account_cash_statement.yml', 'test/test_edi_invoice.yml', 'test/account_report.yml', + 'test/analytic_hierarchy.yml', 'test/account_fiscalyear_close.yml', #last test, as it will definitively close the demo fiscalyear ], 'installable': True, diff --git a/addons/account/test/analytic_hierarchy.yml b/addons/account/test/analytic_hierarchy.yml new file mode 100644 index 00000000000..117d6506b21 --- /dev/null +++ b/addons/account/test/analytic_hierarchy.yml @@ -0,0 +1,56 @@ +- + We create a simple hierarchy like this + Toplevel view account + ├ Real analytic account 1 + └ Real analytic account 2 +- + !record {model: account.analytic.account, id: analytic_account_view1}: + name: 'Toplevel view account' + type: 'view' +- + !record {model: account.analytic.account, id: analytic_account1}: + name: 'Real analytic account 1' + type: 'normal' + parent_id: analytic_account_view1 +- + !record {model: account.analytic.account, id: analytic_account2}: + name: 'Real analytic account 2' + type: 'normal' + parent_id: analytic_account_view1 +- + We add analytic lines in real accounts and journal +- + !record {model: account.analytic.journal, id: analytic_journal1}: + name: 'Analytic journal' +- + !record {model: account.analytic.line, id: analytic_line1}: + name: 'Analytic line 1' + account_id: analytic_account1 + journal_id: analytic_journal1 + general_account_id: account.a_expense +- + !record {model: account.analytic.line, id: analytic_line2}: + name: 'Analytic line 2' + account_id: analytic_account2 + journal_id: analytic_journal1 + general_account_id: account.a_expense +- + Now we copy the toplevel account +- + !function {model: account.analytic.account, name: copy, id: analytic_account_view1}: + - eval: "ref('analytic_account_view1')" +- + !python {model: account.analytic.account}: | + # the toplevel account has been copied with the (copy) suffix + accounts = self.search(cr, uid, [('name', 'like', '%Toplevel view account%')]) + assert len(accounts) == 2, 'The toplevel account has not been copied' + accounts = self.search(cr, uid, [('name', '=', 'Toplevel view account')]) + assert len(accounts) == 1, 'The toplevel account copy has a bad name' + # the sub-accounts and sub-accounts have been copied without the (copy) suffix + accounts = self.search(cr, uid, [('name', '=', 'Real analytic account 1')]) + assert len(accounts) == 2, 'The sub-account has not been copied' + accounts = self.search(cr, uid, [('name', '=', 'Real analytic account 2')]) + assert len(accounts) == 2, 'The sub-account has not been copied' + # the analytic lines should not be duplicated + lines = self.pool.get('account.analytic.line').search(cr, uid, [('name', 'like', '%Analytic line %')]) + assert len(lines) == 2, "The analytic lines shouldn't have been copied" diff --git a/addons/analytic/analytic.py b/addons/analytic/analytic.py index db7a805c555..e2ffbe52345 100644 --- a/addons/analytic/analytic.py +++ b/addons/analytic/analytic.py @@ -261,13 +261,21 @@ class account_analytic_account(osv.osv): def name_create(self, cr, uid, name, context=None): raise osv.except_osv(_('Warning'), _("Quick account creation disallowed.")) + def copy_data(self, cr, uid, id, default=None, context=None): + """executed for all the objects down the hierarchy during copy""" + if not default: + default = {} + default.setdefault('code', False) + default.setdefault('line_ids', []) + return super(account_analytic_account, self).copy_data(cr, uid, id, default, context=context) + def copy(self, cr, uid, id, default=None, context=None): + """ executed only on the toplevel copied object of the hierarchy. + Subobject are actually copied with copy_data""" if not default: default = {} analytic = self.browse(cr, uid, id, context=context) default.update( - code=False, - line_ids=[], name=_("%s (copy)") % (analytic['name'])) return super(account_analytic_account, self).copy(cr, uid, id, default, context=context) diff --git a/addons/email_template/wizard/mail_compose_message.py b/addons/email_template/wizard/mail_compose_message.py index 1a56c07d223..8fafaaddd8c 100644 --- a/addons/email_template/wizard/mail_compose_message.py +++ b/addons/email_template/wizard/mail_compose_message.py @@ -79,6 +79,11 @@ class mail_compose_message(osv.TransientModel): wizard_context['mail_auto_delete'] = wizard.template_id.auto_delete # mass mailing: use template auto_delete value -> note, for emails mass mailing only if not wizard.attachment_ids or wizard.composition_mode == 'mass_mail' or not wizard.template_id: continue + template = wizard.template_id + # template specific outgoing mail server is lost in super send_mail + # store them in the context to avoid falling back to default values + if template.mail_server_id: + wizard_context['mail_server_id'] = template.mail_server_id.id new_attachment_ids = [] for attachment in wizard.attachment_ids: if attachment in wizard.template_id.attachment_ids: diff --git a/addons/mail/mail_followers.py b/addons/mail/mail_followers.py index 7816c3c9044..10b9c40da7f 100644 --- a/addons/mail/mail_followers.py +++ b/addons/mail/mail_followers.py @@ -191,6 +191,8 @@ class mail_notification(osv.Model): 'recipient_ids': [(4, id) for id in email_pids], 'references': references, } + if 'mail_server_id' in context: + mail_values['mail_server_id'] = context['mail_server_id'] email_notif_id = self.pool.get('mail.mail').create(cr, uid, mail_values, context=context) if force_send: self.pool.get('mail.mail').send(cr, uid, [email_notif_id], context=context) diff --git a/addons/purchase/stock.py b/addons/purchase/stock.py index bae6d1c53d0..976ed8b33dd 100644 --- a/addons/purchase/stock.py +++ b/addons/purchase/stock.py @@ -86,7 +86,12 @@ class stock_picking(osv.osv): def _get_price_unit_invoice(self, cursor, user, move_line, type): if move_line.purchase_line_id: if move_line.purchase_line_id.order_id.invoice_method == 'picking': - return move_line.price_unit + price_unit = move_line.price_unit + order = move_line.purchase_line_id.order_id + if order.currency_id.id != order.company_id.currency_id.id: + price_unit = self.pool.get('res.currency').compute(cursor, user, + order.company_id.currency_id.id, order.currency_id.id, move_line.price_unit, round=False, context=dict({}, date=order.date_order)) + return price_unit else: return move_line.purchase_line_id.price_unit return super(stock_picking, self)._get_price_unit_invoice(cursor, user, move_line, type) diff --git a/addons/stock/stock.py b/addons/stock/stock.py index 9b5851141cd..0acf8a229a7 100644 --- a/addons/stock/stock.py +++ b/addons/stock/stock.py @@ -29,7 +29,7 @@ from openerp.osv import fields, osv, orm from openerp.tools.translate import _ from openerp import workflow from openerp import tools -from openerp.tools import float_compare, DEFAULT_SERVER_DATETIME_FORMAT +from openerp.tools import float_compare, float_round, DEFAULT_SERVER_DATETIME_FORMAT import openerp.addons.decimal_precision as dp import logging _logger = logging.getLogger(__name__) @@ -1430,7 +1430,15 @@ class stock_production_lot(osv.osv): """ Searches Ids of products @return: Ids of locations """ - locations = self.pool.get('stock.location').search(cr, uid, [('usage', '=', 'internal')]) + if context is None: + context = {} + + if 'location_id' not in context: + locations = self.pool['stock.location'].search( + cr, uid, [('usage', '=', 'internal')], context=context) + else: + locations = context['location_id'] and [context['location_id']] or [] + cr.execute('''select prodlot_id, sum(qty) @@ -1601,7 +1609,7 @@ class stock_move(osv.osv): "the product reservation, and should be done with care." ), 'product_uom': fields.many2one('product.uom', 'Unit of Measure', required=True,states={'done': [('readonly', True)]}), - 'product_uos_qty': fields.float('Quantity (UOS)', digits_compute=dp.get_precision('Product Unit of Measure'), states={'done': [('readonly', True)]}), + 'product_uos_qty': fields.float('Quantity (UOS)', digits_compute=dp.get_precision('Product UoS'), states={'done': [('readonly', True)]}), 'product_uos': fields.many2one('product.uom', 'Product UOS', states={'done': [('readonly', True)]}), 'product_packaging': fields.many2one('product.packaging', 'Packaging', help="It specifies attributes of packaging like type, quantity of packaging,etc."), @@ -1856,7 +1864,8 @@ class stock_move(osv.osv): break if product_uos and product_uom and (product_uom != product_uos): - result['product_uos_qty'] = product_qty * uos_coeff['uos_coeff'] + precision = self.pool.get('decimal.precision').precision_get(cr, uid, 'Product UoS') + result['product_uos_qty'] = float_round(product_qty * uos_coeff['uos_coeff'], precision_digits=precision) else: result['product_uos_qty'] = product_qty @@ -1886,7 +1895,8 @@ class stock_move(osv.osv): # The clients should call onchange_quantity too anyway if product_uos and product_uom and (product_uom != product_uos): - result['product_qty'] = product_uos_qty / uos_coeff['uos_coeff'] + precision = self.pool.get('decimal.precision').precision_get(cr, uid, 'Product Unit of Measure') + result['product_qty'] = float_round(product_uos_qty / uos_coeff['uos_coeff'], precision_digits=precision) else: result['product_qty'] = product_uos_qty return {'value': result} diff --git a/openerp/addons/base/ir/ir_attachment.py b/openerp/addons/base/ir/ir_attachment.py index 2ef2cd98a69..cd4103e93ad 100644 --- a/openerp/addons/base/ir/ir_attachment.py +++ b/openerp/addons/base/ir/ir_attachment.py @@ -134,7 +134,7 @@ class ir_attachment(osv.osv): else: result[attach.id] = attach.db_datas if bin_size: - result[attach.id] = int(result[attach.id]) + result[attach.id] = int(result[attach.id] or 0) return result