From 60d46345444fcb268d1846d30963e63adcba98c0 Mon Sep 17 00:00:00 2001 From: jeffery chen fan Date: Tue, 18 Jul 2017 23:05:18 +0800 Subject: [PATCH 1/6] [FIX] sale: use date_order for pricelist selection The date of the sale order was ignored when selecting th eappropriate pricelist rule. Fixes #18303 Closes #18308 --- addons/sale/sale.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/addons/sale/sale.py b/addons/sale/sale.py index 802457694f2..7b81e631213 100644 --- a/addons/sale/sale.py +++ b/addons/sale/sale.py @@ -792,7 +792,7 @@ class SaleOrderLine(models.Model): lang=self.order_id.partner_id.lang, partner=self.order_id.partner_id.id, quantity=self.product_uom_qty, - date_order=self.order_id.date_order, + date=self.order_id.date_order, pricelist=self.order_id.pricelist_id.id, uom=self.product_uom.id, fiscal_position=self.env.context.get('fiscal_position') From a03eb9a3e8f625fc2f68dad8e9ef653329fb568f Mon Sep 17 00:00:00 2001 From: Martin Trigaux Date: Wed, 19 Jul 2017 16:16:16 +0200 Subject: [PATCH 2/6] [IMP] sale: add test on pricelist dates Commit 60d46345 fixed issue #18303 that was surprisingly unnoticed for two years Adding tests to make sure it does not happen again. --- addons/sale/tests/test_product_id_change.py | 48 +++++++++++++++++++++ 1 file changed, 48 insertions(+) diff --git a/addons/sale/tests/test_product_id_change.py b/addons/sale/tests/test_product_id_change.py index ffac350cd5f..d7f8deacc33 100644 --- a/addons/sale/tests/test_product_id_change.py +++ b/addons/sale/tests/test_product_id_change.py @@ -64,3 +64,51 @@ class TestOnchangeProductId(TransactionCase): so_line = so.order_line[0] so_line.product_id_change() self.assertEquals(100, so_line.price_unit, "The included tax must be subtracted to the price") + + def test_pricelist_application(self): + """ Test different prices are correctly applied based on dates """ + support_product = self.env.ref('product.product_product_2') + support_product.list_price = 100 + partner = self.res_partner_model.create(dict(name="George")) + + christmas_pricelist = self.env['product.pricelist'].create({ + 'name': 'Christmas pricelist', + 'item_ids': [(0, 0, { + 'date_start': "2017-12-01", + 'date_end': "2017-12-24", + 'compute_price': 'percentage', + 'base': 'list_price', + 'percent_price': 20, + 'applied_on': '3_global', + 'name': 'Pre-Christmas discount' + }), (0, 0, { + 'date_start': "2017-12-25", + 'date_end': "2017-12-31", + 'compute_price': 'percentage', + 'base': 'list_price', + 'percent_price': 50, + 'applied_on': '3_global', + 'name': 'Post-Christmas super-discount' + })] + }) + + so = self.env['sale.order'].create({ + 'partner_id': partner.id, + 'date_order': '2017-12-20', + 'pricelist_id': christmas_pricelist.id, + }) + + order_line = self.env['sale.order.line'].new({ + 'order_id': so.id, + 'product_id': support_product.id, + }) + + # force compute uom and prices + order_line.product_id_change() + order_line.product_uom_change() + + self.assertEqual(order_line.price_unit, 80, "First date pricelist rule not applied") + + so.date_order = '2017-12-30' + order_line.product_id_change() + self.assertEqual(order_line.price_unit, 50, "Second date pricelist rule not applied") From 7333f5e9b8ac8909af45def63002bd26a234e79d Mon Sep 17 00:00:00 2001 From: Goffin Simon Date: Thu, 20 Jul 2017 13:44:43 +0200 Subject: [PATCH 3/6] [FIX] purchase: Wrong scheduled date for backorder Steps to reproduce: -Create a PO with two lines L1 and L2 -L1 has product P with qty=10 for a scheduled date=23/07/2017 -L2 has product P with qty=10 for a scheduled date=30/07/2017 -Confirm the PO When delivering 5P of L1, the scheduled date of the picking is the right date which is 23/07/2017 But when delivering the other 5P of L1, the scheduled date of the second picking was 30/07/2017 instead of 23/07/2017. opw:752192 --- addons/purchase/purchase.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/addons/purchase/purchase.py b/addons/purchase/purchase.py index aab374efd56..23f1f8bebef 100644 --- a/addons/purchase/purchase.py +++ b/addons/purchase/purchase.py @@ -357,6 +357,10 @@ class PurchaseOrder(models.Model): picking = self.env['stock.picking'].create(res) moves = order.order_line.filtered(lambda r: r.product_id.type in ['product', 'consu'])._create_stock_moves(picking) move_ids = moves.action_confirm() + seq = 0 + for move in moves: + seq += 5 + move.sequence = seq moves = self.env['stock.move'].browse(move_ids) moves.force_assign() return True From e2a255717fb941fffa1fcc8ed0c5e390fba76618 Mon Sep 17 00:00:00 2001 From: Pierre Masereel Date: Thu, 6 Apr 2017 17:30:30 +0200 Subject: [PATCH 4/6] [FIX] stock: MTO move status When we validate a SO with an MTO product, then an RFQ and a delivery picking are created. The status of the delivery picking is in state 'confirmed', but should be 'waiting' as the delivery waits for a specific delivery input. The problem is caused by the unreserve method, which is called in the 'procurement_jit' module. This method only checks that a move has ancestors to determine if the move should be in waiting or in confirmed, which is not the case for our delivery move because the RFQ has not been validated yet so there is not ancestors yet. To fix this issue, we check the 'procure_method' of the move, and if it is in 'make_to_order' we put it in 'waiting' instead of 'confirmed'. We also fixed the function recalculate_move_state that could cause the same problem. --- addons/stock/stock.py | 4 +- addons/stock/tests/test_stock_flow.py | 65 +++++++++++++++++++++++++++ 2 files changed, 67 insertions(+), 2 deletions(-) diff --git a/addons/stock/stock.py b/addons/stock/stock.py index f7927cabd73..9ae7bbb37a3 100644 --- a/addons/stock/stock.py +++ b/addons/stock/stock.py @@ -2073,7 +2073,7 @@ class stock_move(osv.osv): raise UserError(_('Cannot unreserve a done move')) quant_obj.quants_unreserve(cr, uid, move, context=context) if not context.get('no_state_change'): - if self.find_move_ancestors(cr, uid, move, context=context): + if move.procure_method == 'make_to_order' or self.find_move_ancestors(cr, uid, move, context=context): self.write(cr, uid, [move.id], {'state': 'waiting'}, context=context) else: self.write(cr, uid, [move.id], {'state': 'confirmed'}, context=context) @@ -2572,7 +2572,7 @@ class stock_move(osv.osv): if len(reserved_quant_ids) == 0 and move.partially_available: vals['partially_available'] = False if move.state == 'assigned': - if self.find_move_ancestors(cr, uid, move, context=context): + if move.procure_method == 'make_to_order' or self.find_move_ancestors(cr, uid, move, context=context): vals['state'] = 'waiting' else: vals['state'] = 'confirmed' diff --git a/addons/stock/tests/test_stock_flow.py b/addons/stock/tests/test_stock_flow.py index 62aeb59d065..07d402985d5 100644 --- a/addons/stock/tests/test_stock_flow.py +++ b/addons/stock/tests/test_stock_flow.py @@ -1621,3 +1621,68 @@ class TestStockFlow(TestStockCommon): # There should be no quant in the inventory loss location quant = self.env['stock.quant'].search([('product_id', '=', productA.id), ('location_id', '=', location_loss.id)]) self.assertEqual(len(quant), 0) + + def test_74_move_state_waiting_mto(self): + """ This test will check that when a move is unreserved, its state changes to 'waiting' if + it has ancestors or if it has a 'procure_method' equal to 'make_to_order' else the state + changes to 'confirmed'. + """ + picking_out = self.PickingObj.create({ + 'partner_id': self.partner_agrolite_id, + 'picking_type_id': self.picking_type_out, + 'location_id': self.stock_location, + 'location_dest_id': self.customer_location}) + move_mto_alone = self.MoveObj.create({ + 'name': self.productA.name, + 'product_id': self.productA.id, + 'product_uom_qty': 2, + 'product_uom': self.productA.uom_id.id, + 'picking_id': picking_out.id, + 'location_id': self.stock_location, + 'location_dest_id': self.customer_location, + 'procure_method':'make_to_order'}) + move_with_ancestors = self.MoveObj.create({ + 'name': self.productA.name, + 'product_id': self.productA.id, + 'product_uom_qty': 2, + 'product_uom': self.productA.uom_id.id, + 'picking_id': picking_out.id, + 'location_id': self.stock_location, + 'location_dest_id': self.customer_location}) + self.MoveObj.create({ + 'name': self.productA.name, + 'product_id': self.productA.id, + 'product_uom_qty': 2, + 'product_uom': self.productA.uom_id.id, + 'picking_id': picking_out.id, + 'location_id': self.stock_location, + 'location_dest_id': self.customer_location, + 'move_dest_id': move_with_ancestors.id}) + other_move = self.MoveObj.create({ + 'name': self.productA.name, + 'product_id': self.productA.id, + 'product_uom_qty': 2, + 'product_uom': self.productA.uom_id.id, + 'picking_id': picking_out.id, + 'location_id': self.stock_location, + 'location_dest_id': self.customer_location}) + + move_mto_alone.action_confirm() + move_with_ancestors.action_confirm() + other_move.action_confirm() + + move_mto_alone.do_unreserve() + move_with_ancestors.do_unreserve() + other_move.do_unreserve() + + self.assertEquals(move_mto_alone.state, "waiting") + self.assertEquals(move_with_ancestors.state, "waiting") + self.assertEquals(other_move.state, "confirmed") + + move_mto_alone.recalculate_move_state() + move_with_ancestors.recalculate_move_state() + other_move.recalculate_move_state() + + self.assertEquals(move_mto_alone.state, "waiting") + self.assertEquals(move_with_ancestors.state, "waiting") + self.assertEquals(other_move.state, "confirmed") From 890152cb6b8ac742f71a0b0faf7222ddbdc8f6f3 Mon Sep 17 00:00:00 2001 From: "Lucas Perais (lpe)" Date: Mon, 24 Jul 2017 16:01:29 +0200 Subject: [PATCH 5/6] [FIX] website_sale, website_sale_options: fix modal prices Backport of 8bd56bb46628e57f41c635f20c527264d7a20d86 OPW 744943 Closes #18416 --- .../static/src/js/website_sale.js | 38 ++++++++++++------- addons/website_sale/views/templates.xml | 3 +- .../website_sale_options/controllers/main.py | 9 +++++ .../static/src/css/website_sale.css | 5 +++ .../static/src/js/website_sale.js | 2 +- .../website_sale_options/views/templates.xml | 8 ++-- 6 files changed, 46 insertions(+), 19 deletions(-) diff --git a/addons/website_sale/static/src/js/website_sale.js b/addons/website_sale/static/src/js/website_sale.js index 5f33ed0d11b..8476977b724 100644 --- a/addons/website_sale/static/src/js/website_sale.js +++ b/addons/website_sale/static/src/js/website_sale.js @@ -105,23 +105,31 @@ $('.oe_website_sale').each(function () { $(oe_website_sale).on("change", 'input[name="add_qty"]', function (event) { var product_ids = []; - var product_dom = $(".js_product .js_add_cart_variants[data-attribute_value_ids]").last(); + var product_dom = $(".js_product .js_add_cart_variants[data-attribute_value_ids]"); + var qty = $(event.target).closest('form').find('input[name="add_qty"]').val(); if (!product_dom.length) { return; } - _.each(product_dom.data("attribute_value_ids"), function(entry) { - product_ids.push(entry[0]);}); - var qty = $(event.target).closest('form').find('input[name="add_qty"]').val(); + _.each(product_dom, function(prod) { + _.each($(prod).data("attribute_value_ids"), function(entry) { + if (product_ids.indexOf(entry[0]) === -1) { + product_ids.push(entry[0]); + }}); + }) + + if ($("#product_detail").length) { // display the reduction from the pricelist in function of the quantity ajax.jsonRpc("/shop/get_unit_price", 'call', {'product_ids': product_ids,'add_qty': parseInt(qty)}) .then(function (data) { - var current = product_dom.data("attribute_value_ids"); - for(var j=0; j < current.length; j++){ - current[j][2] = data[current[j][0]]; - } - product_dom.attr("data-attribute_value_ids", JSON.stringify(current)).trigger("change"); + _.each(product_dom, function (prod) { + var current = $(prod).data("attribute_value_ids"); + for(var j=0; j < current.length; j++){ + current[j][2] = data[current[j][0]]; + } + $(prod).trigger("change"); + }); }); } }); @@ -275,17 +283,17 @@ $('.oe_website_sale').each(function () { $(oe_website_sale).on('change', 'input.js_variant_change, select.js_variant_change, ul[data-attribute_value_ids]', function (ev) { var $ul = $(ev.target).closest('.js_add_cart_variants'); var $parent = $ul.closest('.js_product'); - var $product_id = $parent.find('input.product_id').first(); - var $price = $parent.find(".oe_price:first .oe_currency_value") - .add($('#product_confirmation').find(".oe_price")); - var $default_price = $parent.find(".oe_default_price:first .oe_currency_value") - .add($('#product_confirmation').find(".oe_default_price:first .oe_currency_value")); + var $product_id = $parent.find('.product_id').first(); + var $price = $parent.find(".oe_price:first .oe_currency_value"); + var $default_price = $parent.find(".oe_default_price:first .oe_currency_value"); var $optional_price = $parent.find(".oe_optional:first .oe_currency_value"); var variant_ids = $ul.data("attribute_value_ids"); var values = []; + var unchanged_values = $parent.find('div.oe_unchanged_value_ids').data('unchanged_value_ids') || []; $parent.find('input.js_variant_change:checked, select.js_variant_change').each(function () { values.push(+$(this).val()); }); + values = values.concat(unchanged_values); $parent.find("label").removeClass("text-muted css_not_available"); @@ -298,9 +306,11 @@ $('.oe_website_sale').each(function () { }); if (variant_ids[k][3]-variant_ids[k][2]>0.2) { $default_price.closest('.oe_website_sale').addClass("discount"); + $default_price.parent().removeClass('hidden'); $optional_price.closest('.oe_optional').show().css('text-decoration', 'line-through'); } else { $default_price.closest('.oe_website_sale').removeClass("discount"); + $default_price.parent().addClass('hidden'); $optional_price.closest('.oe_optional').hide(); } product_id = variant_ids[k][0]; diff --git a/addons/website_sale/views/templates.xml b/addons/website_sale/views/templates.xml index c4aab9e1e95..c85be40e8e5 100644 --- a/addons/website_sale/views/templates.xml +++ b/addons/website_sale/views/templates.xml @@ -366,7 +366,8 @@

Product Name