diff options
| author | Christophe Simonis <chs@odoo.com> | 2016-09-15 16:45:13 +0200 |
|---|---|---|
| committer | Christophe Simonis <chs@odoo.com> | 2016-09-15 16:45:13 +0200 |
| commit | 784eaf28dce166d3a75dcbae79339b1676aad01a (patch) | |
| tree | 18621e9e32b31a9c92fe26e0d8db74fa3b74fa71 | |
| parent | fa7430c157a095d2a779245cefa704e63f2ffde1 (diff) | |
| parent | 6647e1f8a98b0fac4310a17c016df0a1a33e13b7 (diff) | |
[MERGE] forward port branch 9.0 up to 6647e1f
| -rw-r--r-- | addons/account/models/account.py | 19 | ||||
| -rw-r--r-- | addons/account/models/account_move.py | 2 | ||||
| -rw-r--r-- | addons/account/static/src/js/account_reconciliation_widgets.js | 20 | ||||
| -rw-r--r-- | addons/barcodes/static/src/js/form_view_barcode_handler.js | 5 | ||||
| -rw-r--r-- | addons/stock/procurement.py | 2 | ||||
| -rw-r--r-- | addons/stock/stock.py | 16 | ||||
| -rw-r--r-- | addons/web/static/src/js/views/list_view.js | 4 | ||||
| -rw-r--r-- | addons/web_editor/static/src/xml/snippets.xml | 2 | ||||
| -rw-r--r-- | addons/website_quote/controllers/main.py | 2 | ||||
| -rw-r--r-- | openerp/addons/base/ir/ir_http.py | 2 | ||||
| -rw-r--r-- | openerp/addons/test_new_api/models.py | 9 | ||||
| -rw-r--r-- | openerp/addons/test_new_api/tests/test_onchange.py | 27 | ||||
| -rw-r--r-- | openerp/addons/test_new_api/views.xml | 1 | ||||
| -rw-r--r-- | openerp/fields.py | 4 | ||||
| -rw-r--r-- | openerp/modules/module.py | 2 | ||||
| -rw-r--r-- | openerp/tools/convert.py | 3 |
16 files changed, 103 insertions, 17 deletions
diff --git a/addons/account/models/account.py b/addons/account/models/account.py index 87d5d7e7f1f..262773414e8 100644 --- a/addons/account/models/account.py +++ b/addons/account/models/account.py @@ -686,7 +686,18 @@ class AccountTax(models.Model): # the 'Account' decimal precision + 5), and that way it's like # rounding after the sum of the tax amounts of each line prec = currency.decimal_places - if company_id.tax_calculation_rounding_method == 'round_globally' or not bool(self.env.context.get("round", True)): + + # In some cases, it is necessary to force/prevent the rounding of the tax and the total + # amounts. For example, in SO/PO line, we don't want to round the price unit at the + # precision of the currency. + # The context key 'round' allows to force the standard behavior. + round_tax = False if company_id.tax_calculation_rounding_method == 'round_globally' else True + round_total = True + if 'round' in self.env.context: + round_tax = bool(self.env.context['round']) + round_total = bool(self.env.context['round']) + + if not round_tax: prec += 5 total_excluded = total_included = base = round(price_unit * quantity, prec) @@ -705,7 +716,7 @@ class AccountTax(models.Model): continue tax_amount = tax._compute_amount(base, price_unit, quantity, product, partner) - if company_id.tax_calculation_rounding_method == 'round_globally' or not bool(self.env.context.get("round", True)): + if not round_tax: tax_amount = round(tax_amount, prec) else: tax_amount = currency.round(tax_amount) @@ -731,8 +742,8 @@ class AccountTax(models.Model): return { 'taxes': sorted(taxes, key=lambda k: k['sequence']), - 'total_excluded': currency.round(total_excluded) if bool(self.env.context.get("round", True)) else total_excluded, - 'total_included': currency.round(total_included) if bool(self.env.context.get("round", True)) else total_included, + 'total_excluded': currency.round(total_excluded) if round_total else total_excluded, + 'total_included': currency.round(total_included) if round_total else total_included, 'base': base, } diff --git a/addons/account/models/account_move.py b/addons/account/models/account_move.py index 8efbd9e3c7a..48dc26141b8 100644 --- a/addons/account/models/account_move.py +++ b/addons/account/models/account_move.py @@ -1087,7 +1087,7 @@ class AccountMoveLine(models.Model): taxes = self.env['account.tax'].browse(tax_ids) currency = self.env['res.currency'].browse(vals.get('currency_id')) partner = self.env['res.partner'].browse(vals.get('partner_id')) - res = taxes.compute_all(amount, + res = taxes.with_context(round=True).compute_all(amount, currency, 1, vals.get('product_id'), partner) # Adjust line amount if any tax is price_include if abs(res['total_excluded']) < abs(amount): diff --git a/addons/account/static/src/js/account_reconciliation_widgets.js b/addons/account/static/src/js/account_reconciliation_widgets.js index cb84ee74b23..c32a1a2850a 100644 --- a/addons/account/static/src/js/account_reconciliation_widgets.js +++ b/addons/account/static/src/js/account_reconciliation_widgets.js @@ -18,6 +18,7 @@ var pyeval = require('web.pyeval'); var web_client = require('web.web_client'); var parse_value = require('web.web_client'); var Widget = require('web.Widget'); +var session = require('web.session'); var FieldMany2One = core.form_widget_registry.get('many2one'); var FieldChar = core.form_widget_registry.get('char'); @@ -76,8 +77,6 @@ var abstractReconciliation = Widget.extend(ControlPanelMixin, { this.action_manager = this.findAncestor(function(ancestor){ return ancestor instanceof ActionManager }); this.crash_manager = new CrashManager(); - // Method that tests if a monetary amount == 0, we use 4 digits because no currency uses more - this.monetaryIsZero = _.partial(utils.float_is_zero, _, 4); this.formatCurrencies; // Method that formats the currency ; loaded from the server this.model_res_users = new Model("res.users"); this.model_tax = new Model("account.tax"); @@ -371,6 +370,11 @@ var abstractReconciliation = Widget.extend(ControlPanelMixin, { if (line.ref && line.ref !== line.name) line.q_label = line.q_label + " : " + line.ref; }, + + // Method that tests if a monetary amount == 0, we use 4 digits because no currency uses more + monetaryIsZero: function(amount, digits) { + return utils.float_is_zero(amount, digits === undefined ? 4 : digits); + }, }); var abstractReconciliationLine = Widget.extend({ @@ -1875,7 +1879,11 @@ var bankStatementReconciliationLine = abstractReconciliationLine.extend({ // Find out if the counterpart is lower than, equal or greater than the transaction being reconciled var balance_type = undefined; - if (self.monetaryIsZero(self.get("balance"))) balance_type = "equal"; + var digits = 4; + if (this.get("currency_id") && session.get_currency(this.get("currency_id"))) { + digits = session.get_currency(this.get("currency_id")).digits[1]; + } + if (self.monetaryIsZero(self.get("balance"), digits)) balance_type = "equal"; else if (self.get("balance") * self.st_line.amount > 0) balance_type = "greater"; else if (self.get("balance") * self.st_line.amount < 0) balance_type = "lower"; @@ -2070,7 +2078,11 @@ var bankStatementReconciliationLine = abstractReconciliationLine.extend({ var payment_aml = _.filter(this.get("mv_lines_selected"), function(line) { return line.already_paid }); var payment_aml_ids = _.collect(payment_aml, function(line) { return line.id }); var new_aml_dicts = this.prepareCreatedMoveLinesForPersisting(this.getCreatedLines()); - if (! self.monetaryIsZero(this.get("balance"))) new_aml_dicts.push(this.prepareOpenBalanceForPersisting()); + var digits = 4; + if (this.get("currency_id") && session.get_currency(this.get("currency_id"))) { + digits = session.get_currency(this.get("currency_id")).digits[1]; + } + if (! self.monetaryIsZero(this.get("balance"), digits)) new_aml_dicts.push(this.prepareOpenBalanceForPersisting()); return { 'counterpart_aml_dicts': counterpart_aml_dicts, 'payment_aml_ids': payment_aml_ids, diff --git a/addons/barcodes/static/src/js/form_view_barcode_handler.js b/addons/barcodes/static/src/js/form_view_barcode_handler.js index a6fa4b55adf..b00bce51c5e 100644 --- a/addons/barcodes/static/src/js/form_view_barcode_handler.js +++ b/addons/barcodes/static/src/js/form_view_barcode_handler.js @@ -52,6 +52,11 @@ var FormViewBarcodeHandler = common.AbstractField.extend(BarcodeHandlerMixin, { } }, + destroy: function () { + this.stop_listening(); + this._super.apply(this, arguments); + }, + _display_no_edit_mode_warning: function() { this.do_warn(_t('Error : Document not editable'), _t('To modify this document, please first start edition.')); }, diff --git a/addons/stock/procurement.py b/addons/stock/procurement.py index 6d1a045ece8..9c8a7c45dae 100644 --- a/addons/stock/procurement.py +++ b/addons/stock/procurement.py @@ -423,7 +423,7 @@ class procurement_order(osv.osv): def _get_orderpoint_date_planned(self, cr, uid, orderpoint, start_date, context=None): days = orderpoint.lead_days or 0.0 - if orderpoint.lead_type=='purchase': + if orderpoint.lead_type == 'supplier': # These days will be substracted when creating the PO days += orderpoint.product_id._select_seller(orderpoint.product_id).delay or 0.0 date_planned = start_date + relativedelta(days=days) diff --git a/addons/stock/stock.py b/addons/stock/stock.py index 8291aedc1a4..34b3c444838 100644 --- a/addons/stock/stock.py +++ b/addons/stock/stock.py @@ -1748,6 +1748,10 @@ class stock_picking(models.Model): packlots_transfer = [(4, x.id) for x in operation.pack_lot_ids] stock_operation_obj.write(cr, uid, [new_operation], {'pack_lot_ids': packlots_transfer}, context=context) + # the stock.pack.operation.lot records now belong to the new, packaged stock.pack.operation + # we have to create new ones with new quantities for our original, unfinished stock.pack.operation + stock_operation_obj._copy_remaining_pack_lot_ids(cr, uid, new_operation, operation.id, context=context) + op = stock_operation_obj.browse(cr, uid, new_operation, context=context) pack_operation_ids.append(op.id) if operations: @@ -4512,6 +4516,7 @@ class stock_pack_operation(osv.osv): if pack.product_qty - pack.qty_done > 0.0 and pack.qty_done < pack.product_qty: pack2 = self.copy(cr, uid, pack.id, default={'qty_done': 0.0, 'product_qty': pack.product_qty - pack.qty_done}, context=context) self.write(cr, uid, [pack.id], {'product_qty': pack.qty_done}, context=context) + self._copy_remaining_pack_lot_ids(cr, uid, pack.id, pack2, context=context) else: raise UserError(_('The quantity to split should be smaller than the quantity To Do. ')) return True @@ -4596,6 +4601,17 @@ class stock_pack_operation(osv.osv): 'context': context, } + def _copy_remaining_pack_lot_ids(self, cr, uid, id, new_operation_id, context=None): + stock_pack_operation_lot_obj = self.pool["stock.pack.operation.lot"] + old_operation = self.browse(cr, uid, id, context=context) + + for pack_lot_id in old_operation.pack_lot_ids: + new_qty_todo = pack_lot_id.qty_todo - pack_lot_id.qty + + if float_compare(new_qty_todo, 0, precision_rounding=old_operation.product_uom_id.rounding) > 0: + stock_pack_operation_lot_obj.copy(cr, uid, pack_lot_id.id, {'operation_id': new_operation_id, + 'qty_todo': new_qty_todo, + 'qty': 0}, context=context) class stock_pack_operation_lot(osv.osv): _name = "stock.pack.operation.lot" diff --git a/addons/web/static/src/js/views/list_view.js b/addons/web/static/src/js/views/list_view.js index 0923776c951..bd0abeebccc 100644 --- a/addons/web/static/src/js/views/list_view.js +++ b/addons/web/static/src/js/views/list_view.js @@ -1182,8 +1182,8 @@ ListView.List = Class.extend( /** @lends instance.web.ListView.List# */{ _(names).pluck(1).join(', ')); record.set(column.id, ids); }); - // temp empty value - record.set(column.id, false); + // temporary empty display name + record.set(column.id + '__display', false); } } return column.format(record.toForm().data, { diff --git a/addons/web_editor/static/src/xml/snippets.xml b/addons/web_editor/static/src/xml/snippets.xml index cbf1e6bef70..3fa6216c7f9 100644 --- a/addons/web_editor/static/src/xml/snippets.xml +++ b/addons/web_editor/static/src/xml/snippets.xml @@ -60,7 +60,7 @@ <!-- t-field options --> <t t-name="web_editor.many2one.button"> - <div class="dropdown"> + <div class="btn-group"> <a href="#" class="btn btn-default btn-sm dropdown-toggle" data-toggle="dropdown" data-hover="dropdown" title="Search Contact"><i class="fa fa-search"></i></a> <ul class="dropdown-menu contact_menu" role="menu"> <li><a><input href="#" type="email" placeholder="Search"/></a></li> diff --git a/addons/website_quote/controllers/main.py b/addons/website_quote/controllers/main.py index f472cbdfea3..a5e7b6c85ac 100644 --- a/addons/website_quote/controllers/main.py +++ b/addons/website_quote/controllers/main.py @@ -30,7 +30,7 @@ class sale_quote(http.Controller): if request.session.get('view_quote',False)!=now: request.session['view_quote'] = now body=_('Quotation viewed by customer') - _message_post_helper(res_model='sale.order', res_id=order.id, message=body, token=token, token_field="access_token", message_type='notification') + _message_post_helper(res_model='sale.order', res_id=order.id, message=body, token=token, token_field="access_token", message_type='notification', subtype="mail.mt_note", partner_ids=order.user_id.partner_id.ids) days = 0 if order.validity_date: days = (datetime.datetime.strptime(order.validity_date, '%Y-%m-%d') - datetime.datetime.now()).days + 1 diff --git a/openerp/addons/base/ir/ir_http.py b/openerp/addons/base/ir/ir_http.py index d8ece72e1b1..dde75a64a76 100644 --- a/openerp/addons/base/ir/ir_http.py +++ b/openerp/addons/base/ir/ir_http.py @@ -302,7 +302,7 @@ class ir_http(osv.AbstractModel): # cache etag = hasattr(request, 'httprequest') and request.httprequest.headers.get('If-None-Match') - retag = hashlib.md5(last_update).hexdigest() + retag = '"%s"' % hashlib.md5(last_update).hexdigest() status = status or (304 if etag == retag else 200) headers.append(('ETag', retag)) headers.append(('Cache-Control', 'max-age=%s' % (STATIC_CACHE if unique else 0))) diff --git a/openerp/addons/test_new_api/models.py b/openerp/addons/test_new_api/models.py index b9e0ffc0957..f6d533d29f1 100644 --- a/openerp/addons/test_new_api/models.py +++ b/openerp/addons/test_new_api/models.py @@ -82,6 +82,7 @@ class Category(models.Model): name = fields.Char(required=True) color = fields.Integer('Color Index') parent = fields.Many2one('test_new_api.category') + root_categ = fields.Many2one(_name, compute='_compute_root_categ') display_name = fields.Char(compute='_compute_display_name', inverse='_inverse_display_name') dummy = fields.Char(store=False) discussions = fields.Many2many('test_new_api.discussion', 'test_new_api_discussion_category', @@ -95,6 +96,14 @@ class Category(models.Model): else: self.display_name = self.name + @api.depends('parent') + def _compute_root_categ(self): + for cat in self: + current = cat + while current.parent: + current = current.parent + cat.root_categ = current + @api.one def _inverse_display_name(self): names = self.display_name.split('/') diff --git a/openerp/addons/test_new_api/tests/test_onchange.py b/openerp/addons/test_new_api/tests/test_onchange.py index adecda3e4aa..8516f9394e2 100644 --- a/openerp/addons/test_new_api/tests/test_onchange.py +++ b/openerp/addons/test_new_api/tests/test_onchange.py @@ -75,6 +75,33 @@ class TestOnChange(common.TransactionCase): result = self.Message.onchange(values, 'body', field_onchange) self.assertNotIn('name', result['value']) + def test_onchange_many2one(self): + Category = self.env['test_new_api.category'] + + field_onchange = Category._onchange_spec() + self.assertEqual(field_onchange.get('parent'), '1') + + root = Category.create(dict(name='root')) + + values = { + 'name': 'test', + 'parent': root.id, + 'root_categ': False, + } + + self.env.invalidate_all() + result = Category.onchange(values, 'parent', field_onchange).get('value', {}) + self.assertIn('root_categ', result) + self.assertEqual(result['root_categ'], root.name_get()[0]) + + values.update(result) + values['parent'] = False + + self.env.invalidate_all() + result = Category.onchange(values, 'parent', field_onchange).get('value', {}) + self.assertIn('root_categ', result) + self.assertIs(result['root_categ'], False) + def test_onchange_one2many(self): """ test the effect of onchange() on one2many fields """ BODY = "What a beautiful day!" diff --git a/openerp/addons/test_new_api/views.xml b/openerp/addons/test_new_api/views.xml index 9cc5c6ce2ce..b8e56e1119c 100644 --- a/openerp/addons/test_new_api/views.xml +++ b/openerp/addons/test_new_api/views.xml @@ -207,6 +207,7 @@ <group> <field name="name"/> <field name="parent"/> + <field name="root_categ"/> <field name="dummy"/> </group> </sheet> diff --git a/openerp/fields.py b/openerp/fields.py index a9a86fbd681..a60f8223539 100644 --- a/openerp/fields.py +++ b/openerp/fields.py @@ -1733,6 +1733,10 @@ class Many2one(_Relational): def convert_to_display_name(self, value, record=None): return ustr(value.display_name) + def convert_to_onchange(self, value, fnames=None): + if not value.id: + return False + return super(Many2one, self).convert_to_onchange(value, fnames) class UnionUpdate(SpecialValue): """ Placeholder for a value update; when this value is taken from the cache, diff --git a/openerp/modules/module.py b/openerp/modules/module.py index c7b01b99736..4f953091dd5 100644 --- a/openerp/modules/module.py +++ b/openerp/modules/module.py @@ -158,7 +158,7 @@ def get_module_path(module, downloaded=False, display_warning=True): """ initialize_sys_path() for adp in ad_paths: - if os.path.exists(opj(adp, module)) or os.path.exists(opj(adp, '%s.zip' % module)): + if os.path.exists(opj(adp, module, MANIFEST)) or os.path.exists(opj(adp, '%s.zip' % module)): return opj(adp, module) if downloaded: diff --git a/openerp/tools/convert.py b/openerp/tools/convert.py index 68ed0b4ffbf..f7c5677fea8 100644 --- a/openerp/tools/convert.py +++ b/openerp/tools/convert.py @@ -743,7 +743,8 @@ form: module.record_id""" % (xml_id,) record.append(Field(name, name='name')) record.append(Field(full_tpl_id, name='key')) record.append(Field("qweb", name='type')) - record.append(Field(el.get('priority', "16"), name='priority')) + if 'priority' in el.attrib: + record.append(Field(el.get('priority'), name='priority')) if 'inherit_id' in el.attrib: record.append(Field(name='inherit_id', ref=el.get('inherit_id'))) if 'website_id' in el.attrib: |
