diff --git a/oca_dependencies.txt b/oca_dependencies.txt index 4403e5350df..4eaa4bbc782 100644 --- a/oca_dependencies.txt +++ b/oca_dependencies.txt @@ -1,2 +1,3 @@ server-tools +product-attribute server-ux diff --git a/purchase_order_secondary_unit/__init__.py b/purchase_order_secondary_unit/__init__.py new file mode 100644 index 00000000000..3275ac2adf3 --- /dev/null +++ b/purchase_order_secondary_unit/__init__.py @@ -0,0 +1,2 @@ +# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl). +from . import models diff --git a/purchase_order_secondary_unit/__manifest__.py b/purchase_order_secondary_unit/__manifest__.py new file mode 100644 index 00000000000..45c6dd339bb --- /dev/null +++ b/purchase_order_secondary_unit/__manifest__.py @@ -0,0 +1,23 @@ +# Copyright 2018 Tecnativa - Sergio Teruel +# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl). +{ + 'name': 'Purchase Order Secondary Unit', + 'summary': 'Purchase product in a secondary unit', + 'version': '11.0.1.0.0', + 'development_status': 'Beta', + 'category': 'Purchase', + 'website': 'https://github.com/OCA/purchase-workflow', + 'author': 'Tecnativa, Odoo Community Association (OCA)', + 'license': 'AGPL-3', + 'application': False, + 'installable': True, + 'auto_install': True, + 'depends': [ + 'purchase', + 'product_secondary_unit', + ], + 'data': [ + 'views/product_views.xml', + 'views/purchase_order_views.xml', + ], +} diff --git a/purchase_order_secondary_unit/i18n/es.po b/purchase_order_secondary_unit/i18n/es.po new file mode 100644 index 00000000000..9d4d5550865 --- /dev/null +++ b/purchase_order_secondary_unit/i18n/es.po @@ -0,0 +1,33 @@ +# Translation of Odoo Server. +# This file contains the translation of the following modules: +# * purchase_order_secondary_unit +# +msgid "" +msgstr "" +"Project-Id-Version: Odoo Server 11.0\n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2018-10-25 21:04+0000\n" +"PO-Revision-Date: 2018-10-25 23:05+0200\n" +"Language-Team: \n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"Plural-Forms: nplurals=2; plural=(n != 1);\n" +"X-Generator: Poedit 2.0.6\n" +"Last-Translator: \n" +"Language: es\n" + +#. module: purchase_order_secondary_unit +#: model:ir.model,name:purchase_order_secondary_unit.model_purchase_order_line +msgid "Purchase Order Line" +msgstr "Línea pedido de compra" + +#. module: purchase_order_secondary_unit +#: model:ir.model.fields,field_description:purchase_order_secondary_unit.field_purchase_order_line_secondary_uom_qty +msgid "Secondary Qty" +msgstr "Cdad. secundaria" + +#. module: purchase_order_secondary_unit +#: model:ir.model.fields,field_description:purchase_order_secondary_unit.field_purchase_order_line_secondary_uom_id +msgid "Secondary uom" +msgstr "UdM Secundaría" diff --git a/purchase_order_secondary_unit/models/__init__.py b/purchase_order_secondary_unit/models/__init__.py new file mode 100644 index 00000000000..1bc2f98dc03 --- /dev/null +++ b/purchase_order_secondary_unit/models/__init__.py @@ -0,0 +1,3 @@ +# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl). +from . import product_template +from . import purchase_order diff --git a/purchase_order_secondary_unit/models/product_template.py b/purchase_order_secondary_unit/models/product_template.py new file mode 100755 index 00000000000..bd0b29e83c5 --- /dev/null +++ b/purchase_order_secondary_unit/models/product_template.py @@ -0,0 +1,12 @@ +# Copyright 2018 Tecnativa - Sergio Teruel +# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl). +from odoo import fields, models + + +class ProductTemplate(models.Model): + _inherit = 'product.template' + + purchase_secondary_uom_id = fields.Many2one( + comodel_name='product.secondary.unit', + string='Default unit purchase', + ) diff --git a/purchase_order_secondary_unit/models/purchase_order.py b/purchase_order_secondary_unit/models/purchase_order.py new file mode 100644 index 00000000000..cdc7c378e98 --- /dev/null +++ b/purchase_order_secondary_unit/models/purchase_order.py @@ -0,0 +1,65 @@ +# Copyright 2018 Tecnativa - Sergio Teruel +# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl). +from odoo import api, fields, models +from odoo.addons import decimal_precision as dp +from odoo.tools.float_utils import float_compare, float_round + + +class PurchaseOrderLine(models.Model): + _inherit = 'purchase.order.line' + + secondary_uom_qty = fields.Float( + string='Secondary Qty', + digits=dp.get_precision('Product Unit of Measure'), + ) + secondary_uom_id = fields.Many2one( + comodel_name='product.secondary.unit', + string='Secondary uom', + ondelete='restrict', + ) + + @api.onchange('secondary_uom_id', 'secondary_uom_qty') + def _onchange_secondary_uom(self): + if not self.secondary_uom_id: + return + factor = self.secondary_uom_id.factor * self.product_uom.factor + qty = float_round( + self.secondary_uom_qty * factor, + precision_rounding=self.product_uom.rounding + ) + if float_compare( + self.product_qty, qty, + precision_rounding=self.product_uom.rounding) != 0: + self.product_qty = qty + + @api.onchange('product_qty') + def _onchange_product_qty_purchase_order_secondary_unit(self): + if not self.secondary_uom_id: + return + factor = self.secondary_uom_id.factor * self.product_uom.factor + qty = float_round( + self.product_qty / (factor or 1.0), + precision_rounding=self.secondary_uom_id.uom_id.rounding + ) + if float_compare( + self.secondary_uom_qty, qty, + precision_rounding=self.secondary_uom_id.uom_id.rounding) != 0: + self.secondary_uom_qty = qty + + @api.onchange('product_uom') + def _onchange_product_uom_purchase_order_secondary_unit(self): + if not self.secondary_uom_id: + return + factor = self.product_uom.factor * self.secondary_uom_id.factor + qty = float_round( + self.product_qty / (factor or 1.0), + precision_rounding=self.product_uom.rounding + ) + if float_compare( + self.secondary_uom_qty, qty, + precision_rounding=self.product_uom.rounding) != 0: + self.secondary_uom_qty = qty + + @api.onchange('product_id') + def _onchange_product_id_purchase_order_secondary_unit(self): + self.secondary_uom_id = self.product_id.purchase_secondary_uom_id diff --git a/purchase_order_secondary_unit/readme/CONTRIBUTORS.rst b/purchase_order_secondary_unit/readme/CONTRIBUTORS.rst new file mode 100644 index 00000000000..f24a0b0dc74 --- /dev/null +++ b/purchase_order_secondary_unit/readme/CONTRIBUTORS.rst @@ -0,0 +1 @@ +* Sergio Teruel diff --git a/purchase_order_secondary_unit/readme/DESCRIPTION.rst b/purchase_order_secondary_unit/readme/DESCRIPTION.rst new file mode 100644 index 00000000000..0d1e023e2d1 --- /dev/null +++ b/purchase_order_secondary_unit/readme/DESCRIPTION.rst @@ -0,0 +1,2 @@ +This module extends the functionality of purchase orders to allow buy products +in secondary unit of distinct category. diff --git a/purchase_order_secondary_unit/readme/USAGE.rst b/purchase_order_secondary_unit/readme/USAGE.rst new file mode 100644 index 00000000000..c2aef4ad5bf --- /dev/null +++ b/purchase_order_secondary_unit/readme/USAGE.rst @@ -0,0 +1,7 @@ +To use this module you need to: + +#. Go to a *Product > General Information tab*. +#. Create any record in "Secondary unit of measure". +#. Set the conversion factor. +#. Go to *Purchase > Quotation > Create*. +#. Change quantities in line and secondary unit (produc_qty will be change). diff --git a/purchase_order_secondary_unit/static/description/icon.png b/purchase_order_secondary_unit/static/description/icon.png new file mode 100644 index 00000000000..3a0328b516c Binary files /dev/null and b/purchase_order_secondary_unit/static/description/icon.png differ diff --git a/purchase_order_secondary_unit/tests/__init__.py b/purchase_order_secondary_unit/tests/__init__.py new file mode 100644 index 00000000000..6df95cebd79 --- /dev/null +++ b/purchase_order_secondary_unit/tests/__init__.py @@ -0,0 +1,2 @@ +# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl). +from . import test_purchase_order_secondary_unit diff --git a/purchase_order_secondary_unit/tests/test_purchase_order_secondary_unit.py b/purchase_order_secondary_unit/tests/test_purchase_order_secondary_unit.py new file mode 100644 index 00000000000..d9ea71d6ab1 --- /dev/null +++ b/purchase_order_secondary_unit/tests/test_purchase_order_secondary_unit.py @@ -0,0 +1,84 @@ +# Copyright 2018 Tecnativa - Sergio Teruel +# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl). +from odoo import fields +from odoo.tests import SavepointCase + + +class TestPurchaseOrderSecondaryUnit(SavepointCase): + + @classmethod + def setUpClass(cls): + super().setUpClass() + cls.product_uom_kg = cls.env.ref('product.product_uom_kgm') + cls.product_uom_gram = cls.env.ref('product.product_uom_gram') + cls.product_uom_unit = cls.env.ref('product.product_uom_unit') + cls.product = cls.env['product.product'].create({ + 'name': 'test', + 'uom_id': cls.product_uom_kg.id, + 'uom_po_id': cls.product_uom_kg.id, + 'secondary_uom_ids': [ + (0, 0, { + 'name': 'unit-700', + 'uom_id': cls.product_uom_unit.id, + 'factor': 0.7, + })], + }) + cls.secondary_unit = cls.env['product.secondary.unit'].search([ + ('product_tmpl_id', '=', cls.product.product_tmpl_id.id), + ]) + cls.product.purchase_secondary_uom_id = cls.secondary_unit.id + cls.partner = cls.env['res.partner'].create({ + 'name': 'test - partner', + 'supplier': True, + }) + po = cls.env['purchase.order'].new({ + 'partner_id': cls.partner.id, + 'company_id': cls.env.user.company_id.id, + 'order_line': [(0, 0, { + 'name': cls.product.name, + 'product_id': cls.product.id, + 'product_qty': 1, + 'product_uom': cls.product.uom_id.id, + 'price_unit': 1000.00, + 'date_planned': fields.Datetime.now(), + })], + }) + po.onchange_partner_id() + cls.order = cls.env['purchase.order'].create( + po._convert_to_write(po._cache)) + + def test_onchange_secondary_uom(self): + self.order.order_line.write({ + 'secondary_uom_id': self.secondary_unit.id, + 'secondary_uom_qty': 5, + }) + self.order.order_line._onchange_secondary_uom() + self.assertEqual( + self.order.order_line.product_qty, 3.5) + + def test_onchange_product_qty_purchase_order_secondary_unit(self): + self.order.order_line.update({ + 'secondary_uom_id': self.secondary_unit.id, + 'product_qty': 3.5, + }) + self.order.order_line.\ + _onchange_product_qty_purchase_order_secondary_unit() + self.assertEqual( + self.order.order_line.secondary_uom_qty, 5.0) + + def test_default_secondary_unit(self): + self.order.order_line.\ + _onchange_product_id_purchase_order_secondary_unit() + self.assertEqual( + self.order.order_line.secondary_uom_id, self.secondary_unit) + + def test_onchange_order_product_uom(self): + self.order.order_line.update({ + 'secondary_uom_id': self.secondary_unit.id, + 'product_uom': self.product_uom_gram.id, + 'product_qty': 3500.00, + }) + self.order.order_line.\ + _onchange_product_uom_purchase_order_secondary_unit() + self.assertEqual( + self.order.order_line.secondary_uom_qty, 5.0) diff --git a/purchase_order_secondary_unit/views/product_views.xml b/purchase_order_secondary_unit/views/product_views.xml new file mode 100755 index 00000000000..552fd225f6a --- /dev/null +++ b/purchase_order_secondary_unit/views/product_views.xml @@ -0,0 +1,20 @@ + + + + + + Product template Secondary Unit + product.template + + + + + + + + + + diff --git a/purchase_order_secondary_unit/views/purchase_order_views.xml b/purchase_order_secondary_unit/views/purchase_order_views.xml new file mode 100755 index 00000000000..d13ae94e257 --- /dev/null +++ b/purchase_order_secondary_unit/views/purchase_order_views.xml @@ -0,0 +1,29 @@ + + + + + + Purchase Order Secondary Unit + purchase.order + + + + + + + + + + + + + + +