From 56704c19d6f40b0a7b647966fd972703d0b9b8d0 Mon Sep 17 00:00:00 2001 From: Xavier Morel Date: Wed, 23 Aug 2017 17:58:42 +0200 Subject: [PATCH 01/13] [FIX] account: iteration different under ordereddict mutation Did you know that if you iterate on an OrderedDict in Python 2 and add to the dict during iteration, the new items will be iterated? No? Me neither. And did you know that that does not work in Python 3 likely because OrderedDict's implementation details changed? Why the fuck would anyone do that and thus why would we care you ask? WELL WE FUCKING DO IT TURNS OUT and that breaks test_partial_reconcile_currencies on Python 3... Fix it, try to make it more obvious that we're doing weird crap instead of abusing OrderedDict in ways it was never meant to be used. --- addons/account/models/account_move.py | 17 +++++++++++------ 1 file changed, 11 insertions(+), 6 deletions(-) diff --git a/addons/account/models/account_move.py b/addons/account/models/account_move.py index 34a18555049..6139f36bb19 100644 --- a/addons/account/models/account_move.py +++ b/addons/account/models/account_move.py @@ -1722,17 +1722,20 @@ class AccountPartialReconcile(models.Model): return self #check if the reconcilation is full #first, gather all journal items involved in the reconciliation just created - partial_rec_set = OrderedDict.fromkeys([x for x in self]) aml_set = aml_to_balance = self.env['account.move.line'] total_debit = 0 total_credit = 0 total_amount_currency = 0 #make sure that all partial reconciliations share the same secondary currency otherwise it's not #possible to compute the exchange difference entry and it has to be done manually. - currency = list(partial_rec_set)[0].currency_id + currency = self[0].currency_id maxdate = '0000-00-00' - for partial_rec in partial_rec_set: + seen = set() + todo = set(self) + while todo: + partial_rec = todo.pop() + seen.add(partial_rec) if partial_rec.currency_id != currency: #no exchange rate entry will be created currency = None @@ -1751,13 +1754,15 @@ class AccountPartialReconcile(models.Model): #currency is recorded on the partial rec and in order to check if the reconciliation is total, we need to convert the #aml.balance in that foreign currency total_amount_currency += aml.company_id.currency_id.with_context(date=aml.date).compute(aml.balance, partial_rec.currency_id) + for x in aml.matched_debit_ids | aml.matched_credit_ids: - partial_rec_set[x] = None - partial_rec_ids = [x.id for x in partial_rec_set] + if x not in seen: + todo.add(x) + + partial_rec_ids = [x.id for x in seen] aml_ids = aml_set.ids #then, if the total debit and credit are equal, or the total amount in currency is 0, the reconciliation is full digits_rounding_precision = aml_set[0].company_id.currency_id.rounding - if (currency and float_is_zero(total_amount_currency, precision_rounding=currency.rounding)) or float_compare(total_debit, total_credit, precision_rounding=digits_rounding_precision) == 0: exchange_move_id = False exchange_partial_rec_id = False From fa6252153736aa9d7765f1dff5016ad23aa1d172 Mon Sep 17 00:00:00 2001 From: Simon Lejeune Date: Wed, 9 Aug 2017 13:29:48 +0200 Subject: [PATCH 02/13] [FIX] stock: stock.move.line's write Make sure that a move's `product_uom_qty` is equal to its `quantity_done`, even after writing on a done move line. --- addons/stock/models/stock_move_line.py | 8 ++++++++ addons/stock/tests/test_move.py | 7 +++++++ 2 files changed, 15 insertions(+) diff --git a/addons/stock/models/stock_move_line.py b/addons/stock/models/stock_move_line.py index 124f67ef6e2..1050d60902e 100644 --- a/addons/stock/models/stock_move_line.py +++ b/addons/stock/models/stock_move_line.py @@ -230,6 +230,14 @@ class StockMoveLine(models.Model): # Unreserve and reserve following move in order to have the real reserved quantity on move_line. next_moves |= ml.move_id.move_dest_ids.filtered(lambda move: move.state not in ('done', 'cancel')) res = super(StockMoveLine, self).write(vals) + + # As stock_account values according to a move's `product_uom_qty`, we consider that any + # done stock move should have its `quantity_done` equals to its `product_uom_qty`, and + # this is what move's `action_done` will do. So, we replicate the behavior here. + if updates or 'qty_done' in vals: + moves = self.filtered(lambda ml: ml.move_id.state == 'done').mapped('move_id') + for move in moves: + move.product_uom_qty = move.quantity_done next_moves.do_unreserve() next_moves.action_assign() return res diff --git a/addons/stock/tests/test_move.py b/addons/stock/tests/test_move.py index 338208e48e3..22ae95448e4 100644 --- a/addons/stock/tests/test_move.py +++ b/addons/stock/tests/test_move.py @@ -2016,6 +2016,7 @@ class StockMove(TransactionCase): move1.move_line_ids.qty_done = 1 move1.action_done() + self.assertEqual(move1.product_uom_qty, 1.0) self.assertEqual(self.env['stock.quant']._get_available_quantity(self.product1, shelf1_location), 0.0) self.assertEqual(self.env['stock.quant']._get_available_quantity(self.product1, self.stock_location), 0.0) @@ -2024,6 +2025,7 @@ class StockMove(TransactionCase): self.assertEqual(self.env['stock.quant']._get_available_quantity(self.product1, shelf1_location), -1.0) self.assertEqual(self.env['stock.quant']._get_available_quantity(self.product1, self.stock_location), -1.0) + self.assertEqual(move1.product_uom_qty, 2.0) def test_edit_done_move_line_9(self): """ Test that editing a done stock move line linked to an untracked product correctly and @@ -2052,12 +2054,14 @@ class StockMove(TransactionCase): move1.move_line_ids.qty_done = 1 move1.action_done() + self.assertEqual(move1.product_uom_qty, 1.0) self.assertEqual(self.env['stock.quant']._get_available_quantity(self.product1, shelf1_location), 0.0) self.assertEqual(self.env['stock.quant']._get_available_quantity(self.product1, self.stock_location), 0.0) # edit once done, we actually moved 2 products move1.move_line_ids.qty_done = 0 + self.assertEqual(move1.product_uom_qty, 0.0) self.assertEqual(self.env['stock.quant']._get_available_quantity(self.product1, shelf1_location), 1.0) self.assertEqual(self.env['stock.quant']._get_available_quantity(self.product1, self.stock_location), 1.0) @@ -2087,6 +2091,7 @@ class StockMove(TransactionCase): quant = self.env['stock.quant']._gather(self.product1, self.stock_location) self.assertEqual(len(quant), 0.0) + self.assertEqual(move1.product_uom_qty, 0.0) def test_edit_done_move_line_11(self): """ Add a move line and check if the quant is updated @@ -2112,6 +2117,7 @@ class StockMove(TransactionCase): picking.action_assign() move1.move_line_ids.qty_done = 10 picking.action_done() + self.assertEqual(move1.product_uom_qty, 10.0) self.assertEqual(self.env['stock.quant']._get_available_quantity(self.product1, self.stock_location), 10.0) self.env['stock.move.line'].create({ 'picking_id': move1.move_line_ids.picking_id.id, @@ -2124,6 +2130,7 @@ class StockMove(TransactionCase): }) self.assertEqual(self.env['stock.quant']._get_available_quantity(self.product1, self.stock_location), 20.0) move1.move_line_ids[1].qty_done = 5 + self.assertEqual(move1.product_uom_qty, 15.0) self.assertEqual(self.env['stock.quant']._get_available_quantity(self.product1, self.stock_location), 15.0) def test_immediate_validate_1(self): From c8f1581060123fa6132b29b3f0e3e463228955ba Mon Sep 17 00:00:00 2001 From: Pierre Masereel Date: Tue, 22 Aug 2017 15:58:18 +0200 Subject: [PATCH 03/13] [FIX] purchase: update received quantity of PO When a picking is edited in done state, the related purchase order line received quantity is not updated. To fix this, we add a depends on the product_uom_qty of its stock moves which is changed when a move is edited in 'done' state --- addons/purchase/models/purchase.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/addons/purchase/models/purchase.py b/addons/purchase/models/purchase.py index 9c8c9b00f57..e5b95282faf 100644 --- a/addons/purchase/models/purchase.py +++ b/addons/purchase/models/purchase.py @@ -548,7 +548,7 @@ class PurchaseOrderLine(models.Model): qty -= inv_line.uom_id._compute_quantity(inv_line.quantity, line.product_uom) line.qty_invoiced = qty - @api.depends('order_id.state', 'move_ids.state') + @api.depends('order_id.state', 'move_ids.state', 'move_ids.product_uom_qty') def _compute_qty_received(self): for line in self: if line.order_id.state not in ['purchase', 'done']: From 837789fec27426b09cb8b6f71f2381544737991d Mon Sep 17 00:00:00 2001 From: Pierre Masereel Date: Wed, 23 Aug 2017 14:11:37 +0200 Subject: [PATCH 04/13] [FIX] sale_stock: update SO delivered quantity When a picking is done and the quantity done of a move is update, the delivered quantity of the linked sale order is not updated because it's only updated on action_done of a move. To fix this we override the write function of stock move in the sale_stock module to update the delivered quantity of the corresponding sale order line --- addons/sale_stock/models/stock.py | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/addons/sale_stock/models/stock.py b/addons/sale_stock/models/stock.py index 2db872ad8c7..045a8f0498c 100644 --- a/addons/sale_stock/models/stock.py +++ b/addons/sale_stock/models/stock.py @@ -36,6 +36,17 @@ class StockMove(models.Model): subtype_id=self.env.ref('mail.mt_note').id) return result + @api.multi + def write(self, vals): + res = super(StockMove, self).write(vals) + if 'product_uom_qty' in vals: + for move in self: + if move.state == 'done': + sale_order_lines = self.filtered(lambda move: move.procurement_id.sale_line_id and move.product_id.expense_policy == 'no').mapped('procurement_id.sale_line_id') + for line in sale_order_lines: + line.qty_delivered = line._get_delivered_qty() + return res + class StockPicking(models.Model): _inherit = 'stock.picking' From fff4d41da24c8fcd228477ead34794317e9db15a Mon Sep 17 00:00:00 2001 From: qdp-odoo Date: Thu, 24 Aug 2017 14:43:30 +0200 Subject: [PATCH 05/13] [FIX] account: removed unecessary styling on invoice form view. This extra styling was limiting the field to few digits whereas there was no reason to do so. Fixes #18996 --- addons/account/views/account_invoice_view.xml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/addons/account/views/account_invoice_view.xml b/addons/account/views/account_invoice_view.xml index 4ab3bda6e6a..a4bbc80bcee 100644 --- a/addons/account/views/account_invoice_view.xml +++ b/addons/account/views/account_invoice_view.xml @@ -238,7 +238,7 @@
- +
@@ -378,7 +378,7 @@
From 900701213f1700ed56e2a82a098a35171b253bec Mon Sep 17 00:00:00 2001 From: Christophe Simonis Date: Tue, 25 Apr 2017 18:45:28 +0200 Subject: [PATCH 06/13] [IMP] test_pylint: check for python3 incompatibilities Cherry-pick of 9882f24313c988b1f8e40f1fbe734a798b0dbf7b --- odoo/addons/base/module/module.py | 1 + odoo/addons/base/module/wizard/base_module_upgrade.py | 1 + odoo/addons/base/res/res_config.py | 7 ++++++- odoo/addons/test_pylint/tests/test_pylint.py | 5 +++++ 4 files changed, 13 insertions(+), 1 deletion(-) diff --git a/odoo/addons/base/module/module.py b/odoo/addons/base/module/module.py index cc77b392a18..91449a25343 100644 --- a/odoo/addons/base/module/module.py +++ b/odoo/addons/base/module/module.py @@ -507,6 +507,7 @@ class Module(models.Model): self._cr.commit() env = api.Environment(self._cr, self._uid, self._context) + # pylint: disable=next-method-called config = env['ir.module.module'].next() or {} if config.get('type') not in ('ir.actions.act_window_close',): return config diff --git a/odoo/addons/base/module/wizard/base_module_upgrade.py b/odoo/addons/base/module/wizard/base_module_upgrade.py index 2544675df3d..a590111e644 100644 --- a/odoo/addons/base/module/wizard/base_module_upgrade.py +++ b/odoo/addons/base/module/wizard/base_module_upgrade.py @@ -79,4 +79,5 @@ class BaseModuleUpgrade(models.TransientModel): @api.multi def config(self): + # pylint: disable=next-method-called return self.env['res.config'].next() diff --git a/odoo/addons/base/res/res_config.py b/odoo/addons/base/res/res_config.py index 4176d57ae52..7fd426ed2c5 100644 --- a/odoo/addons/base/res/res_config.py +++ b/odoo/addons/base/res/res_config.py @@ -53,9 +53,9 @@ class ResConfigConfigurable(models.TransientModel): ''' _name = 'res.config' - @api.multi def start(self): + # pylint: disable=next-method-called return self.next() @api.multi @@ -105,6 +105,7 @@ class ResConfigConfigurable(models.TransientModel): an action dictionary -- executes the action provided by calling ``next``. """ + # pylint: disable=next-method-called return self.execute() or self.next() @api.multi @@ -116,6 +117,7 @@ class ResConfigConfigurable(models.TransientModel): an action dictionary -- executes the action provided by calling ``next``. """ + # pylint: disable=next-method-called return self.cancel() or self.next() @api.multi @@ -130,6 +132,7 @@ class ResConfigConfigurable(models.TransientModel): an action dictionary -- executes the action provided by calling ``next``. """ + # pylint: disable=next-method-called return self.cancel() or self.next() @@ -561,6 +564,8 @@ class ResConfigSettings(models.TransientModel, ResConfigModuleInstallationMixin) # are no longer valid. So we reset the environment. self.env.reset() self = self.env()[self._name] + + # pylint: disable=next-method-called config = self.env['res.config'].next() or {} if config.get('type') not in ('ir.actions.act_window_close',): return config diff --git a/odoo/addons/test_pylint/tests/test_pylint.py b/odoo/addons/test_pylint/tests/test_pylint.py index d1a40dc43c2..d5df3d60ecb 100644 --- a/odoo/addons/test_pylint/tests/test_pylint.py +++ b/odoo/addons/test_pylint/tests/test_pylint.py @@ -25,6 +25,11 @@ class TestPyLint(TransactionCase): 'W0123', # eval used 'W0101', # unreachable code + # py3k checks + 'print-statement', + 'backtick', + 'next-method-called', + 'misplaced-future', 'relative-import', 'deprecated-module', From a567b50035f73575c658ad6f87070713e12b4b74 Mon Sep 17 00:00:00 2001 From: Christophe Simonis Date: Wed, 26 Apr 2017 17:55:16 +0200 Subject: [PATCH 07/13] [IMP] test_pylint: check for badly written try/except Cherry-pick of 0ebe59f5d013c8d082a25cda243219a76508c10e --- .../test_pylint/tests/_odoo_checkers.py | 44 +++++++++++++++++++ odoo/addons/test_pylint/tests/test_pylint.py | 16 ++++++- 2 files changed, 58 insertions(+), 2 deletions(-) create mode 100644 odoo/addons/test_pylint/tests/_odoo_checkers.py diff --git a/odoo/addons/test_pylint/tests/_odoo_checkers.py b/odoo/addons/test_pylint/tests/_odoo_checkers.py new file mode 100644 index 00000000000..5648ac52219 --- /dev/null +++ b/odoo/addons/test_pylint/tests/_odoo_checkers.py @@ -0,0 +1,44 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- + +import tokenize + +from pylint import checkers, interfaces + +class PEP3110TokenChecker(checkers.BaseTokenChecker): + __implements__ = interfaces.ITokenChecker + name = 'python3' + enabled = False + + msgs = { + 'E3110': ('Python 3 uses `as` instead of comma token to catch exceptions', + 'no-comma-exception', + 'See http://www.python.org/dev/peps/pep-3110/', + {'maxversion': (3, 0)}), + } + + def process_tokens(self, tokens): + comma_found = in_except = False + pcount = 0 + for tok_type, token, start, _, _ in tokens: + if tok_type == tokenize.NAME and token == 'except': + in_except = True + comma_found = False + pcount = 0 + + elif tok_type == tokenize.OP and in_except: + if token == '(': + pcount += 1 + elif token == ')': + pcount -= 1 + elif token == ',' and pcount == 0: + comma_found = True + if token == ':': + if in_except and comma_found: + self.add_message('no-comma-exception', line=start[0]) + comma_found = in_except = False + pcount = 0 + + +def register(linter): + linter.register_checker(PEP3110TokenChecker(linter)) diff --git a/odoo/addons/test_pylint/tests/test_pylint.py b/odoo/addons/test_pylint/tests/test_pylint.py index d5df3d60ecb..51a63a6d4fa 100644 --- a/odoo/addons/test_pylint/tests/test_pylint.py +++ b/odoo/addons/test_pylint/tests/test_pylint.py @@ -8,12 +8,14 @@ except ImportError: pylint = None import subprocess from distutils.version import LooseVersion +import os from os.path import join from odoo.tests.common import TransactionCase from odoo import tools from odoo.modules import get_modules, get_module_path +HERE = os.path.dirname(os.path.realpath(__file__)) _logger = logging.getLogger(__name__) @@ -54,6 +56,8 @@ class TestPyLint(TransactionCase): 'old-raise-syntax', 'raising-string', 'unpacking-in-except', + + 'no-comma-exception', ] BAD_FUNCTIONS = [ @@ -122,13 +126,21 @@ class TestPyLint(TransactionCase): '--enable=%s' % ','.join(self.ENABLED_CODES), '--reports=n', "--msg-template='{msg} ({msg_id}) at {path}:{line}'", - '--load-plugins=pylint.extensions.bad_builtin', + '--load-plugins=pylint.extensions.bad_builtin,_odoo_checkers', '--bad-functions=%s' % ','.join(self.BAD_FUNCTIONS), '--deprecated-modules=%s' % ','.join(self.BAD_MODULES) ] + pypath = HERE + os.pathsep + os.environ.get('PYTHONPATH', '') + env = dict(os.environ, PYTHONPATH=pypath) try: - process = subprocess.Popen(['pylint'] + options + paths, stdout=subprocess.PIPE, stderr=subprocess.PIPE) + pylint_bin = tools.which('pylint') + process = subprocess.Popen( + [pylint_bin] + options + paths, + stdout=subprocess.PIPE, + stderr=subprocess.PIPE, + env=env, + ) except (OSError, IOError): self._skip_test('pylint executable not found in the path') else: From 6587eaaf0fa5e5ac7b048aea0ad6de3689fd78bd Mon Sep 17 00:00:00 2001 From: Christophe Simonis Date: Mon, 24 Jul 2017 18:16:03 +0200 Subject: [PATCH 08/13] [IMP] test_pylint: do not load default config file This will avoid useless warning when the file does not exists and possible false positive errors in case of invalid config in this file. Also strip output Cherry-pick of c955c3051cc6c68a39340e146c4120d514b61437 --- odoo/addons/test_pylint/tests/test_pylint.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/odoo/addons/test_pylint/tests/test_pylint.py b/odoo/addons/test_pylint/tests/test_pylint.py index 51a63a6d4fa..86d193b161d 100644 --- a/odoo/addons/test_pylint/tests/test_pylint.py +++ b/odoo/addons/test_pylint/tests/test_pylint.py @@ -122,6 +122,7 @@ class TestPyLint(TransactionCase): paths.append(module_path) options = [ + '--rcfile=%s' % os.devnull, '--disable=all', '--enable=%s' % ','.join(self.ENABLED_CODES), '--reports=n', @@ -146,4 +147,4 @@ class TestPyLint(TransactionCase): else: out, err = process.communicate() if process.returncode: - self.fail((b"\n" + out + b"\n" + err).decode('utf-8')) + self.fail("pylint test failed:\n" + (b"\n" + out + b"\n" + err).decode('utf-8').strip()) From abc482c3d4b614df9e4bcb659c16b30d779fd898 Mon Sep 17 00:00:00 2001 From: amoyaux Date: Tue, 22 Aug 2017 14:07:05 +0200 Subject: [PATCH 09/13] [REF] stock: remove function action_show_picking It is useless and no more used since refactoring introduced in saas-17 --- addons/stock/models/stock_move.py | 15 --------------- 1 file changed, 15 deletions(-) diff --git a/addons/stock/models/stock_move.py b/addons/stock/models/stock_move.py index 6379289bfe6..245304a31d6 100644 --- a/addons/stock/models/stock_move.py +++ b/addons/stock/models/stock_move.py @@ -983,18 +983,3 @@ class StockMove(models.Model): move.state = 'waiting' else: move.state = 'confirmed' - - @api.multi - def action_show_picking(self): - view = self.env.ref('stock.view_picking_form') - return { - 'name': _('Transfer'), - 'type': 'ir.actions.act_window', - 'view_type': 'form', - 'view_mode': 'form', - 'res_model': 'stock.picking', - 'views': [(view.id, 'form')], - 'view_id': view.id, - 'target': 'new', - 'res_id': self.id} - show_picking = action_show_picking From cdd7f09520c166a0288468d355be5ba94f38ffe3 Mon Sep 17 00:00:00 2001 From: amoyaux Date: Tue, 22 Aug 2017 13:49:44 +0200 Subject: [PATCH 10/13] [FIX] stock: action_assign groupby failure The function action assign on stock move try to comprare moves origin and moves dest in order to guess the available quanity. However with return it happens that the group by method could return twice the same key. The method sorted was not correctly used because it used recordset comparator instead of their id. Sorted on recordset use set comparaison (superset, subset,..) as defined in models.py with method __gt__, __lt__,... This commit change the function used on sort in order to use id but keep the old way on groupby so that we are able to get back recordset. --- addons/stock/models/stock_move.py | 18 +++++++++++++----- 1 file changed, 13 insertions(+), 5 deletions(-) diff --git a/addons/stock/models/stock_move.py b/addons/stock/models/stock_move.py index 245304a31d6..c29a941e3bb 100644 --- a/addons/stock/models/stock_move.py +++ b/addons/stock/models/stock_move.py @@ -741,9 +741,13 @@ class StockMove(models.Model): # the reserved quantity on the quants, convert it here in # `product_id.uom_id` (the UOM of the quants is the UOM of the product). move_lines_in = move.move_orig_ids.filtered(lambda m: m.state == 'done').mapped('move_line_ids') - keys_in = ['location_dest_id', 'lot_id', 'result_package_id', 'owner_id'] + keys_in_groupby = ['location_dest_id', 'lot_id', 'result_package_id', 'owner_id'] + + def _keys_in_sorted(ml): + return (ml.location_dest_id.id, ml.lot_id.id, ml.result_package_id.id, ml.owner_id.id) + grouped_move_lines_in = {} - for k, g in groupby(sorted(move_lines_in, key=itemgetter(*keys_in)), key=itemgetter(*keys_in)): + for k, g in groupby(sorted(move_lines_in, key=_keys_in_sorted), key=itemgetter(*keys_in_groupby)): qty_done = 0 for ml in g: qty_done += ml.product_uom_id._compute_quantity(ml.qty_done, ml.product_id.uom_id) @@ -757,14 +761,18 @@ class StockMove(models.Model): moves_out_siblings_to_consider = moves_out_siblings & (assigned_moves + partially_available_moves) reserved_moves_out_siblings = moves_out_siblings.filtered(lambda m: m.state in ['partially_available', 'assigned']) move_lines_out_reserved = (reserved_moves_out_siblings | moves_out_siblings_to_consider).mapped('move_line_ids') - keys_out = ['location_id', 'lot_id', 'package_id', 'owner_id'] + keys_out_groupby = ['location_id', 'lot_id', 'package_id', 'owner_id'] + + def _keys_out_sorted(ml): + return (ml.location_id.id, ml.lot_id.id, ml.package_id.id, ml.owner_id.id) + grouped_move_lines_out = {} - for k, g in groupby(sorted(move_lines_out_done, key=itemgetter(*keys_out)), key=itemgetter(*keys_out)): + for k, g in groupby(sorted(move_lines_out_done, key=_keys_out_sorted), key=itemgetter(*keys_out_groupby)): qty_done = 0 for ml in g: qty_done += ml.product_uom_id._compute_quantity(ml.qty_done, ml.product_id.uom_id) grouped_move_lines_out[k] = qty_done - for k, g in groupby(sorted(move_lines_out_reserved, key=itemgetter(*keys_out)), key=itemgetter(*keys_out)): + for k, g in groupby(sorted(move_lines_out_reserved, key=_keys_out_sorted), key=itemgetter(*keys_out_groupby)): grouped_move_lines_out[k] = sum(self.env['stock.move.line'].concat(*list(g)).mapped('product_qty')) available_move_lines = {key: grouped_move_lines_in[key] - grouped_move_lines_out.get(key, 0) for key in grouped_move_lines_in.keys()} # pop key if the quantity available amount to 0 From d6c7749344d581529689d539af11355fdd23350a Mon Sep 17 00:00:00 2001 From: amoyaux Date: Wed, 23 Aug 2017 14:14:10 +0200 Subject: [PATCH 11/13] [FIX] mrp: workorder qty update consumed quantities This commit correct some variables renaming forgot in the stock refactoring introduced by commit 0a76e158e114e0e88f7e0158a3318662aa9b9c02 It also add a invisible field for active_move_line_ids in order to pass them to the create and avoid the error about uom_id. --- addons/mrp/models/mrp_workorder.py | 6 +++--- addons/mrp/views/mrp_workorder_views.xml | 3 +++ 2 files changed, 6 insertions(+), 3 deletions(-) diff --git a/addons/mrp/models/mrp_workorder.py b/addons/mrp/models/mrp_workorder.py index 5493e5928bc..99a7872a5e4 100644 --- a/addons/mrp/models/mrp_workorder.py +++ b/addons/mrp/models/mrp_workorder.py @@ -196,7 +196,7 @@ class MrpWorkorder(models.Model): move_lots[0].qty_done = new_qty elif move.product_id.tracking == 'serial': # Create extra pseudo record - qty_todo = new_qty - sum(move_lots.mapped('quantity')) + qty_todo = new_qty - sum(move_lots.mapped('qty_done')) if float_compare(qty_todo, 0.0, precision_rounding=move.product_uom.rounding) > 0: while float_compare(qty_todo, 0.0, precision_rounding=move.product_uom.rounding) > 0: self.active_move_line_ids += self.env['stock.move.line'].new({ @@ -217,8 +217,8 @@ class MrpWorkorder(models.Model): for move_lot in move_lots: if qty_todo <= 0: break - if not move_lot.lot_id and qty_todo >= move_lot.quantity: - qty_todo = qty_todo - move_lot.quantity + if not move_lot.lot_id and qty_todo >= move_lot.qty_done: + qty_todo = qty_todo - move_lot.qty_done self.active_move_line_ids -= move_lot # Difference operator else: #move_lot.product_qty = move_lot.product_qty - qty_todo diff --git a/addons/mrp/views/mrp_workorder_views.xml b/addons/mrp/views/mrp_workorder_views.xml index 58856092cb9..6276cacc5f5 100644 --- a/addons/mrp/views/mrp_workorder_views.xml +++ b/addons/mrp/views/mrp_workorder_views.xml @@ -152,6 +152,9 @@ + + + From 9db0bf1331bf4e730b9bc12043fada84753c3cbe Mon Sep 17 00:00:00 2001 From: amoyaux Date: Wed, 23 Aug 2017 11:47:52 +0200 Subject: [PATCH 12/13] [FIX] stock: show_details view error on edit button Show details view should directly relfect the stock move line related to a move. However the user was not able to update locations and it still exists a button that allow to edit the move line. Since the move line is the final object everything should be directly editable and the button should not exist This commit update the view in order to be able to edit information directly on it. --- addons/stock/views/stock_picking_views.xml | 13 +++++-------- 1 file changed, 5 insertions(+), 8 deletions(-) diff --git a/addons/stock/views/stock_picking_views.xml b/addons/stock/views/stock_picking_views.xml index ac9c856bdb3..30ec38c8f79 100644 --- a/addons/stock/views/stock_picking_views.xml +++ b/addons/stock/views/stock_picking_views.xml @@ -257,19 +257,16 @@ - - - - - - + + + + - + -