From 8e7e6dfaa3089d054a0bc203567b0b05b5df30d8 Mon Sep 17 00:00:00 2001 From: sebalix Date: Fri, 8 Aug 2014 14:13:58 +0200 Subject: [PATCH 01/50] [FIX] web_diagram, unlink connectors/transitions between nodes from the diagram view (transitions IDs sent to the web client were strings instead of integers) --- addons/web_diagram/controllers/main.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/addons/web_diagram/controllers/main.py b/addons/web_diagram/controllers/main.py index d9a435f1924..0e89eb13db2 100644 --- a/addons/web_diagram/controllers/main.py +++ b/addons/web_diagram/controllers/main.py @@ -48,7 +48,7 @@ class DiagramView(openerp.http.Controller): for tr in transitions: list_tr.append(tr) connectors.setdefault(tr, { - 'id': tr, + 'id': int(tr), 's_id': transitions[tr][0], 'd_id': transitions[tr][1] }) From 79ebe1060d7a6303dbd5ff6e401aec5337161883 Mon Sep 17 00:00:00 2001 From: Martin Trigaux Date: Thu, 23 Oct 2014 17:14:19 +0200 Subject: [PATCH 02/50] [FIX] product: pricelist, uom and price_surcharge The price_surcharge attribute must be computed based on the reference unit of measure (divided by the factor). This is to make sure than 12 units and 1 dozen have the same price after pricelist computation (opw 599727). Added test checking the correctness of pricelist computation based on unit of measures. --- addons/product/pricelist.py | 7 ++- addons/product/tests/__init__.py | 3 +- addons/product/tests/test_pricelist.py | 70 ++++++++++++++++++++++++++ 3 files changed, 78 insertions(+), 2 deletions(-) create mode 100644 addons/product/tests/test_pricelist.py diff --git a/addons/product/pricelist.py b/addons/product/pricelist.py index d4ed2567afc..0dd6b45acda 100644 --- a/addons/product/pricelist.py +++ b/addons/product/pricelist.py @@ -297,7 +297,12 @@ class product_pricelist(osv.osv): price = price * (1.0+(res['price_discount'] or 0.0)) if res['price_round']: price = tools.float_round(price, precision_rounding=res['price_round']) - price += (res['price_surcharge'] or 0.0) + if context.get('uom'): + # compute price_surcharge based on reference uom + factor = product_uom_obj.browse(cr, uid, context.get('uom'), context=context).factor + else: + factor = 1.0 + price += (res['price_surcharge'] or 0.0) / factor if res['price_min_margin']: price = max(price, price_limit+res['price_min_margin']) if res['price_max_margin']: diff --git a/addons/product/tests/__init__.py b/addons/product/tests/__init__.py index 850189ac0f4..4a1ce43351b 100644 --- a/addons/product/tests/__init__.py +++ b/addons/product/tests/__init__.py @@ -1,5 +1,6 @@ -from . import test_uom +from . import test_uom, test_pricelist fast_suite = [ test_uom, + test_pricelist ] diff --git a/addons/product/tests/test_pricelist.py b/addons/product/tests/test_pricelist.py new file mode 100644 index 00000000000..dd9130e1be5 --- /dev/null +++ b/addons/product/tests/test_pricelist.py @@ -0,0 +1,70 @@ +from openerp.tests.common import TransactionCase + +class TestPricelist(TransactionCase): + """Tests for unit of measure conversion""" + + def setUp(self): + super(TestPricelist, self).setUp() + cr, uid, context = self.cr, self.uid, {} + self.ir_model_data = self.registry('ir.model.data') + self.product_product = self.registry('product.product') + self.product_pricelist = self.registry('product.pricelist') + self.uom = self.registry('product.uom') + + self.usb_adapter_id = self.ir_model_data.get_object_reference(cr, uid, 'product', 'product_product_48')[1] + self.datacard_id = self.ir_model_data.get_object_reference(cr, uid, 'product', 'product_template_46')[1] + self.unit_id = self.ir_model_data.get_object_reference(cr, uid, 'product', 'product_uom_unit')[1] + self.dozen_id = self.ir_model_data.get_object_reference(cr, uid, 'product', 'product_uom_dozen')[1] + + self.public_pricelist_id = self.ir_model_data.get_object_reference(cr, uid, 'product', 'list0')[1] + self.sale_pricelist_id = self.product_pricelist.create(cr, uid, { + 'name': 'Sale pricelist', + 'type': 'sale', + 'version_id': [(0, 0, { + 'name': 'v1.0', + 'items_id': [(0, 0, { + 'name': 'Discount 10%', + 'base': 1, # based on public price + 'price_discount': -0.1, + 'product_id': self.usb_adapter_id + }), (0, 0, { + 'name': 'Discount -0.5', + 'base': 1, # based on public price + 'price_surcharge': -0.5, + 'product_id': self.datacard_id + })] + })] + }, context=context) + + def test_10_discount(self): + # Make sure the price using a pricelist is the same than without after + # applying the computation manually + cr, uid, context = self.cr, self.uid, {} + + public_context = dict(context, pricelist=self.public_pricelist_id) + pricelist_context = dict(context, pricelist=self.sale_pricelist_id) + + usb_adapter_without_pricelist = self.product_product.browse(cr, uid, self.usb_adapter_id, context=public_context) + usb_adapter_with_pricelist = self.product_product.browse(cr, uid, self.usb_adapter_id, context=pricelist_context) + self.assertEqual(usb_adapter_with_pricelist.price, usb_adapter_without_pricelist.price*0.9) + + datacard_without_pricelist = self.product_product.browse(cr, uid, self.datacard_id, context=public_context) + datacard_with_pricelist = self.product_product.browse(cr, uid, self.datacard_id, context=pricelist_context) + self.assertEqual(datacard_with_pricelist.price, datacard_without_pricelist.price-0.5) + + # Make sure that changing the unit of measure does not break the unit + # price (after converting) + unit_context = dict(context, + pricelist=self.sale_pricelist_id, + uom=self.unit_id) + dozen_context = dict(context, + pricelist=self.sale_pricelist_id, + uom=self.dozen_id) + + usb_adapter_unit = self.product_product.browse(cr, uid, self.usb_adapter_id, context=unit_context) + usb_adapter_dozen = self.product_product.browse(cr, uid, self.usb_adapter_id, context=dozen_context) + self.assertAlmostEqual(usb_adapter_unit.price*12, usb_adapter_dozen.price) + + datacard_unit = self.product_product.browse(cr, uid, self.datacard_id, context=unit_context) + datacard_dozen = self.product_product.browse(cr, uid, self.datacard_id, context=dozen_context) + self.assertAlmostEqual(datacard_unit.price*12, datacard_dozen.price) From 42bf0a567003168029508e0b501d6aef7876e41f Mon Sep 17 00:00:00 2001 From: Martin Trigaux Date: Fri, 24 Oct 2014 16:07:55 +0200 Subject: [PATCH 03/50] [FIX] product_visible_discount: discount with different unit of measures The computation of the price without pricelist should take care of the unit of measure. e.g. if computing discount for objects in dozen (on a product with price in unit), returned unit price should be (price*12) where 12 is the factor to go from dozen to unit. Otherwise the compared prices (with and without pricelist) would not use the same unit of measure and the comparaison would be inconsistent. (opw 599727) --- addons/product_visible_discount/product_visible_discount.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/addons/product_visible_discount/product_visible_discount.py b/addons/product_visible_discount/product_visible_discount.py index 595deba1983..f173e844b27 100644 --- a/addons/product_visible_discount/product_visible_discount.py +++ b/addons/product_visible_discount/product_visible_discount.py @@ -44,6 +44,7 @@ class sale_order_line(osv.osv): fiscal_position=False, flag=False, context=None): def get_real_price(res_dict, product_id, qty, uom, pricelist): + """Retrieve the price before applying the pricelist""" item_obj = self.pool.get('product.pricelist.item') price_type_obj = self.pool.get('product.price.type') product_obj = self.pool.get('product.product') @@ -60,9 +61,8 @@ class sale_order_line(osv.osv): factor = 1.0 if uom and uom != product.uom_id.id: - product_uom_obj = self.pool.get('product.uom') - uom_data = product_uom_obj.browse(cr, uid, product.uom_id.id) - factor = uom_data.factor + # the unit price is in a different uom + factor = self.pool['product.uom']._compute_qty(cr, uid, uom, 1.0, product.uom_id.id) return product_read[field_name] * factor From 3dff2877a1795a3c296c26db096d0a77aa1b656a Mon Sep 17 00:00:00 2001 From: Josse Colpaert Date: Fri, 24 Oct 2014 18:21:01 +0200 Subject: [PATCH 04/50] [FIX] Operation does not necessary have product for extra move --- addons/stock/stock.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/addons/stock/stock.py b/addons/stock/stock.py index badd9ccb92d..ab0807b40bf 100644 --- a/addons/stock/stock.py +++ b/addons/stock/stock.py @@ -1267,7 +1267,7 @@ class stock_picking(osv.osv): 'product_id': product.id, 'product_uom': product.uom_id.id, 'product_uom_qty': remaining_qty, - 'name': _('Extra Move: ') + op.product_id.name, + 'name': _('Extra Move: ') + product.name, 'state': 'draft', } return res From c589b49afa77d112f68c096341bf92a4dbd1cdac Mon Sep 17 00:00:00 2001 From: Josse Colpaert Date: Sat, 25 Oct 2014 21:30:18 +0200 Subject: [PATCH 05/50] [FIX] When extra move for consumption lines, copy lot and when producing more than produced, create extra move --- addons/mrp/mrp.py | 17 +++++++++++++++-- 1 file changed, 15 insertions(+), 2 deletions(-) diff --git a/addons/mrp/mrp.py b/addons/mrp/mrp.py index 1791450d345..2a597336a78 100644 --- a/addons/mrp/mrp.py +++ b/addons/mrp/mrp.py @@ -915,9 +915,19 @@ class mrp_production(osv.osv): lot_id = False if wiz: lot_id = wiz.lot_id.id - new_moves = stock_mov_obj.action_consume(cr, uid, [produce_product.id], (subproduct_factor * production_qty_uom), + qty = min(subproduct_factor * production_qty_uom, produce_product.product_qty) #Needed when producing more than maximum quantity + new_moves = stock_mov_obj.action_consume(cr, uid, [produce_product.id], qty, location_id=produce_product.location_id.id, restrict_lot_id=lot_id, context=context) stock_mov_obj.write(cr, uid, new_moves, {'production_id': production_id}, context=context) + remaining_qty = subproduct_factor * production_qty_uom - qty + if remaining_qty: # In case you need to make more than planned + #consumed more in wizard than previously planned + extra_move_id = stock_mov_obj.copy(cr, uid, produce_product.id, default={'state': 'confirmed', + 'product_uom_qty': remaining_qty, + 'production_id': production_id}, context=context) + if extra_move_id: + stock_mov_obj.action_done(cr, uid, [extra_move_id], context=context) + if produce_product.product_id.id == production.product_id.id: main_production_move = produce_product.id @@ -936,13 +946,16 @@ class mrp_production(osv.osv): if consume['product_id'] != raw_material_line.product_id.id: continue consumed_qty = min(remaining_qty, raw_material_line.product_qty) - stock_mov_obj.action_consume(cr, uid, [raw_material_line.id], consumed_qty, raw_material_line.location_id.id, restrict_lot_id=consume['lot_id'], consumed_for=main_production_move, context=context) + stock_mov_obj.action_consume(cr, uid, [raw_material_line.id], consumed_qty, raw_material_line.location_id.id, + restrict_lot_id=consume['lot_id'], consumed_for=main_production_move, context=context) remaining_qty -= consumed_qty if remaining_qty: #consumed more in wizard than previously planned product = self.pool.get('product.product').browse(cr, uid, consume['product_id'], context=context) extra_move_id = self._make_consume_line_from_data(cr, uid, production, product, product.uom_id.id, remaining_qty, False, 0, context=context) if extra_move_id: + if consume['lot_id']: + stock_mov_obj.write(cr, uid, [extra_move_id], {'restrict_lot_id': consume['lot_id']}, context=context) stock_mov_obj.action_done(cr, uid, [extra_move_id], context=context) self.message_post(cr, uid, production_id, body=_("%s produced") % self._description, context=context) From 4a46fce95a7c4ddb6ffe85fc7b8e30ffa581690f Mon Sep 17 00:00:00 2001 From: Christophe Matthieu Date: Mon, 29 Sep 2014 16:04:03 +0200 Subject: [PATCH 06/50] [FIX] website_sale: Postal Code is required during checkout for countries without postal codes; Zip must be not required. --- addons/website_sale/controllers/main.py | 8 ++++---- addons/website_sale/views/templates.xml | 4 ++-- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/addons/website_sale/controllers/main.py b/addons/website_sale/controllers/main.py index 6399d55cb49..830b2bd2f90 100644 --- a/addons/website_sale/controllers/main.py +++ b/addons/website_sale/controllers/main.py @@ -432,10 +432,10 @@ class website_sale(http.Controller): return values - mandatory_billing_fields = ["name", "phone", "email", "street2", "city", "country_id", "zip"] - optional_billing_fields = ["street", "state_id", "vat", "vat_subjected"] - mandatory_shipping_fields = ["name", "phone", "street", "city", "country_id", "zip"] - optional_shipping_fields = ["state_id"] + mandatory_billing_fields = ["name", "phone", "email", "street2", "city", "country_id"] + optional_billing_fields = ["street", "state_id", "vat", "vat_subjected", "zip"] + mandatory_shipping_fields = ["name", "phone", "street", "city", "country_id"] + optional_shipping_fields = ["state_id", "zip"] def checkout_parse(self, address_type, data, remove_prefix=False): """ data is a dict OR a partner browse record diff --git a/addons/website_sale/views/templates.xml b/addons/website_sale/views/templates.xml index ef216beb6c8..929d2efd052 100644 --- a/addons/website_sale/views/templates.xml +++ b/addons/website_sale/views/templates.xml @@ -882,7 +882,7 @@
- +
@@ -946,7 +946,7 @@
- +
From 3986e8c509afe376955ff9089a207811b0a66056 Mon Sep 17 00:00:00 2001 From: Christophe Matthieu Date: Mon, 29 Sep 2014 17:12:49 +0200 Subject: [PATCH 07/50] [FIX] product: is_product_variant is all time false. result: can not change ean because the field is hidden in the view. Need to overwrite the function field on product.product to have the real value (true for product.product) --- addons/product/product.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/addons/product/product.py b/addons/product/product.py index 9f014ecca46..3f54cba24d1 100644 --- a/addons/product/product.py +++ b/addons/product/product.py @@ -558,7 +558,7 @@ class product_template(osv.osv): 'active': fields.boolean('Active', help="If unchecked, it will allow you to hide the product without removing it."), 'color': fields.integer('Color Index'), - 'is_product_variant': fields.function( _is_product_variant, type='boolean', string='Only one product variant'), + 'is_product_variant': fields.function( _is_product_variant, type='boolean', string='Is product variant'), 'attribute_line_ids': fields.one2many('product.attribute.line', 'product_tmpl_id', 'Product Attributes'), 'product_variant_ids': fields.one2many('product.product', 'product_tmpl_id', 'Products', required=True), @@ -925,6 +925,7 @@ class product_product(osv.osv): 'product.product': (lambda self, cr, uid, ids, c=None: ids, [], 10), }, select=True), 'attribute_value_ids': fields.many2many('product.attribute.value', id1='prod_id', id2='att_id', string='Attributes', readonly=True, ondelete='restrict'), + 'is_product_variant': fields.function( _is_product_variant_impl, type='boolean', string='Is product variant'), # image: all image fields are base64 encoded and PIL-supported 'image_variant': fields.binary("Variant Image", From 7ce3961693087a26dde73d77ced3193051d75b62 Mon Sep 17 00:00:00 2001 From: Christophe Matthieu Date: Tue, 30 Sep 2014 11:44:51 +0200 Subject: [PATCH 08/50] [FIX] website: update jQuery.transfo lib + adapt code --- addons/website/static/lib/jQuery.transfo.js | 48 +++++++++++++------ .../static/src/js/website.snippets.editor.js | 7 +-- 2 files changed, 38 insertions(+), 17 deletions(-) diff --git a/addons/website/static/lib/jQuery.transfo.js b/addons/website/static/lib/jQuery.transfo.js index 725e5a02216..855d6f48560 100644 --- a/addons/website/static/lib/jQuery.transfo.js +++ b/addons/website/static/lib/jQuery.transfo.js @@ -131,12 +131,13 @@ OTHER DEALINGS IN THE SOFTWARE. // generate all the controls markup var css = "box-sizing: border-box; position: absolute; background-color: #fff; border: 1px solid #ccc; width: 8px; height: 8px; margin-left: -4px; margin-top: -4px;"; transfo.$markup = $('' - + '
' + + '
' + + '
' + '
' - + '' - + '' - + '' - + '' + + '' + + '' + + '' + + '' + '
' + '
' + '
' @@ -147,6 +148,7 @@ OTHER DEALINGS IN THE SOFTWARE. + '
' + '
' + '
' + + '
' + '
'); transfo.$center = transfo.$markup.find(".transfo-scaler-mc"); @@ -165,12 +167,20 @@ OTHER DEALINGS IN THE SOFTWARE. _bind($this, transfo); _targetCss($this, transfo); + _stop_animation($this[0]); } function _overwriteOptions ($this, transfo, settings) { transfo.settings = $.extend(transfo.settings, settings || {}); } + function _stop_animation (target) { + target.style.webkitAnimationPlayState = "paused"; + target.style.animationPlayState = "paused"; + target.style.webkitTransition = "none"; + target.style.transition = "none"; + } + function _setOptions ($this, transfo) { var style = $this.attr("style") || ""; var transform = style.match(/transform\s*:([^;]+)/) ? style.match(/transform\s*:([^;]+)/)[1] : ""; @@ -182,7 +192,10 @@ OTHER DEALINGS IN THE SOFTWARE. transfo.settings.scaley= transform.indexOf('scaleY') != -1 ? parseFloat(transform.match(/scaleY\(([^)]+)\)/)[1]) : 1; transfo.settings.style = style.replace(/[^;]*transform[^;]+/g, '').replace(/;+/g, ';'); + $this.attr("style", transfo.settings.style); + _stop_animation($this[0]); + transfo.settings.pos = $this.offset(); transfo.settings.height = $this.innerHeight(); transfo.settings.width = $this.innerWidth(); @@ -205,7 +218,6 @@ OTHER DEALINGS IN THE SOFTWARE. } transfo.settings.css = window.getComputedStyle($this[0], null); - transfo.settings.pos = $this.offset(); transfo.settings.rotationStep = 5; transfo.settings.hide = false; @@ -226,7 +238,7 @@ OTHER DEALINGS IN THE SOFTWARE. } transfo.$markup.off().on("mousedown", mousedown); - transfo.$markup.find(">:not(.transfo-scaler-mc)").off().on("mousedown", mousedown); + transfo.$markup.find(".transfo-controls >:not(.transfo-scaler-mc)").off().on("mousedown", mousedown); } function _mouseDown($this, div, transfo, event) { @@ -339,6 +351,7 @@ OTHER DEALINGS IN THE SOFTWARE. settings.scaley = Math.round(settings.scaley*100)/100; _targetCss($this, transfo); + _stop_animation($this[0]); return false; } @@ -394,15 +407,22 @@ OTHER DEALINGS IN THE SOFTWARE. _setCss($this, settings.style, settings); - _setCss(transfo.$markup, - "position: absolute;" + - "top:" + settings.pos.top + "px;" + - "left:" + settings.pos.left + "px;" + + transfo.$markup.css({ + "position": "absolute", + "width": width + "px", + "height": height + "px", + "top": settings.pos.top + "px", + "left": settings.pos.left + "px" + }); + + var $controls = transfo.$markup.find('.transfo-controls'); + _setCss($controls, "width:" + width + "px;" + "height:" + height + "px;" + "cursor: move;", settings); - transfo.$markup.find(">").css("transform", "scaleX("+(1/settings.scalex)+") scaleY("+(1/settings.scaley)+")"); + + $controls.children().css("transform", "scaleX("+(1/settings.scalex)+") scaleY("+(1/settings.scaley)+")"); _showHide($this, transfo); @@ -412,10 +432,10 @@ OTHER DEALINGS IN THE SOFTWARE. function _showHide ($this, transfo) { transfo.$markup.css("z-index", transfo.settings.hide ? -1 : 1000); if (transfo.settings.hide) { - transfo.$markup.find(">").hide(); + transfo.$markup.find(".transfo-controls > *").hide(); transfo.$markup.find(".transfo-scaler-mc").show(); } else { - transfo.$markup.find(">").show(); + transfo.$markup.find(".transfo-controls > *").show(); } } diff --git a/addons/website/static/src/js/website.snippets.editor.js b/addons/website/static/src/js/website.snippets.editor.js index 480dc7ab1c4..6b4afd037b9 100644 --- a/addons/website/static/src/js/website.snippets.editor.js +++ b/addons/website/static/src/js/website.snippets.editor.js @@ -1497,10 +1497,11 @@ this.$target.transfo({ hide: true, callback: function () { - var pos = $(this).data("transfo").$center.offset(); + var center = $(this).data("transfo").$markup.find('.transfo-scaler-mc').offset(); + var $option = self.$overlay.find('.btn-group:first'); self.$overlay.css({ - 'top': pos.top, - 'left': pos.left, + 'top': center.top - $option.height()/2, + 'left': center.left, 'position': 'absolute', }); self.$overlay.find(".oe_overlay_options").attr("style", "width:0; left:0!important; top:0;"); From d8e67d647f6779984c241b71728904d223d09415 Mon Sep 17 00:00:00 2001 From: Christophe Matthieu Date: Mon, 29 Sep 2014 16:12:57 +0200 Subject: [PATCH 09/50] [FIX] website_sale_options: only one optional product is added to the cart and the quantity of the optional product don't change in the cart when we click on more or less. Fix the number of items displayed in the modal options. --- addons/website_sale/static/src/js/website_sale.js | 4 ++-- .../static/src/js/website_sale.js | 13 +++++++++++-- addons/website_sale_options/views/templates.xml | 1 + 3 files changed, 14 insertions(+), 4 deletions(-) diff --git a/addons/website_sale/static/src/js/website_sale.js b/addons/website_sale/static/src/js/website_sale.js index 34cf1fce863..dda494aac7e 100644 --- a/addons/website_sale/static/src/js/website_sale.js +++ b/addons/website_sale/static/src/js/website_sale.js @@ -95,7 +95,7 @@ $('.oe_website_sale').each(function () { $parent.find(".oe_default_price:first .oe_currency_value").html( price_to_str(+$(this).data('lst_price')) ); $parent.find(".oe_price:first .oe_currency_value").html(price_to_str(+$(this).data('price')) ); - var $img = $(this).closest('tr.js_product, .oe_website_sale').find('span[data-oe-model^="product."][data-oe-type="image"] img, img.product_detail_img'); + var $img = $(this).closest('tr.js_product, .oe_website_sale').find('span[data-oe-model^="product."][data-oe-type="image"] img:first, img.product_detail_img'); $img.attr("src", "/website/image/product.product/" + $(this).val() + "/image"); }); @@ -129,7 +129,7 @@ $('.oe_website_sale').each(function () { } if (product_id) { - var $img = $(this).closest('tr.js_product, .oe_website_sale').find('span[data-oe-model^="product."][data-oe-type="image"] img, img.product_detail_img'); + var $img = $(this).closest('tr.js_product, .oe_website_sale').find('span[data-oe-model^="product."][data-oe-type="image"] img:first, img.product_detail_img'); $img.attr("src", "/website/image/product.product/" + product_id + "/image"); $img.parent().attr('data-oe-model', 'product.product').attr('data-oe-id', product_id) .data('oe-model', 'product.product').data('oe-id', product_id); diff --git a/addons/website_sale_options/static/src/js/website_sale.js b/addons/website_sale_options/static/src/js/website_sale.js index 72a45139b83..3d8ecee4ebb 100644 --- a/addons/website_sale_options/static/src/js/website_sale.js +++ b/addons/website_sale_options/static/src/js/website_sale.js @@ -53,7 +53,7 @@ $(document).ready(function () { $(".js_remove .js_items").addClass("hidden"); $(".js_remove .js_item").removeClass("hidden"); } else { - $(".js_remove .js_items").removeClass("hidden").text($(".js_remove .js_items").text().replace(/[0-9.,]+/, qty)); + $(".js_remove .js_items").removeClass("hidden").text($(".js_remove .js_items:first").text().replace(/[0-9.,]+/, qty)); $(".js_remove .js_item").addClass("hidden"); } }); @@ -65,5 +65,14 @@ $(document).ready(function () { }); return false; }); - + + + $('#cart_products input.js_quantity').change(function () { + var value = $(this).val(); + var $next = $(this).closest('tr').next('.optional_product'); + while($next.length) { + $next.find('.js_quantity').text(value); + $next = $next.next('.optional_product'); + } + }); }); diff --git a/addons/website_sale_options/views/templates.xml b/addons/website_sale_options/views/templates.xml index 1f0a4303e61..2963666cc8c 100644 --- a/addons/website_sale_options/views/templates.xml +++ b/addons/website_sale_options/views/templates.xml @@ -131,6 +131,7 @@ Remove from cart + From c4ae1ef66d897cc134b354edc9bbb970f4072f5d Mon Sep 17 00:00:00 2001 From: Christophe Matthieu Date: Mon, 29 Sep 2014 12:26:13 +0200 Subject: [PATCH 10/50] [FIX] account.fiscal.position: Fix get_fiscal_position for auto apply: First match a fiscal position for the given country, then for a country group containing the country. If none found, search a fiscal position without country nor country group --- addons/account/partner.py | 23 ++++++++++++++++++++--- addons/account/partner_view.xml | 4 ++-- 2 files changed, 22 insertions(+), 5 deletions(-) diff --git a/addons/account/partner.py b/addons/account/partner.py index e154c407716..b402a33ed11 100644 --- a/addons/account/partner.py +++ b/addons/account/partner.py @@ -47,6 +47,16 @@ class account_fiscal_position(osv.osv): 'active': True, } + def _check_country(self, cr, uid, ids, context=None): + obj = self.browse(cr, uid, ids[0], context=context) + if obj.country_id and obj.country_group_id: + return False + return True + + _constraints = [ + (_check_country, 'You can not select a country and a group of countries', ['country_id', 'country_group_id']), + ] + @api.v7 def map_tax(self, cr, uid, fposition_id, taxes, context=None): if not taxes: @@ -115,10 +125,17 @@ class account_fiscal_position(osv.osv): domain = [ ('auto_apply', '=', True), '|', ('vat_required', '=', False), ('vat_required', '=', partner.vat_subjected), - '|', ('country_id', '=', None), ('country_id', '=', delivery.country_id.id), - '|', ('country_group_id', '=', None), ('country_group_id.country_ids', '=', delivery.country_id.id) ] - fiscal_position_ids = self.search(cr, uid, domain, context=context) + + fiscal_position_ids = self.search(cr, uid, domain + [('country_id', '=', delivery.country_id.id)], context=context, limit=1) + if fiscal_position_ids: + return fiscal_position_ids[0] + + fiscal_position_ids = self.search(cr, uid, domain + [('country_group_id.country_ids', '=', delivery.country_id.id)], context=context, limit=1) + if fiscal_position_ids: + return fiscal_position_ids[0] + + fiscal_position_ids = self.search(cr, uid, domain + [('country_id', '=', None), ('country_group_id', '=', None)], context=context, limit=1) if fiscal_position_ids: return fiscal_position_ids[0] return False diff --git a/addons/account/partner_view.xml b/addons/account/partner_view.xml index 36aa1ed409d..45d92f21d23 100644 --- a/addons/account/partner_view.xml +++ b/addons/account/partner_view.xml @@ -16,8 +16,8 @@ - - + + From 59cda06b00a327ad6f9293ca37c512f369bd0342 Mon Sep 17 00:00:00 2001 From: Christophe Matthieu Date: Mon, 29 Sep 2014 10:32:00 +0200 Subject: [PATCH 11/50] [FIX] website.sale: Double / triple digits do not get displayed correctly on ecommerce quantity selector --- addons/website_sale/static/src/css/website_sale.css | 6 ++++++ addons/website_sale/static/src/css/website_sale.sass | 4 ++++ 2 files changed, 10 insertions(+) diff --git a/addons/website_sale/static/src/css/website_sale.css b/addons/website_sale/static/src/css/website_sale.css index 40ce13f0d09..fa5dbd9c4b6 100644 --- a/addons/website_sale/static/src/css/website_sale.css +++ b/addons/website_sale/static/src/css/website_sale.css @@ -1,3 +1,4 @@ +@charset "utf-8"; /* ---- Default Styles ---- */ .oe_product { border: 1px solid rgba(100, 100, 100, 0.2); @@ -340,6 +341,11 @@ label.css_attribute_color.css_not_available { display: block; } +.oe_website_sale input.js_quantity { + min-width: 48px; + text-align: center; +} + /* ---- Publish managment and options ---- */ #products_grid .css_options { display: none; diff --git a/addons/website_sale/static/src/css/website_sale.sass b/addons/website_sale/static/src/css/website_sale.sass index d3a6b1943bc..5bb4e9c8f5f 100644 --- a/addons/website_sale/static/src/css/website_sale.sass +++ b/addons/website_sale/static/src/css/website_sale.sass @@ -287,6 +287,10 @@ label.css_attribute_color.css_not_available .discount .oe_default_price display: block +.oe_website_sale input.js_quantity + min-width: 48px + text-align: center + /* ---- Publish managment and options ---- */ #products_grid .css_options From 24ec228acc9cd185a8b00eca740fccf24ed789b2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?St=C3=A9phane=20Bidoul?= Date: Sat, 25 Oct 2014 15:19:20 +0200 Subject: [PATCH 12/50] [FIX] avoid leaving dangling account moves when deleting a bank statement Fixes #3292 --- addons/account/account_bank_statement.py | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/addons/account/account_bank_statement.py b/addons/account/account_bank_statement.py index f18da1e81ca..99afc92b9e7 100644 --- a/addons/account/account_bank_statement.py +++ b/addons/account/account_bank_statement.py @@ -383,12 +383,17 @@ class account_bank_statement(osv.osv): return {'value': res} def unlink(self, cr, uid, ids, context=None): + statement_line_obj = self.pool['account.bank.statement.line'] for item in self.browse(cr, uid, ids, context=context): if item.state != 'draft': raise osv.except_osv( _('Invalid Action!'), _('In order to delete a bank statement, you must first cancel it to delete related journal items.') ) + # Explicitly unlink bank statement lines + # so it will check that the related journal entries have + # been deleted first + statement_line_obj.unlink(cr, uid, [line.id for line in item.line_ids], context=context) return super(account_bank_statement, self).unlink(cr, uid, ids, context=context) def button_journal_entries(self, cr, uid, ids, context=None): @@ -809,7 +814,7 @@ class account_bank_statement_line(osv.osv): 'partner_id': fields.many2one('res.partner', 'Partner'), 'bank_account_id': fields.many2one('res.partner.bank','Bank Account'), 'account_id': fields.many2one('account.account', 'Account', help="This technical field can be used at the statement line creation/import time in order to avoid the reconciliation process on it later on. The statement line will simply create a counterpart on this account"), - 'statement_id': fields.many2one('account.bank.statement', 'Statement', select=True, required=True, ondelete='cascade'), + 'statement_id': fields.many2one('account.bank.statement', 'Statement', select=True, required=True, ondelete='restrict'), 'journal_id': fields.related('statement_id', 'journal_id', type='many2one', relation='account.journal', string='Journal', store=True, readonly=True), 'partner_name': fields.char('Partner Name', help="This field is used to record the third party name when importing bank statement in electronic format, when the partner doesn't exist yet in the database (or cannot be found)."), 'ref': fields.char('Reference'), From 8abd003ef0f1a59b57fbdd85f54b8aadad68bed2 Mon Sep 17 00:00:00 2001 From: Martin Trigaux Date: Mon, 27 Oct 2014 11:41:23 +0100 Subject: [PATCH 13/50] [FIX] product: reference in test --- addons/product/tests/test_pricelist.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/addons/product/tests/test_pricelist.py b/addons/product/tests/test_pricelist.py index dd9130e1be5..4e61cb11b75 100644 --- a/addons/product/tests/test_pricelist.py +++ b/addons/product/tests/test_pricelist.py @@ -12,7 +12,7 @@ class TestPricelist(TransactionCase): self.uom = self.registry('product.uom') self.usb_adapter_id = self.ir_model_data.get_object_reference(cr, uid, 'product', 'product_product_48')[1] - self.datacard_id = self.ir_model_data.get_object_reference(cr, uid, 'product', 'product_template_46')[1] + self.datacard_id = self.ir_model_data.get_object_reference(cr, uid, 'product', 'product_product_46')[1] self.unit_id = self.ir_model_data.get_object_reference(cr, uid, 'product', 'product_uom_unit')[1] self.dozen_id = self.ir_model_data.get_object_reference(cr, uid, 'product', 'product_uom_dozen')[1] From 230f55face5f212b48104de37669b4e6ab19a502 Mon Sep 17 00:00:00 2001 From: Frederic van der Essen Date: Mon, 27 Oct 2014 12:21:04 +0100 Subject: [PATCH 14/50] [FIX] web: cors rpc calls were missing the session cookie, resulting in new sessions being created for every call --- addons/web/static/src/js/openerpframework.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/addons/web/static/src/js/openerpframework.js b/addons/web/static/src/js/openerpframework.js index de8f5f660a3..007edae7220 100644 --- a/addons/web/static/src/js/openerpframework.js +++ b/addons/web/static/src/js/openerpframework.js @@ -1058,11 +1058,11 @@ openerp.Session = openerp.Class.extend(openerp.PropertiesMixin, { }, check_session_id: function() { var self = this; - if (this.avoid_recursion || self.use_cors) + if (this.avoid_recursion) return $.when(); if (this.session_id) return $.when(); // we already have the session id - if (this.override_session || ! this.origin_server) { + if (!this.use_cors && (this.override_session || ! this.origin_server)) { // If we don't use the origin server we consider we should always create a new session. // Even if some browsers could support cookies when using jsonp that behavior is // not consistent and the browser creators are tending to removing that feature. From d68d1d0434e870b7fed39665be2d8c33afe07eb6 Mon Sep 17 00:00:00 2001 From: Frederic van der Essen Date: Mon, 27 Oct 2014 12:28:47 +0100 Subject: [PATCH 15/50] [FIX] web: cors rpc calls were missing the session cookie, resulting in new sessions being created for every call --- addons/web/static/src/js/openerpframework.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/addons/web/static/src/js/openerpframework.js b/addons/web/static/src/js/openerpframework.js index 5dcc2197944..34fe523753c 100644 --- a/addons/web/static/src/js/openerpframework.js +++ b/addons/web/static/src/js/openerpframework.js @@ -1018,11 +1018,11 @@ openerp.Session = openerp.Class.extend(openerp.PropertiesMixin, { }, check_session_id: function() { var self = this; - if (this.avoid_recursion || self.use_cors) + if (this.avoid_recursion) return $.when(); if (this.session_id) return $.when(); // we already have the session id - if (this.override_session || ! this.origin_server) { + if (!this.use_cors && (this.override_session || ! this.origin_server)) { // If we don't use the origin server we consider we should always create a new session. // Even if some browsers could support cookies when using jsonp that behavior is // not consistent and the browser creators are tending to removing that feature. From 0790682296e43989e4e232189009c3ba380e32ba Mon Sep 17 00:00:00 2001 From: Denis Ledoux Date: Sat, 25 Oct 2014 14:26:52 +0200 Subject: [PATCH 16/50] [FIX] mail: read more show original message Instead of html cleaned html, to allow display blockquotes opw-614729 --- addons/mail/mail_message.py | 1 + addons/mail/static/src/js/mail.js | 12 +++++++++++- addons/mail/static/src/xml/mail.xml | 5 ++++- 3 files changed, 16 insertions(+), 2 deletions(-) diff --git a/addons/mail/mail_message.py b/addons/mail/mail_message.py index 8ac047fb77e..be88fa9de60 100644 --- a/addons/mail/mail_message.py +++ b/addons/mail/mail_message.py @@ -373,6 +373,7 @@ class mail_message(osv.Model): 'type': message.type, 'subtype': message.subtype_id.name if message.subtype_id else False, 'body': body_html, + 'body_original': message.body if message.type == 'email' else None, 'model': message.model, 'res_id': message.res_id, 'record_name': message.record_name, diff --git a/addons/mail/static/src/js/mail.js b/addons/mail/static/src/js/mail.js index 1f65058d4af..094a4f72de5 100644 --- a/addons/mail/static/src/js/mail.js +++ b/addons/mail/static/src/js/mail.js @@ -225,6 +225,7 @@ openerp.mail = function (session) { this.name = datasets.name || false, this.record_name = datasets.record_name || false, this.body = datasets.body || '', + this.body_original = datasets.body_original || '', this.vote_nb = datasets.vote_nb || 0, this.has_voted = datasets.has_voted || false, this.is_favorite = datasets.is_favorite || false, @@ -976,6 +977,7 @@ openerp.mail = function (session) { }, expender: function () { + var self = this; this.$('.oe_msg_body:first').expander({ slicePoint: this.options.truncate_limit, expandText: _t('read more'), @@ -983,7 +985,15 @@ openerp.mail = function (session) { detailClass: 'oe_msg_tail', moreClass: 'oe_mail_expand', lessClass: 'oe_mail_reduce', - }); + afterExpand: function(){ + self.$('.oe_msg_body').hide(); + self.$('.oe_msg_original').show(); + }, + onCollapse: function(){ + self.$('.oe_msg_body').show(); + self.$('.oe_msg_original').hide(); + }, + }); }, /** diff --git a/addons/mail/static/src/xml/mail.xml b/addons/mail/static/src/xml/mail.xml index 1e52105c739..e57b9d7d7a2 100644 --- a/addons/mail/static/src/xml/mail.xml +++ b/addons/mail/static/src/xml/mail.xml @@ -257,7 +257,10 @@
- + +
+
- - - +

From c6741fb537a5e4bf4280c20f59e9cd1b9d7e7d30 Mon Sep 17 00:00:00 2001 From: Florian Kisser Date: Thu, 7 Aug 2014 12:33:03 +0200 Subject: [PATCH 34/50] [FIX] sql_db: don't log dsn with unmasked passwords The dsn may contain the connection password of the database when not accessed from a psycopg connection object. Replace the unfiltered logs to use cxn.dsn avoiding password leakage in logs. Fixes #1433 --- openerp/sql_db.py | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/openerp/sql_db.py b/openerp/sql_db.py index 739c32a4f12..589da7ffe3b 100644 --- a/openerp/sql_db.py +++ b/openerp/sql_db.py @@ -477,8 +477,6 @@ class ConnectionPool(object): @locked def borrow(self, dsn): - self._debug('Borrow connection to %r', dsn) - # free dead and leaked connections for i, (cnx, _) in tools.reverse_enumerate(self._connections): if cnx.closed: @@ -503,7 +501,7 @@ class ConnectionPool(object): continue self._connections.pop(i) self._connections.append((cnx, True)) - self._debug('Existing connection found at index %d', i) + self._debug('Borrow existing connection to %r at index %d', cnx.dsn, i) return cnx @@ -546,11 +544,15 @@ class ConnectionPool(object): @locked def close_all(self, dsn=None): - _logger.info('%r: Close all connections to %r', self, dsn) + count = 0 + last = None for i, (cnx, used) in tools.reverse_enumerate(self._connections): if dsn is None or cnx._original_dsn == dsn: cnx.close() - self._connections.pop(i) + last = self._connections.pop(i)[0] + count += 1 + _logger.info('%r: Closed %d connections %s', self, count, + (dsn and last and 'to %r' % last.dsn) or '') class Connection(object): From b1df5aab6e529e9667bb97c96c9196e1a4e5e0ce Mon Sep 17 00:00:00 2001 From: Julien Legros Date: Tue, 28 Oct 2014 15:12:53 +0100 Subject: [PATCH 35/50] [FIX] sale: trigger all_lines signal when all lines are invoiced --- addons/sale/wizard/sale_line_invoice.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/addons/sale/wizard/sale_line_invoice.py b/addons/sale/wizard/sale_line_invoice.py index 5f979ead31e..fd3e78c05a0 100644 --- a/addons/sale/wizard/sale_line_invoice.py +++ b/addons/sale/wizard/sale_line_invoice.py @@ -100,7 +100,7 @@ class sale_order_line_make_invoice(osv.osv_memory): flag = False break if flag: - wf_service.trg_validate(uid, 'sale.order', order.id, 'manual_invoice', cr) + wf_service.trg_validate(uid, 'sale.order', order.id, 'all_lines', cr) if not invoices: raise osv.except_osv(_('Warning!'), _('Invoice cannot be created for this Sales Order Line due to one of the following reasons:\n1.The state of this sales order line is either "draft" or "cancel"!\n2.The Sales Order Line is Invoiced!')) From b6c0e34b7b8cbe1590f820e2418499c3a473059d Mon Sep 17 00:00:00 2001 From: Denis Ledoux Date: Tue, 28 Oct 2014 16:19:26 +0100 Subject: [PATCH 36/50] [FIX] account_analytic_plans: convert_ref method has been removed. This issue is related to f4e350ca36668d13c9eb09fbf77c85df110ce40c --- addons/account_analytic_plans/account_analytic_plans.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/addons/account_analytic_plans/account_analytic_plans.py b/addons/account_analytic_plans/account_analytic_plans.py index 02fc371f41e..4bd3822e8d8 100644 --- a/addons/account_analytic_plans/account_analytic_plans.py +++ b/addons/account_analytic_plans/account_analytic_plans.py @@ -392,7 +392,7 @@ class account_invoice(osv.osv): if inv.type in ('in_invoice', 'in_refund'): ref = inv.reference else: - ref = self._convert_ref(inv.number) + ref = inv.number obj_move_line = acct_ins_obj.browse(cr, uid, il['analytics_id'], context=context) ctx = context.copy() ctx.update({'date': inv.date_invoice}) From ba55e31f5798933feb4b720386b48fa85b22fd51 Mon Sep 17 00:00:00 2001 From: Xavier Morel Date: Tue, 28 Oct 2014 16:38:03 +0100 Subject: [PATCH 37/50] [FIX] add charset declaration to dashboard.sass so sassc stops freaking out --- addons/board/static/src/css/dashboard.css | 1 + addons/board/static/src/css/dashboard.sass | 2 ++ 2 files changed, 3 insertions(+) diff --git a/addons/board/static/src/css/dashboard.css b/addons/board/static/src/css/dashboard.css index f2bbcd07071..2d5e2b0cd23 100644 --- a/addons/board/static/src/css/dashboard.css +++ b/addons/board/static/src/css/dashboard.css @@ -1,3 +1,4 @@ +@charset "UTF-8"; .openerp .oe_dashboard_layout_1 .oe_dashboard_column.index_0 { width: 100%; } diff --git a/addons/board/static/src/css/dashboard.sass b/addons/board/static/src/css/dashboard.sass index 122b6dc232c..33ff7ce7c30 100644 --- a/addons/board/static/src/css/dashboard.sass +++ b/addons/board/static/src/css/dashboard.sass @@ -1,3 +1,5 @@ +@charset "utf-8" + @mixin radius($radius: 5px) -moz-border-radius: $radius -webkit-border-radius: $radius From f4870f84182eb7a062f11e37e31827009d237cbb Mon Sep 17 00:00:00 2001 From: Denis Ledoux Date: Tue, 28 Oct 2014 17:57:08 +0100 Subject: [PATCH 38/50] [FIX] sale: when invoicing on lines, set order in progress when all lines invoiced This fix is related to rev. b1df5aab6e529e9667bb97c96c9196e1a4e5e0ce --- addons/sale/wizard/sale_line_invoice.py | 1 + 1 file changed, 1 insertion(+) diff --git a/addons/sale/wizard/sale_line_invoice.py b/addons/sale/wizard/sale_line_invoice.py index fd3e78c05a0..998c66a8a03 100644 --- a/addons/sale/wizard/sale_line_invoice.py +++ b/addons/sale/wizard/sale_line_invoice.py @@ -100,6 +100,7 @@ class sale_order_line_make_invoice(osv.osv_memory): flag = False break if flag: + line.order_id.write({'state': 'progress'}) wf_service.trg_validate(uid, 'sale.order', order.id, 'all_lines', cr) if not invoices: From 43db7267c5051c9ad4877c27ce1220d446d7552d Mon Sep 17 00:00:00 2001 From: Mohammad Alhashash Date: Tue, 21 Oct 2014 21:12:58 +0200 Subject: [PATCH 39/50] [FIX] stock: UoS quantity in stock.picking Implements the UoS TODO items on stock.picking.do_partial() to fix #1432. Add a new method _compute_uos_qty() on product.product to computes product's invoicing quantity in UoS from quantity in UoM. The created invoice will use the product_uos of the stock.move, meaning keeping the quantity specified on the partial picking and the unit of measure of the original stock.move (e.g. recieving 1 dozen from a 12 unit picking should either get uos=dozen, uos_qty=1 or uos=unit, uos_qty=12, not a mix of both) Fixes #1432, opw 611479 --- addons/product/product.py | 28 ++++++++++++++++++++++++++++ addons/stock/stock.py | 9 +++++---- 2 files changed, 33 insertions(+), 4 deletions(-) diff --git a/addons/product/product.py b/addons/product/product.py index ff0a6a770d7..ac0de1d2fec 100644 --- a/addons/product/product.py +++ b/addons/product/product.py @@ -770,6 +770,34 @@ class product_product(osv.osv): args.append((('categ_id', 'child_of', context['search_default_categ_id']))) return super(product_product, self).search(cr, uid, args, offset=offset, limit=limit, order=order, context=context, count=count) + def _compute_uos_qty(self, cr, uid, ids, uom, qty, uos, context=None): + ''' + Computes product's invoicing quantity in UoS from quantity in UoM. + Takes into account the + :param uom: Source unit + :param qty: Source quantity + :param uos: Target UoS unit. + ''' + if not uom or not qty or not uos: + return qty + uom_obj = self.pool['product.uom'] + product_id = ids[0] if isinstance(ids, (list, tuple)) else ids + product = self.browse(cr, uid, product_id, context=context) + if isinstance(uos, (int, long)): + uos = uom_obj.browse(cr, uid, uos, context=context) + if isinstance(uom, (int, long)): + uom = uom_obj.browse(cr, uid, uom, context=context) + if product.uos_id: # Product has UoS defined + # We cannot convert directly between units even if the units are of the same category + # as we need to apply the conversion coefficient which is valid only between quantities + # in product's default UoM/UoS + qty_default_uom = uom_obj._compute_qty_obj(cr, uid, uom, qty, product.uom_id) # qty in product's default UoM + qty_default_uos = qty_default_uom * product.uos_coeff + return uom_obj._compute_qty_obj(cr, uid, product.uos_id, qty_default_uos, uos) + else: + return uom_obj._compute_qty_obj(cr, uid, uom, qty, uos) + + product_product() class product_packaging(osv.osv): diff --git a/addons/stock/stock.py b/addons/stock/stock.py index f117bdbc27d..644dae4ddf9 100644 --- a/addons/stock/stock.py +++ b/addons/stock/stock.py @@ -1241,7 +1241,7 @@ class stock_picking(osv.osv): for pick in self.browse(cr, uid, ids, context=context): new_picking = None complete, too_many, too_few = [], [], [] - move_product_qty, prodlot_ids, product_avail, partial_qty, product_uoms = {}, {}, {}, {}, {} + move_product_qty, prodlot_ids, product_avail, partial_qty, uos_qty, product_uoms = {}, {}, {}, {}, {}, {} for move in pick.move_lines: if move.state in ('done', 'cancel'): continue @@ -1255,6 +1255,7 @@ class stock_picking(osv.osv): prodlot_ids[move.id] = prodlot_id product_uoms[move.id] = product_uom partial_qty[move.id] = uom_obj._compute_qty(cr, uid, product_uoms[move.id], product_qty, move.product_uom.id) + uos_qty[move.id] = move.product_id._compute_uos_qty(product_uom, product_qty, move.product_uos) if product_qty else 0.0 if move.product_qty == partial_qty[move.id]: complete.append(move) elif move.product_qty > partial_qty[move.id]: @@ -1318,7 +1319,7 @@ class stock_picking(osv.osv): if product_qty != 0: defaults = { 'product_qty' : product_qty, - 'product_uos_qty': product_qty, #TODO: put correct uos_qty + 'product_uos_qty': uos_qty[move.id], 'picking_id' : new_picking, 'state': 'assigned', 'move_dest_id': False, @@ -1332,7 +1333,7 @@ class stock_picking(osv.osv): move_obj.write(cr, uid, [move.id], { 'product_qty': move.product_qty - partial_qty[move.id], - 'product_uos_qty': move.product_qty - partial_qty[move.id], #TODO: put correct uos_qty + 'product_uos_qty': move.product_uos_qty - uos_qty[move.id], 'prodlot_id': False, 'tracking_id': False, }) @@ -1348,7 +1349,7 @@ class stock_picking(osv.osv): product_qty = move_product_qty[move.id] defaults = { 'product_qty' : product_qty, - 'product_uos_qty': product_qty, #TODO: put correct uos_qty + 'product_uos_qty': uos_qty[move.id], 'product_uom': product_uoms[move.id] } prodlot_id = prodlot_ids.get(move.id) From 27fa742a700dfada76658661073f4b002032adf5 Mon Sep 17 00:00:00 2001 From: David Monjoie Date: Wed, 29 Oct 2014 10:56:48 +0100 Subject: [PATCH 40/50] [FIX] purchase: translate purchaseorder and quotation reports --- .../purchase/views/report_purchaseorder.xml | 238 +++++++++--------- .../views/report_purchasequotation.xml | 122 ++++----- 2 files changed, 184 insertions(+), 176 deletions(-) diff --git a/addons/purchase/views/report_purchaseorder.xml b/addons/purchase/views/report_purchaseorder.xml index 91486c035aa..fd199a6db65 100644 --- a/addons/purchase/views/report_purchaseorder.xml +++ b/addons/purchase/views/report_purchaseorder.xml @@ -1,125 +1,129 @@ + + diff --git a/addons/purchase/views/report_purchasequotation.xml b/addons/purchase/views/report_purchasequotation.xml index 6abd64b2f33..60f1081eb06 100644 --- a/addons/purchase/views/report_purchasequotation.xml +++ b/addons/purchase/views/report_purchasequotation.xml @@ -1,67 +1,71 @@ + + From 28a69fa250cda19c702d284dc4d8c66802f1e2d4 Mon Sep 17 00:00:00 2001 From: Xavier Morel Date: Wed, 29 Oct 2014 15:53:41 +0100 Subject: [PATCH 41/50] [ADD] doc: support for analytics keys --- doc/_themes/odoodoc/layout.html | 11 +++++++++++ doc/conf.py | 9 +++++++++ 2 files changed, 20 insertions(+) diff --git a/doc/_themes/odoodoc/layout.html b/doc/_themes/odoodoc/layout.html index 5e469cf7ad3..7881c701b60 100644 --- a/doc/_themes/odoodoc/layout.html +++ b/doc/_themes/odoodoc/layout.html @@ -113,4 +113,15 @@

+ {%- if google_analytics_key -%} + + {%- endif -%} {%- endblock %} diff --git a/doc/conf.py b/doc/conf.py index 9ae72b90928..cd04e65dae0 100644 --- a/doc/conf.py +++ b/doc/conf.py @@ -187,6 +187,9 @@ def setup(app): app.connect('html-page-context', versionize) app.add_config_value('versions', '', 'env') + app.connect('html-page-context', analytics) + app.add_config_value('google_analytics_key', False, 'env') + def canonicalize(app, pagename, templatename, context, doctree): """ Adds a 'canonical' URL for the current document in the rendering context. Requires the ``canonical_root`` setting being set. The canonical @@ -212,6 +215,12 @@ def versionize(app, pagename, templatename, context, doctree): if vs != app.config.version ] +def analytics(app, pagename, templatename, context, doctree): + if not app.config.google_analytics_key: + return + + context['google_analytics_key'] = app.config.google_analytics_key + def _build_url(root, branch, pagename): return "{canonical_url}{canonical_branch}/{canonical_page}".format( canonical_url=root, From ced8c3cf3f6b2f940d6b1c227f51472a7f860b41 Mon Sep 17 00:00:00 2001 From: Simon Lejeune Date: Wed, 29 Oct 2014 13:25:43 +0100 Subject: [PATCH 42/50] [FIX] website_sale: remove spurious print --- addons/website_sale/controllers/main.py | 1 - 1 file changed, 1 deletion(-) diff --git a/addons/website_sale/controllers/main.py b/addons/website_sale/controllers/main.py index 830b2bd2f90..249635c48c5 100644 --- a/addons/website_sale/controllers/main.py +++ b/addons/website_sale/controllers/main.py @@ -367,7 +367,6 @@ class website_sale(http.Controller): shipping_ids = [] checkout = {} if not data: - print request.uid, request.website.user_id.id if request.uid != request.website.user_id.id: checkout.update( self.checkout_parse("billing", partner) ) shipping_ids = orm_partner.search(cr, SUPERUSER_ID, [("parent_id", "=", partner.id), ('type', "=", 'delivery')], context=context) From a644bbaec8c3008e2744a85a21007a15baed7c88 Mon Sep 17 00:00:00 2001 From: Simon Lejeune Date: Wed, 29 Oct 2014 13:27:15 +0100 Subject: [PATCH 43/50] [FIX] website_sale: don't apply the company name as street logic in shipping address --- addons/website_sale/controllers/main.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/addons/website_sale/controllers/main.py b/addons/website_sale/controllers/main.py index 249635c48c5..712b35e0ac5 100644 --- a/addons/website_sale/controllers/main.py +++ b/addons/website_sale/controllers/main.py @@ -455,7 +455,7 @@ class website_sale(http.Controller): else: query = dict((prefix + field_name, getattr(data, field_name)) for field_name in all_fields if getattr(data, field_name)) - if data.parent_id: + if address_type == 'billing' and data.parent_id: query[prefix + 'street'] = data.parent_id.name if query.get(prefix + 'state_id'): From a03e5c42ec46a19051ef773d2fc29db0335230f2 Mon Sep 17 00:00:00 2001 From: Simon Lejeune Date: Wed, 29 Oct 2014 13:31:52 +0100 Subject: [PATCH 44/50] [FIX] website_sale: usability fixes in the checkout/address form * `disabled` on the country select tag instead of `readonly` * `create a new address` selected when the user set an invalid shipping address and must correct it. Else the user was correcting the shipping address but the option "ship to the same address" was selected. --- addons/website_sale/static/src/js/website_sale.js | 4 +++- addons/website_sale/views/templates.xml | 4 ++-- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/addons/website_sale/static/src/js/website_sale.js b/addons/website_sale/static/src/js/website_sale.js index dda494aac7e..6fbd2995125 100644 --- a/addons/website_sale/static/src/js/website_sale.js +++ b/addons/website_sale/static/src/js/website_sale.js @@ -7,10 +7,12 @@ $('.oe_website_sale').each(function () { var value = +$shippingDifferent.val(); var data = $shippingDifferent.find("option:selected").data(); var $snipping = $(".js_shipping", oe_website_sale); - var $inputs = $snipping.find("input,select"); + var $inputs = $snipping.find("input"); + var $selects = $snipping.find("select"); $snipping.toggle(!!value); $inputs.attr("readonly", value <= 0 ? null : "readonly" ).prop("readonly", value <= 0 ? null : "readonly" ); + $selects.attr("disabled", value <= 0 ? null : "disabled" ).prop("disabled", value <= 0 ? null : "disabled" ); $inputs.each(function () { $(this).val( data[$(this).attr("name")] || "" ); diff --git a/addons/website_sale/views/templates.xml b/addons/website_sale/views/templates.xml index 929d2efd052..4de3c36007d 100644 --- a/addons/website_sale/views/templates.xml +++ b/addons/website_sale/views/templates.xml @@ -921,7 +921,7 @@ t-att-data-shipping_country_id="shipping.country_id and shipping.country_id.id" > - + @@ -951,7 +951,7 @@
- From 25bc377b05365011ffed0658b7f4e94f85c6384a Mon Sep 17 00:00:00 2001 From: Andrea Stirpe Date: Wed, 29 Oct 2014 12:54:52 +0100 Subject: [PATCH 45/50] [FIX] bus: useless setTimeout call Partial backport of master fix eb52156 Solving javascript error after refresh in ie9 `Error: useless setTimeout call (missing quotes around argument?)` --- addons/bus/static/src/js/bus.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/addons/bus/static/src/js/bus.js b/addons/bus/static/src/js/bus.js index 6ca515f815e..393f101c77c 100644 --- a/addons/bus/static/src/js/bus.js +++ b/addons/bus/static/src/js/bus.js @@ -14,7 +14,7 @@ }, start_polling: function(){ if(!this.activated){ - setTimeout(this.poll(), 1); + this.poll(); this.stop = false; } }, @@ -58,4 +58,4 @@ // singleton bus.bus = new bus.Bus(); return bus; -})(); \ No newline at end of file +})(); From 82588faa71e8da27d886ce8402841dc4985b95ff Mon Sep 17 00:00:00 2001 From: Olivier Dony Date: Tue, 28 Oct 2014 13:30:26 +0100 Subject: [PATCH 46/50] [FIX] account: tax mapping logic was broken by 7508b7c The original tax should not be applied if at least one destination tax was applied. It should only be applied if explicitly included in target taxes. (Re-)Fixes #2261 --- addons/account/partner.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/addons/account/partner.py b/addons/account/partner.py index fa6dd0eb8ce..74fbbe17853 100644 --- a/addons/account/partner.py +++ b/addons/account/partner.py @@ -79,11 +79,13 @@ class account_fiscal_position(osv.osv): def map_tax(self, taxes): result = self.env['account.tax'].browse() for tax in taxes: + tax_count = 0 for t in self.tax_ids: if t.tax_src_id == tax: if t.tax_dest_id: + tax_count += 1 result |= t.tax_dest_id - else: + if not tax_count: result |= tax return result From 787147d52b85464588c046b2afaa0bab0796009b Mon Sep 17 00:00:00 2001 From: Jeremy Kersten Date: Wed, 29 Oct 2014 17:43:44 +0100 Subject: [PATCH 47/50] [FIX] web_calendar: cast the id into int. When you open a form view in a popup via the attr 'event_open_popup', in some cases the id is a string and so the popup is empty... FIX #545 PR #551 --- addons/web_calendar/static/src/js/web_calendar.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/addons/web_calendar/static/src/js/web_calendar.js b/addons/web_calendar/static/src/js/web_calendar.js index d32cbe453ad..5f90f49acad 100644 --- a/addons/web_calendar/static/src/js/web_calendar.js +++ b/addons/web_calendar/static/src/js/web_calendar.js @@ -855,7 +855,7 @@ openerp.web_calendar = function(instance) { } else { var pop = new instance.web.form.FormOpenPopup(this); - pop.show_element(this.dataset.model, id, this.dataset.get_context(), { + pop.show_element(this.dataset.model, parseInt(id), this.dataset.get_context(), { title: _.str.sprintf(_t("View: %s"),title), view_id: +this.open_popup_action, res_id: id, From cc4fba6089d3a65bb014d2aa33601668fca6f0bb Mon Sep 17 00:00:00 2001 From: Christophe Simonis Date: Wed, 29 Oct 2014 18:28:07 +0100 Subject: [PATCH 48/50] [IMP] core: manage registries via an LRU. When working with a large number of databases, the memory allocated to registries wasn't limited, resulting to waste memory (especially in the longpolling worker, which is not recycled). The size of the LRU is depending on the soft limit configured for workers. --- openerp/modules/registry.py | 26 +++++++++++++++++++++----- openerp/tools/func.py | 10 +++++++++- 2 files changed, 30 insertions(+), 6 deletions(-) diff --git a/openerp/modules/registry.py b/openerp/modules/registry.py index 679fae0cfc2..34672c44a78 100644 --- a/openerp/modules/registry.py +++ b/openerp/modules/registry.py @@ -23,13 +23,14 @@ """ from collections import Mapping -from contextlib import contextmanager import logging +import os import threading import openerp from .. import SUPERUSER_ID -from openerp.tools import assertion_report, lazy_property +from openerp.tools import assertion_report, lazy_property, classproperty, config +from openerp.tools.lru import LRU _logger = logging.getLogger(__name__) @@ -259,12 +260,27 @@ class RegistryManager(object): registries (essentially database connection/model registry pairs). """ - # Mapping between db name and model registry. - # Accessed through the methods below. - registries = {} + _registries = None _lock = threading.RLock() _saved_lock = None + @classproperty + def registries(cls): + if cls._registries is None: + size = config.get('registry_lru_size', None) + if not size: + # Size the LRU depending of the memory limits + if os.name != 'posix': + # cannot specify the memory limit soft on windows... + size = 42 + else: + # On average, a clean registry take 25MB of memory + cache + avgsz = 30 * 1024 * 1024 + size = int(config['limit_memory_soft'] / avgsz) + + cls._registries = LRU(size) + return cls._registries + @classmethod def lock(cls): """ Return the current registry lock. """ diff --git a/openerp/tools/func.py b/openerp/tools/func.py index baeae0b1465..0a36166acee 100644 --- a/openerp/tools/func.py +++ b/openerp/tools/func.py @@ -20,7 +20,7 @@ # ############################################################################## -__all__ = ['synchronized', 'lazy_property'] +__all__ = ['synchronized', 'lazy_property', 'classproperty'] from functools import wraps from inspect import getsourcefile @@ -103,4 +103,12 @@ def compose(a, b): return a(b(*args, **kwargs)) return wrapper + +class _ClassProperty(property): + def __get__(self, cls, owner): + return self.fget.__get__(None, owner)() + +def classproperty(func): + return _ClassProperty(classmethod(func)) + # vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4: From fe6eb853a56df42bc89e264ff252a6bf7c41ddd3 Mon Sep 17 00:00:00 2001 From: Simon Lejeune Date: Wed, 29 Oct 2014 18:45:25 +0100 Subject: [PATCH 49/50] [FIX] l10n_fr: "passif" part of the "bilan" report Report's model was full, but not the view. Leftover of rev 7e8dcc5600b61d68af refactoring. --- addons/l10n_fr/views/report_l10nfrbilan.xml | 149 ++++++++++++++++++++ 1 file changed, 149 insertions(+) diff --git a/addons/l10n_fr/views/report_l10nfrbilan.xml b/addons/l10n_fr/views/report_l10nfrbilan.xml index 5b7335e0db0..f5c697c3fad 100644 --- a/addons/l10n_fr/views/report_l10nfrbilan.xml +++ b/addons/l10n_fr/views/report_l10nfrbilan.xml @@ -20,6 +20,7 @@

+

Actif

@@ -538,6 +539,154 @@
+ +

Passif

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
CAPITAUX PROPRES
Capital [dont versé...]
Primes d'émission, de fusion, d'apport
Écarts de réévaluation
Écart d'équivalence
RÉSERVES
Réserve légale
Réserves statutaires ou contractuelles
Réserves réglementées
Autres réserves
Report à nouveau
RÉSULTAT DE L'EXERCICE [bénéfice ou perte]
Subventions d'investissement
Provisions réglementées
TOTAL I
PROVISIONS
Provisions pour risques
Provisions pour charges
TOTAL II
DETTES
Emprunts obligataires convertibles
Autres emprunts obligataires
Emprunts et dettes auprès des établissements de crédit
Emprunts et dettes financières diverses
Avances et acomptes reçus sur commandes en cours
Dettes fournisseurs et comptes rattachés
Dettes fiscales et sociales
Dettes sur immobilisations et comptes rattachés
Autres dettes
Instruments de trésorerie
Produits constatés d'avance
TOTAL III
Écarts de conversion passif ( IV )
TOTAL GÉNÉRAL (I + II + III + IV)
&nbsp;&nbsp;
ACTIF - PASSIF
From 92c7874eb8f4cf14c04d4d7b50853d56aef30436 Mon Sep 17 00:00:00 2001 From: Olivier Dony Date: Wed, 29 Oct 2014 19:42:13 +0100 Subject: [PATCH 50/50] [FIX] account: indendation error in 82588fa The original tax should not be included if it was found in the tax mapping, even if it has no replacement. Fixes #2261 (hopefully, this time) --- addons/account/partner.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/addons/account/partner.py b/addons/account/partner.py index 74fbbe17853..c0a1a695e0c 100644 --- a/addons/account/partner.py +++ b/addons/account/partner.py @@ -82,8 +82,8 @@ class account_fiscal_position(osv.osv): tax_count = 0 for t in self.tax_ids: if t.tax_src_id == tax: + tax_count += 1 if t.tax_dest_id: - tax_count += 1 result |= t.tax_dest_id if not tax_count: result |= tax