diff --git a/stock_quant_merge/README.rst b/stock_quant_merge/README.rst new file mode 100644 index 000000000..b5e48f2cf --- /dev/null +++ b/stock_quant_merge/README.rst @@ -0,0 +1,61 @@ +.. image:: https://img.shields.io/badge/licence-AGPL--3-blue.svg + :target: http://www.gnu.org/licenses/agpl-3.0-standalone.html + :alt: License: AGPL-3 + +=========== +Quant merge +=========== + +Odoo splits quants each time a reservation is done: this module makes Odoo +merge them back if they still meet the following requirements: + +* same product +* same serial number/lot +* same location +* same package + +Usage +===== + +The merge is done automatically when a reservation is undone. No user intervention is needed. + +.. image:: https://odoo-community.org/website/image/ir.attachment/5784_f2813bd/datas + :alt: Try me on Runbot + :target: https://runbot.odoo-community.org/runbot/153/8.0 + +Bug Tracker +=========== + +Bugs are tracked on `GitHub Issues +`_. In case of trouble, please +check there if your issue has already been reported. If you spotted it first, +help us smashing it by providing a detailed and welcomed `feedback +`_. + +Credits +======= + +Contributors +------------ +* Oihane Crucelaegui +* Pedro M. Baeza +* Ana Juaristi +* Lionel Sausin + +Maintainer +---------- + +.. image:: https://odoo-community.org/logo.png + :alt: Odoo Community Association + :target: https://odoo-community.org + +This module is maintained by the OCA. + +OCA, or the Odoo Community Association, is a nonprofit organization whose +mission is to support the collaborative development of Odoo features and +promote its widespread use. + +To contribute to this module, please visit https://odoo-community.org. diff --git a/stock_quant_merge/__init__.py b/stock_quant_merge/__init__.py new file mode 100644 index 000000000..d50e8c06d --- /dev/null +++ b/stock_quant_merge/__init__.py @@ -0,0 +1,7 @@ +# -*- coding: utf-8 -*- +# © 2015 OdooMRP team +# © 2015 AvanzOSC +# © 2015 Serv. Tecnol. Avanzados - Pedro M. Baeza +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html). + +from . import models diff --git a/stock_quant_merge/__openerp__.py b/stock_quant_merge/__openerp__.py new file mode 100644 index 000000000..da087b8e2 --- /dev/null +++ b/stock_quant_merge/__openerp__.py @@ -0,0 +1,21 @@ +# -*- coding: utf-8 -*- +# © 2015 OdooMRP team +# © 2015 AvanzOSC +# © 2015 Serv. Tecnol. Avanzados - Pedro M. Baeza +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html). +{ + "name": "Stock - Quant merge", + "version": "8.0.1.0.0", + "depends": [ + "stock", + ], + "author": "OdooMRP team," + "AvanzOSC," + "Serv. Tecnol. Avanzados - Pedro M. Baeza," + "Odoo Community Association (OCA)", + "website": "http://www.odoomrp.com", + "category": "Warehouse Management", + "installable": False, + "license": "AGPL-3", + "images": [], +} diff --git a/stock_quant_merge/i18n/de.po b/stock_quant_merge/i18n/de.po new file mode 100644 index 000000000..cd2df2bd4 --- /dev/null +++ b/stock_quant_merge/i18n/de.po @@ -0,0 +1,23 @@ +# Translation of Odoo Server. +# This file contains the translation of the following modules: +# * stock_quant_merge +# +# Translators: +msgid "" +msgstr "" +"Project-Id-Version: stock-logistics-warehouse (8.0)\n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2016-01-14 01:38+0000\n" +"PO-Revision-Date: 2015-12-03 11:29+0000\n" +"Last-Translator: <>\n" +"Language-Team: German (http://www.transifex.com/oca/OCA-stock-logistics-warehouse-8-0/language/de/)\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: \n" +"Language: de\n" +"Plural-Forms: nplurals=2; plural=(n != 1);\n" + +#. module: stock_quant_merge +#: model:ir.model,name:stock_quant_merge.model_stock_quant +msgid "Quants" +msgstr "Quants" diff --git a/stock_quant_merge/i18n/en.po b/stock_quant_merge/i18n/en.po new file mode 100644 index 000000000..940ab0e04 --- /dev/null +++ b/stock_quant_merge/i18n/en.po @@ -0,0 +1,23 @@ +# Translation of Odoo Server. +# This file contains the translation of the following modules: +# * stock_quant_merge +# +# Translators: +msgid "" +msgstr "" +"Project-Id-Version: stock-logistics-warehouse (8.0)\n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2015-12-04 01:47+0000\n" +"PO-Revision-Date: 2015-12-03 11:29+0000\n" +"Last-Translator: OCA Transbot \n" +"Language-Team: English (http://www.transifex.com/oca/OCA-stock-logistics-warehouse-8-0/language/en/)\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: \n" +"Language: en\n" +"Plural-Forms: nplurals=2; plural=(n != 1);\n" + +#. module: stock_quant_merge +#: model:ir.model,name:stock_quant_merge.model_stock_quant +msgid "Quants" +msgstr "Quants" diff --git a/stock_quant_merge/i18n/es.po b/stock_quant_merge/i18n/es.po new file mode 100644 index 000000000..9388249a7 --- /dev/null +++ b/stock_quant_merge/i18n/es.po @@ -0,0 +1,23 @@ +# Translation of Odoo Server. +# This file contains the translation of the following modules: +# * stock_quant_merge +# +# Translators: +msgid "" +msgstr "" +"Project-Id-Version: odoomrp-wip (8.0)\n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2015-10-07 10:45+0000\n" +"PO-Revision-Date: 2015-10-09 10:50+0000\n" +"Last-Translator: Pedro M. Baeza \n" +"Language-Team: Spanish (http://www.transifex.com/oca/odoomrp-wip-8-0/language/es/)\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: \n" +"Language: es\n" +"Plural-Forms: nplurals=2; plural=(n != 1);\n" + +#. module: stock_quant_merge +#: model:ir.model,name:stock_quant_merge.model_stock_quant +msgid "Quants" +msgstr "Quants" diff --git a/stock_quant_merge/i18n/fi.po b/stock_quant_merge/i18n/fi.po new file mode 100644 index 000000000..32072a33c --- /dev/null +++ b/stock_quant_merge/i18n/fi.po @@ -0,0 +1,24 @@ +# Translation of Odoo Server. +# This file contains the translation of the following modules: +# * stock_quant_merge +# +# Translators: +# Miku Laitinen , 2016 +msgid "" +msgstr "" +"Project-Id-Version: stock-logistics-warehouse (8.0)\n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2016-03-04 07:59+0000\n" +"PO-Revision-Date: 2016-03-05 08:46+0000\n" +"Last-Translator: Miku Laitinen \n" +"Language-Team: Finnish (http://www.transifex.com/oca/OCA-stock-logistics-warehouse-8-0/language/fi/)\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: \n" +"Language: fi\n" +"Plural-Forms: nplurals=2; plural=(n != 1);\n" + +#. module: stock_quant_merge +#: model:ir.model,name:stock_quant_merge.model_stock_quant +msgid "Quants" +msgstr "Määrät" diff --git a/stock_quant_merge/i18n/fr.po b/stock_quant_merge/i18n/fr.po new file mode 100644 index 000000000..99d8ca634 --- /dev/null +++ b/stock_quant_merge/i18n/fr.po @@ -0,0 +1,23 @@ +# Translation of Odoo Server. +# This file contains the translation of the following modules: +# * stock_quant_merge +# +# Translators: +msgid "" +msgstr "" +"Project-Id-Version: odoomrp-wip (8.0)\n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2015-10-07 10:45+0000\n" +"PO-Revision-Date: 2015-09-10 16:35+0000\n" +"Last-Translator: <>\n" +"Language-Team: French (http://www.transifex.com/oca/odoomrp-wip-8-0/language/fr/)\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: \n" +"Language: fr\n" +"Plural-Forms: nplurals=2; plural=(n > 1);\n" + +#. module: stock_quant_merge +#: model:ir.model,name:stock_quant_merge.model_stock_quant +msgid "Quants" +msgstr "Quants" diff --git a/stock_quant_merge/i18n/pt_BR.po b/stock_quant_merge/i18n/pt_BR.po new file mode 100644 index 000000000..0fcf08022 --- /dev/null +++ b/stock_quant_merge/i18n/pt_BR.po @@ -0,0 +1,23 @@ +# Translation of Odoo Server. +# This file contains the translation of the following modules: +# * stock_quant_merge +# +# Translators: +msgid "" +msgstr "" +"Project-Id-Version: odoomrp-wip (8.0)\n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2015-10-07 10:45+0000\n" +"PO-Revision-Date: 2015-10-09 03:19+0000\n" +"Last-Translator: danimaribeiro \n" +"Language-Team: Portuguese (Brazil) (http://www.transifex.com/oca/odoomrp-wip-8-0/language/pt_BR/)\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: \n" +"Language: pt_BR\n" +"Plural-Forms: nplurals=2; plural=(n > 1);\n" + +#. module: stock_quant_merge +#: model:ir.model,name:stock_quant_merge.model_stock_quant +msgid "Quants" +msgstr "Quants" diff --git a/stock_quant_merge/i18n/sl.po b/stock_quant_merge/i18n/sl.po new file mode 100644 index 000000000..f5cfbcebe --- /dev/null +++ b/stock_quant_merge/i18n/sl.po @@ -0,0 +1,23 @@ +# Translation of Odoo Server. +# This file contains the translation of the following modules: +# * stock_quant_merge +# +# Translators: +msgid "" +msgstr "" +"Project-Id-Version: odoomrp-wip (8.0)\n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2015-10-07 10:45+0000\n" +"PO-Revision-Date: 2015-09-20 19:06+0000\n" +"Last-Translator: Matjaž Mozetič \n" +"Language-Team: Slovenian (http://www.transifex.com/oca/odoomrp-wip-8-0/language/sl/)\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: \n" +"Language: sl\n" +"Plural-Forms: nplurals=4; plural=(n%100==1 ? 0 : n%100==2 ? 1 : n%100==3 || n%100==4 ? 2 : 3);\n" + +#. module: stock_quant_merge +#: model:ir.model,name:stock_quant_merge.model_stock_quant +msgid "Quants" +msgstr "Kvant" diff --git a/stock_quant_merge/models/__init__.py b/stock_quant_merge/models/__init__.py new file mode 100644 index 000000000..5e5e22c5e --- /dev/null +++ b/stock_quant_merge/models/__init__.py @@ -0,0 +1,7 @@ +# -*- coding: utf-8 -*- +# © 2015 OdooMRP team +# © 2015 AvanzOSC +# © 2015 Serv. Tecnol. Avanzados - Pedro M. Baeza +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html). + +from . import stock diff --git a/stock_quant_merge/models/stock.py b/stock_quant_merge/models/stock.py new file mode 100644 index 000000000..aaf70c89b --- /dev/null +++ b/stock_quant_merge/models/stock.py @@ -0,0 +1,48 @@ +# -*- coding: utf-8 -*- +# © 2015 OdooMRP team +# © 2015 AvanzOSC +# © 2015 Serv. Tecnol. Avanzados - Pedro M. Baeza +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html). + +from openerp import models, api + + +class StockQuant(models.Model): + _inherit = 'stock.quant' + + @api.multi + def _mergeable_domain(self): + """Return the quants which may be merged with the current record""" + self.ensure_one() + return [('id', '!=', self.id), + ('product_id', '=', self.product_id.id), + ('lot_id', '=', self.lot_id.id), + ('package_id', '=', self.package_id.id), + ('location_id', '=', self.location_id.id), + ('reservation_id', '=', False), + ('propagated_from_id', '=', self.propagated_from_id.id)] + + @api.multi + def merge_stock_quants(self): + # Get a copy of the recorset + pending_quants = self.browse(self.ids) + for quant2merge in self.filtered(lambda x: not x.reservation_id): + if quant2merge in pending_quants: + quants = self.search(quant2merge._mergeable_domain()) + cont = 1 + cost = quant2merge.cost + for quant in quants: + if (self._get_latest_move(quant2merge) == + self._get_latest_move(quant)): + quant2merge.sudo().qty += quant.qty + cost += quant.cost + cont += 1 + pending_quants -= quant + quant.with_context(force_unlink=True).sudo().unlink() + quant2merge.sudo().cost = cost / cont + + @api.model + def quants_unreserve(self, move): + quants = move.reserved_quant_ids + super(StockQuant, self).quants_unreserve(move) + quants.merge_stock_quants() diff --git a/stock_quant_merge/tests/__init__.py b/stock_quant_merge/tests/__init__.py new file mode 100644 index 000000000..b4d071dd8 --- /dev/null +++ b/stock_quant_merge/tests/__init__.py @@ -0,0 +1,5 @@ +# -*- coding: utf-8 -*- +# © 2014 Numérigraphe SARL +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html). + +from . import test_merge diff --git a/stock_quant_merge/tests/test_merge.py b/stock_quant_merge/tests/test_merge.py new file mode 100644 index 000000000..fbd3f6d89 --- /dev/null +++ b/stock_quant_merge/tests/test_merge.py @@ -0,0 +1,70 @@ +# -*- coding: utf-8 -*- +# © 2015 Numérigraphe SARL +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html). + +from openerp.tests.common import TransactionCase + + +class TestMerge(TransactionCase): + """Test the potential quantity on a product with a multi-line BoM""" + + def setUp(self): + super(TestMerge, self).setUp() + + # Get the warehouses + self.wh_main = self.browse_ref('stock.warehouse0') + self.wh_ch = self.browse_ref('stock.stock_warehouse_shop0') + + # Get a product + self.product = self.browse_ref('product.product_product_4') + + # Zero out the inventory of the product + inventory = self.env['stock.inventory'].create( + {'name': 'Remove product for test', + 'location_id': self.ref('stock.stock_location_locations'), + 'filter': 'product', + 'product_id': self.product.id}) + inventory.prepare_inventory() + inventory.reset_real_qty() + inventory.action_done() + + # Make sure we have some products in Chicago + inventory = self.env['stock.inventory'].create( + {'name': 'Test stock available for reservation', + 'location_id': self.wh_ch.lot_stock_id.id, + 'filter': 'none'}) + inventory.prepare_inventory() + self.env['stock.inventory.line'].create({ + 'inventory_id': inventory.id, + 'product_id': self.product.id, + 'location_id': self.wh_ch.lot_stock_id.id, + 'product_qty': 10.0}) + inventory.action_done() + + def test_merge(self): + quant_obj = self.env['stock.quant'] + domain = [('location_id', '=', self.wh_ch.lot_stock_id.id), + ('product_id', '=', self.product.id)] + + quants = quant_obj.search(domain) + self.assertEqual(len(quants), 1, "There should be 1 quant") + + # Make a reservation to split the quant + move = self.env['stock.move'].create( + {'name': 'Test move', + 'product_id': self.product.id, + 'location_id': self.wh_ch.lot_stock_id.id, + 'location_dest_id': self.wh_main.lot_stock_id.id, + 'product_uom_qty': 5.0, + 'product_uom': self.product.uom_id.id}) + move.action_confirm() + move.action_assign() + + quants = quant_obj.search(domain) + self.assertEqual(len(quants), 2, "There should be 2 quants") + + # Cancel the move : the quants should be merged back together + move.action_cancel() + + quants = quant_obj.search(domain) + self.assertEqual(len(quants), 1, "There should be 1 quant")