{"action":"created","issue":{"url":"https://api.github.com/repos/OCA/stock-logistics-warehouse/issues/109","labels_url":"https://api.github.com/repos/OCA/stock-logistics-warehouse/issues/109/labels{/name}","comments_url":"https://api.github.com/repos/OCA/stock-logistics-warehouse/issues/109/comments","events_url":"https://api.github.com/repos/OCA/stock-logistics-warehouse/issues/109/events","html_url":"https://github.com/OCA/stock-logistics-warehouse/issues/109","id":121685809,"number":109,"title":"8.0 stock_quant_merge Crash when unreserve move linked to quants on multiple locations","user":{"login":"alexis-via","id":1157917,"avatar_url":"https://avatars.githubusercontent.com/u/1157917?v=3","gravatar_id":"","url":"https://api.github.com/users/alexis-via","html_url":"https://github.com/alexis-via","followers_url":"https://api.github.com/users/alexis-via/followers","following_url":"https://api.github.com/users/alexis-via/following{/other_user}","gists_url":"https://api.github.com/users/alexis-via/gists{/gist_id}","starred_url":"https://api.github.com/users/alexis-via/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/alexis-via/subscriptions","organizations_url":"https://api.github.com/users/alexis-via/orgs","repos_url":"https://api.github.com/users/alexis-via/repos","events_url":"https://api.github.com/users/alexis-via/events{/privacy}","received_events_url":"https://api.github.com/users/alexis-via/received_events","type":"User","site_admin":false},"labels":[],"state":"open","locked":false,"assignee":null,"milestone":null,"comments":5,"created_at":"2015-12-11T11:00:03Z","updated_at":"2015-12-11T12:57:17Z","closed_at":null,"body":"Here is the scenario to reproduce the bug on Odoo v8 with an update OCA/stock-logistics-warehouse/8.0 branch:\r\n\r\n1) In a demo database, install the module stock_quant_merge\r\n2) Create a new stockable product TEST1\r\n3) Let's say that we have 5 units of TEST1 on Self1 and 2 units on Shelf 2.\r\nIn the \"Inventory\" Tab, click on the \"Update\" button next to \"Quantity on Hand\":\r\n- Location : WH/Stock/Self 1\r\n- New Quantity on hand : 5\r\nClick a second time on the Update button:\r\n- Location : WH/Stock/Self 2\r\n- New Quantity on hand : 2\r\n5) Create a new sale order with one sale order line with:\r\n- product: TEST1\r\n- quantity: 6\r\nConfirm the sale order.\r\n6) On the related outgoing picking, click on \"Check Availability\": 6 units should be reserved in stock and the picking should become \"Ready to transfer\"\r\n7) Go back to the sale order and click on the \"Cancel Order\" button (which will unreserve the stock)\r\nYou get an error message : Expected singleton: stock.quant(41, 42)\r\n\r\nHere is the backtrace:\r\n```\r\nTraceback (most recent call last):\r\n  File \"/home/alexis/new_boite/dev/odoo8/openerp/http.py\", line 537, in _handle_exception\r\n    return super(JsonRequest, self)._handle_exception(exception)\r\n  File \"/home/alexis/new_boite/dev/odoo8/openerp/http.py\", line 574, in dispatch\r\n    result = self._call_function(**self.params)\r\n  File \"/home/alexis/new_boite/dev/odoo8/openerp/http.py\", line 310, in _call_function\r\n    return checked_call(self.db, *args, **kwargs)\r\n  File \"/home/alexis/new_boite/dev/odoo8/openerp/service/model.py\", line 118, in wrapper\r\n    return f(dbname, *args, **kwargs)\r\n  File \"/home/alexis/new_boite/dev/odoo8/openerp/http.py\", line 307, in checked_call\r\n    return self.endpoint(*a, **kw)\r\n  File \"/home/alexis/new_boite/dev/odoo8/openerp/http.py\", line 803, in __call__\r\n    return self.method(*args, **kw)\r\n  File \"/home/alexis/new_boite/dev/odoo8/openerp/http.py\", line 403, in response_wrap\r\n    response = f(*args, **kw)\r\n  File \"/home/alexis/new_boite/dev/odoo8/addons/web/controllers/main.py\", line 948, in call_button\r\n    action = self._call_kw(model, method, args, {})\r\n  File \"/home/alexis/new_boite/dev/odoo8/addons/web/controllers/main.py\", line 936, in _call_kw\r\n    return getattr(request.registry.get(model), method)(request.cr, request.uid, *args, **kwargs)\r\n  File \"/home/alexis/new_boite/dev/odoo8/openerp/api.py\", line 256, in wrapper\r\n    return old_api(self, *args, **kwargs)\r\n  File \"/home/alexis/new_boite/dev/odoo8/addons/sale/sale.py\", line 599, in action_cancel\r\n    sale_order_line_obj.button_cancel(cr, uid, line_ids, context=context)\r\n  File \"/home/alexis/new_boite/dev/odoo8/openerp/api.py\", line 256, in wrapper\r\n    return old_api(self, *args, **kwargs)\r\n  File \"/home/alexis/new_boite/dev/odoo8/addons/sale_stock/sale_stock.py\", line 364, in button_cancel\r\n    return super(sale_order_line, self).button_cancel(cr, uid, ids, context=context)\r\n  File \"/home/alexis/new_boite/dev/odoo8/openerp/api.py\", line 256, in wrapper\r\n    return old_api(self, *args, **kwargs)\r\n  File \"/home/alexis/new_boite/dev/odoo8/addons/sale/sale.py\", line 1047, in button_cancel\r\n    procurement_obj.cancel(cr, uid, sum([l.procurement_ids.ids for l in lines], []), context=context)\r\n  File \"/home/alexis/new_boite/dev/odoo8/openerp/api.py\", line 256, in wrapper\r\n    return old_api(self, *args, **kwargs)\r\n  File \"/home/alexis/new_boite/dev/odoo8/addons/stock/procurement.py\", line 103, in cancel\r\n    self.propagate_cancel(cr, uid, procurement, context=ctx)\r\n  File \"/home/alexis/new_boite/dev/odoo8/openerp/api.py\", line 256, in wrapper\r\n    return old_api(self, *args, **kwargs)\r\n  File \"/home/alexis/new_boite/dev/odoo8/addons/purchase/purchase.py\", line 1299, in propagate_cancel\r\n    return super(procurement_order, self).propagate_cancel(cr, uid, procurement, context=context)\r\n  File \"/home/alexis/new_boite/dev/odoo8/openerp/api.py\", line 256, in wrapper\r\n    return old_api(self, *args, **kwargs)\r\n  File \"/home/alexis/new_boite/dev/odoo8/addons/stock/procurement.py\", line 93, in propagate_cancel\r\n    self.pool.get('stock.move').action_cancel(cr, uid, [m.id for m in procurement.move_ids], context=context)\r\n  File \"/home/alexis/new_boite/dev/odoo8/openerp/api.py\", line 256, in wrapper\r\n    return old_api(self, *args, **kwargs)\r\n  File \"/home/alexis/new_boite/dev/odoo8/addons/stock/stock.py\", line 2336, in action_cancel\r\n    self.pool.get(\"stock.quant\").quants_unreserve(cr, uid, move, context=context)\r\n  File \"/home/alexis/new_boite/dev/odoo8/openerp/api.py\", line 256, in wrapper\r\n    return old_api(self, *args, **kwargs)\r\n  File \"/home/alexis/new_boite/dev/odoo8/openerp/api.py\", line 360, in old_api\r\n    result = method(recs, *args, **kwargs)\r\n  File \"/home/alexis/new_boite/dev/stock-logistics-warehouse8/stock_quant_merge/models/stock.py\", line 52, in quants_unreserve\r\n    quants.merge_stock_quants()\r\n  File \"/home/alexis/new_boite/dev/odoo8/openerp/api.py\", line 254, in wrapper\r\n    return new_api(self, *args, **kwargs)\r\n  File \"/home/alexis/new_boite/dev/stock-logistics-warehouse8/stock_quant_merge/models/stock.py\", line 33, in merge_stock_quants\r\n    quants = self.search(self._mergeable_domain())\r\n  File \"/home/alexis/new_boite/dev/odoo8/openerp/api.py\", line 254, in wrapper\r\n    return new_api(self, *args, **kwargs)\r\n  File \"/home/alexis/new_boite/dev/stock-logistics-warehouse8/stock_quant_merge/models/stock.py\", line 17, in _mergeable_domain\r\n    self.ensure_one()\r\n  File \"/home/alexis/new_boite/dev/odoo8/openerp/models.py\", line 5307, in ensure_one\r\n    raise except_orm(\"ValueError\", \"Expected singleton: %s\" % self)\r\nexcept_orm: ('ValueError', 'Expected singleton: stock.quant(41, 42)')\r\n```\r\n\r\nCode analysis: the bug is quite easy to understand:\r\n\r\nIn the method quants_unreserve(), you have:\r\n```\r\nquants = move.reserved_quant_ids\r\n```\r\nIn our scenario, the move will reserve 2 quants : one on Shelf1 and one on Self2, so quants is a recordset with 2 records.\r\n\r\nThen we do:\r\n```\r\nquants.merge_stock_quants()\r\n```\r\nSo, in the method merge_stock_quants(), self will have 2 records\r\n\r\nThen we do:\r\n```\r\nself._mergeable_domain()\r\n```\r\n\r\nBut, at the beginning of the method _mergeable_domain(), we have:\r\n```\r\nself.ensure_one()\r\n```\r\nSo it blocks !"},"comment":{"url":"https://api.github.com/repos/OCA/stock-logistics-warehouse/issues/comments/163931000","html_url":"https://github.com/OCA/stock-logistics-warehouse/issues/109#issuecomment-163931000","issue_url":"https://api.github.com/repos/OCA/stock-logistics-warehouse/issues/109","id":163931000,"user":{"login":"rvalyi","id":16926,"avatar_url":"https://avatars.githubusercontent.com/u/16926?v=3","gravatar_id":"","url":"https://api.github.com/users/rvalyi","html_url":"https://github.com/rvalyi","followers_url":"https://api.github.com/users/rvalyi/followers","following_url":"https://api.github.com/users/rvalyi/following{/other_user}","gists_url":"https://api.github.com/users/rvalyi/gists{/gist_id}","starred_url":"https://api.github.com/users/rvalyi/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/rvalyi/subscriptions","organizations_url":"https://api.github.com/users/rvalyi/orgs","repos_url":"https://api.github.com/users/rvalyi/repos","events_url":"https://api.github.com/users/rvalyi/events{/privacy}","received_events_url":"https://api.github.com/users/rvalyi/received_events","type":"User","site_admin":false},"created_at":"2015-12-11T12:57:17Z","updated_at":"2015-12-11T12:57:17Z","body":"@alexis-via you are right!"}}