From 6fa1e022d47350bb8e1f9234fe38a5328dd4efc4 Mon Sep 17 00:00:00 2001 From: "Laurent Mignon (ACSONE)" Date: Thu, 9 Mar 2017 17:07:44 +0100 Subject: [PATCH] [MIG] packaging_uom, purchase_packaging, sale_packaging: Migrated to 10.0 --- purchase_packaging/README.rst | 32 +- purchase_packaging/__init__.py | 8 +- purchase_packaging/__manifest__.py | 23 ++ purchase_packaging/__openerp__.py | 40 --- purchase_packaging/hooks.py | 8 + purchase_packaging/models/__init__.py | 7 +- .../models/procurement_order.py | 30 ++ purchase_packaging/models/product.py | 49 --- .../models/product_supplier_info.py | 39 ++ purchase_packaging/models/purchase.py | 239 ------------- .../models/purchase_order_line.py | 179 ++++++++++ .../static/description/icon.png | Bin 0 -> 9455 bytes purchase_packaging/tests/__init__.py | 6 +- purchase_packaging/tests/test_packaging.py | 338 ------------------ .../tests/test_procurement_order.py | 261 ++++++++++++++ .../tests/test_product_supplier_info.py | 35 ++ .../tests/test_purchase_order_line.py | 81 +++++ ...ews.xml => product_supplier_info_view.xml} | 2 + .../views/purchase_order_line_view.xml | 26 ++ .../views/purchase_order_view.xml | 44 +++ purchase_packaging/views/purchase_views.xml | 52 --- 21 files changed, 755 insertions(+), 744 deletions(-) create mode 100755 purchase_packaging/__manifest__.py delete mode 100755 purchase_packaging/__openerp__.py create mode 100755 purchase_packaging/hooks.py create mode 100644 purchase_packaging/models/procurement_order.py delete mode 100644 purchase_packaging/models/product.py create mode 100644 purchase_packaging/models/product_supplier_info.py delete mode 100644 purchase_packaging/models/purchase.py create mode 100644 purchase_packaging/models/purchase_order_line.py create mode 100644 purchase_packaging/static/description/icon.png delete mode 100644 purchase_packaging/tests/test_packaging.py create mode 100644 purchase_packaging/tests/test_procurement_order.py create mode 100644 purchase_packaging/tests/test_product_supplier_info.py create mode 100644 purchase_packaging/tests/test_purchase_order_line.py rename purchase_packaging/views/{product_views.xml => product_supplier_info_view.xml} (92%) create mode 100644 purchase_packaging/views/purchase_order_line_view.xml create mode 100644 purchase_packaging/views/purchase_order_view.xml delete mode 100644 purchase_packaging/views/purchase_views.xml diff --git a/purchase_packaging/README.rst b/purchase_packaging/README.rst index dd1071593..8055da1e9 100644 --- a/purchase_packaging/README.rst +++ b/purchase_packaging/README.rst @@ -1,5 +1,6 @@ .. image:: https://img.shields.io/badge/licence-AGPL--3-blue.svg - :alt: License: AGPL-3 + :target: http://www.gnu.org/licenses/agpl-3.0-standalone.html + :alt: License: AGPL-3 Purchase Packaging ================== @@ -10,15 +11,6 @@ Purchase Packaging - On purchase order line compute the quantity with the quantity and unit of measure - - -Installation -============ - -To install this module, you need to: - -* Click on install button - Configuration ============= @@ -32,22 +24,26 @@ To configure this module, you need to: Usage ===== -For further information, please visit: - -* https://www.odoo.com/forum/help-1 +.. 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/10.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 -`here `_. - +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 ======= +Images +------ + +* Odoo Community Association: `Icon `_. + Contributors ------------ diff --git a/purchase_packaging/__init__.py b/purchase_packaging/__init__.py index e09445c76..baa5ee9ba 100755 --- a/purchase_packaging/__init__.py +++ b/purchase_packaging/__init__.py @@ -1,8 +1,6 @@ # -*- coding: utf-8 -*- +# Copyright 2015-2017 ACSONE SA/NV () +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). from . import models - - -def set_product_purchase_qty(cr, registry): - cr.execute("""update purchase_order_line - set product_purchase_qty = product_qty""") +from .hooks import post_init_hook diff --git a/purchase_packaging/__manifest__.py b/purchase_packaging/__manifest__.py new file mode 100755 index 000000000..abca09a7d --- /dev/null +++ b/purchase_packaging/__manifest__.py @@ -0,0 +1,23 @@ +# -*- coding: utf-8 -*- +# Copyright 2015-2017 ACSONE SA/NV () +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). +{ + "name": "Purchase Packaging", + "version": "10.0.1.0.0", + "author": 'ACSONE SA/NV, ' + 'Odoo Community Association (OCA)', + "category": "Warehouse", + "website": "http://www.acsone.eu", + 'summary': "In purchase, use package", + "depends": ["product", + "purchase", + "packaging_uom", + ], + "data": ["views/product_supplier_info_view.xml", + "views/purchase_order_view.xml", + "views/purchase_order_line_view.xml", + ], + "post_init_hook": "post_init_hook", + "license": "AGPL-3", + "installable": True, +} diff --git a/purchase_packaging/__openerp__.py b/purchase_packaging/__openerp__.py deleted file mode 100755 index 732f2b0fd..000000000 --- a/purchase_packaging/__openerp__.py +++ /dev/null @@ -1,40 +0,0 @@ -# -*- coding: utf-8 -*- -############################################################################## -# -# Authors: Laetitia Gangloff -# Copyright (c) 2015 Acsone SA/NV (http://www.acsone.eu) -# -# This program is free software: you can redistribute it and/or modify -# it under the terms of the GNU Affero General Public License as -# published by the Free Software Foundation, either version 3 of the -# License, or (at your option) any later version. -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU Affero General Public License for more details. -# -# You should have received a copy of the GNU Affero General Public License -# along with this program. If not, see . -# -############################################################################## - -{ - "name": "Purchase Packaging", - "version": "0.1", - 'author': "Acsone, Odoo Community Association (OCA)", - "category": "Other", - "website": "http://www.acsone.eu", - 'summary': "In purchase, use package", - "depends": ["product", - "purchase", - "packaging_uom", - ], - "data": ["views/product_views.xml", - "views/purchase_views.xml", - ], - "post_init_hook": "set_product_purchase_qty", - "license": "AGPL-3", - "installable": True, - "application": False, -} diff --git a/purchase_packaging/hooks.py b/purchase_packaging/hooks.py new file mode 100755 index 000000000..cbd99871a --- /dev/null +++ b/purchase_packaging/hooks.py @@ -0,0 +1,8 @@ +# -*- coding: utf-8 -*- +# Copyright 2015-2017 ACSONE SA/NV () +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). + + +def post_init_hook(cr, registry): + cr.execute("""update purchase_order_line + set product_purchase_qty = product_qty""") diff --git a/purchase_packaging/models/__init__.py b/purchase_packaging/models/__init__.py index 2ce3fc0ca..14144ded2 100755 --- a/purchase_packaging/models/__init__.py +++ b/purchase_packaging/models/__init__.py @@ -1,4 +1,7 @@ # -*- coding: utf-8 -*- +# Copyright 2015-2017 ACSONE SA/NV () +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). -from . import product -from . import purchase +from . import product_supplier_info +from . import purchase_order_line +from . import procurement_order diff --git a/purchase_packaging/models/procurement_order.py b/purchase_packaging/models/procurement_order.py new file mode 100644 index 000000000..48a1b233a --- /dev/null +++ b/purchase_packaging/models/procurement_order.py @@ -0,0 +1,30 @@ +# -*- coding: utf-8 -*- +# Copyright 2015-2017 ACSONE SA/NV () +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). +from odoo import api, models + + +class ProcurementOrder(models.Model): + _inherit = 'procurement.order' + + @api.multi + def _prepare_purchase_order_line(self, po, supplier): + """ add packaging and update product_uom/quantity if necessary + """ + self.ensure_one() + res = super(ProcurementOrder, self)._prepare_purchase_order_line( + po, supplier) + seller = self.product_id._select_seller( + partner_id=supplier.name, + quantity=res['product_qty'], + date=po.date_order and po.date_order[:10], + uom_id=self.product_id.uom_po_id) + if seller.packaging_id: + res['packaging_id'] = seller.packaging_id.id + new_uom_id = seller.product_uom + if new_uom_id.id != res['product_uom']: + res['product_uom'] = new_uom_id + qty = self.product_uom._compute_quantity( + self.product_qty, new_uom_id) + res['product_qty'] = max(qty, seller.min_qty) + return res diff --git a/purchase_packaging/models/product.py b/purchase_packaging/models/product.py deleted file mode 100644 index d165cb531..000000000 --- a/purchase_packaging/models/product.py +++ /dev/null @@ -1,49 +0,0 @@ -# -*- coding: utf-8 -*- -############################################################################## -# -# Authors: Laetitia Gangloff -# Copyright (c) 2015 Acsone SA/NV (http://www.acsone.eu) -# -# This program is free software: you can redistribute it and/or modify -# it under the terms of the GNU Affero General Public License as -# published by the Free Software Foundation, either version 3 of the -# License, or (at your option) any later version. -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU Affero General Public License for more details. -# -# You should have received a copy of the GNU Affero General Public License -# along with this program. If not, see . -# -############################################################################## - - -from openerp import api, fields, models - - -class ProductSupplierinfo(models.Model): - _inherit = "product.supplierinfo" - - @api.model - def _default_min_qty_uom_id(self): - return self.env.ref('product.product_uom_unit') - - packaging_id = fields.Many2one('product.packaging', 'Logisitical Units') - product_uom = fields.Many2one(compute='_compute_product_uom', - string="Supplier Unit of Measure", - readonly=True) - min_qty_uom_id = fields.Many2one('product.uom', - 'Minimal Unit of Measure Quantity', - required=True, - default=_default_min_qty_uom_id) - - @api.one - @api.depends('product_tmpl_id', 'packaging_id') - def _compute_product_uom(self): - """ Set product_uom as a computed field instead of a related field. - To use uom of link packaging - """ - self.product_uom = self.packaging_id.uom_id or \ - self.product_tmpl_id.uom_po_id diff --git a/purchase_packaging/models/product_supplier_info.py b/purchase_packaging/models/product_supplier_info.py new file mode 100644 index 000000000..ca214f489 --- /dev/null +++ b/purchase_packaging/models/product_supplier_info.py @@ -0,0 +1,39 @@ +# -*- coding: utf-8 -*- +# Copyright 2015-2017 ACSONE SA/NV () +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). +from odoo import api, fields, models + + +class ProductSupplierinfo(models.Model): + _inherit = "product.supplierinfo" + + @api.model + def _default_min_qty_uom_id(self): + return self.env.ref('product.product_uom_unit') + + packaging_id = fields.Many2one( + 'product.packaging', + 'Logisitical Units' + ) + product_uom = fields.Many2one( + compute='_compute_product_uom', + string="Supplier Unit of Measure", + related=False + ) + min_qty_uom_id = fields.Many2one( + 'product.uom', + 'Minimal Unit of Measure Quantity', + required=True, + default=_default_min_qty_uom_id + ) + + @api.multi + @api.depends('product_tmpl_id', 'packaging_id') + def _compute_product_uom(self): + """ Set product_uom as a computed field instead of a related field. + To use uom of link packaging + """ + for rec in self: + rec.product_uom = rec.packaging_id.uom_id or \ + rec.product_id.uom_po_id or \ + rec.product_tmpl_id.uom_po_id diff --git a/purchase_packaging/models/purchase.py b/purchase_packaging/models/purchase.py deleted file mode 100644 index 1910fcf3f..000000000 --- a/purchase_packaging/models/purchase.py +++ /dev/null @@ -1,239 +0,0 @@ -# -*- coding: utf-8 -*- -############################################################################## -# -# Authors: Laetitia Gangloff -# Copyright (c) 2015 Acsone SA/NV (http://www.acsone.eu) -# -# This program is free software: you can redistribute it and/or modify -# it under the terms of the GNU Affero General Public License as -# published by the Free Software Foundation, either version 3 of the -# License, or (at your option) any later version. -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU Affero General Public License for more details. -# -# You should have received a copy of the GNU Affero General Public License -# along with this program. If not, see . -# -############################################################################## - - -from openerp import api, fields, models -import openerp.addons.decimal_precision as dp - - -class PurchaseOrder(models.Model): - _inherit = "purchase.order" - - @api.cr_uid_context - def _prepare_order_line_move(self, cr, uid, order, order_line, picking_id, - group_id, context=None): - """ Set product_packaging on stock move - """ - result = super(PurchaseOrder, self)._prepare_order_line_move( - cr, uid, order, order_line, picking_id, group_id, - context=context) - if order_line.packaging_id: - for res in result: - res['product_packaging'] = order_line.packaging_id.id - return result - - -class PurchaseOrderLine(models.Model): - _inherit = "purchase.order.line" - - @api.model - def _default_product_purchase_uom_id(self): - return self.env.ref('product.product_uom_unit') - - product_tmpl_id = fields.Many2one(related='product_id.product_tmpl_id', - comodel_name='product.template') - packaging_id = fields.Many2one('product.packaging', 'Packaging') - product_purchase_qty = fields.Float( - 'Purchase quantity', - digits_compute=dp.get_precision('Product Unit of Measure'), - required=True, default=lambda *a: 1.0) - product_purchase_uom_id = fields.Many2one( - 'product.uom', 'Purchase Unit of Measure', required=True, - default=_default_product_purchase_uom_id) - product_qty = fields.Float( - compute="_compute_product_qty", string='Quantity', - inverse='_inverse_product_qty',) - - @api.one - @api.depends('product_purchase_uom_id', 'product_purchase_qty') - def _compute_product_qty(self): - """ - Compute the total quantity - """ - uom_obj = self.env['product.uom'] - to_uom = uom_obj.search( - [('category_id', '=', self.product_purchase_uom_id.category_id.id), - ('uom_type', '=', 'reference')], limit=1) - self.product_qty = uom_obj._compute_qty( - self.product_purchase_uom_id.id, - self.product_purchase_qty, - to_uom.id) - - @api.one - def _inverse_product_qty(self): - """ If product_quantity is set compute the purchase_qty - """ - if self.product_id and self.order_id.partner_id: - for supplier in self.product_id.seller_ids: - if (supplier.name.id == self.order_id.partner_id.id): - product_purchase_uom = supplier.min_qty_uom_id - uom_obj = self.env['product.uom'] - from_uom = uom_obj.search( - [('category_id', '=', - product_purchase_uom.category_id.id), - ('uom_type', '=', 'reference')], limit=1) - self.product_purchase_qty = uom_obj._compute_qty( - from_uom.id, - self.product_qty, - product_purchase_uom.id,) - self.product_purchase_uom_id = product_purchase_uom.id - break - else: - self.product_purchase_qty = self.product_qty - - @api.onchange("packaging_id") - def _onchange_packaging_id(self): - if self.packaging_id: - self.product_uom = self.packaging_id.uom_id - - @api.cr_uid_context - def onchange_product_id(self, cr, uid, ids, pricelist_id, product_id, qty, - uom_id, partner_id, date_order=False, - fiscal_position_id=False, date_planned=False, - name=False, price_unit=False, state='draft', - context=None): - """ set domain on product_purchase_uom_id and packaging_id - if there is no qty (first pass), - set the first packagigng, purchase_uom and purchase_qty - """ - product_product = self.pool['product.product'] - product_purchase_qty = 0 - product_purchase_uom_id = False - category_product_purchase_uom_id = False - packaging_id = False - new_uom_id = False - domain = {} - - if product_id and partner_id: - product = product_product.browse(cr, uid, product_id, - context=context) - first = True - packaging_ids = [] - po_uom_ids = [] - domain['packaging_id'] = [('id', 'in', packaging_ids)] - domain['product_purchase_uom_id'] = [('id', 'in', po_uom_ids)] - for supplier in product.seller_ids: - if (supplier.name.id == partner_id): - if first: - product_purchase_qty = supplier.min_qty - product_purchase_uom_id = supplier.min_qty_uom_id.id - category_product_purchase_uom_id = \ - supplier.min_qty_uom_id.category_id.id - new_uom_id = supplier.product_uom.id - if supplier.packaging_id: - packaging_id = supplier.packaging_id.id - first = False - po_uom_ids.append(supplier.min_qty_uom_id.id) - if supplier.packaging_id: - packaging_ids.append(supplier.packaging_id.id) - - uom_id = new_uom_id if not qty else uom_id - res = super(PurchaseOrderLine, self).onchange_product_id( - cr, uid, ids, pricelist_id, product_id, qty, uom_id, - partner_id, date_order=date_order, - fiscal_position_id=fiscal_position_id, date_planned=date_planned, - name=name, price_unit=price_unit, state=state, context=context) - - if product_id and partner_id and not qty \ - and category_product_purchase_uom_id: - res['value']['product_purchase_qty'] = product_purchase_qty - res['value']['product_purchase_uom_id'] = product_purchase_uom_id - uom_obj = self.pool['product.uom'] - to_uom_id = uom_obj.search( - cr, uid, - [('category_id', '=', category_product_purchase_uom_id), - ('uom_type', '=', 'reference')], limit=1, context=context)[0] - res['value']['product_qty'] = uom_obj._compute_qty( - cr, uid, product_purchase_uom_id, - product_purchase_qty, to_uom_id) - res['value']['packaging_id'] = packaging_id - if domain: - if res.get('domain'): - res['domain'].update(domain) - else: - res['domain'] = domain - return res - - @api.model - def update_vals(self, vals): - """ - When packaging_id is set, uom_id is readonly, - so we need to reset the uom value in the vals dict - """ - if vals.get('packaging_id'): - vals['product_uom'] = self.env['product.packaging'].browse( - vals['packaging_id']).uom_id.id - return vals - - @api.model - @api.returns('self', lambda rec: rec.id) - def create(self, vals): - if 'product_qty' not in vals and 'product_purchase_qty' in vals: - # compute product_qty to avoid inverse computation and reset to 1 - uom_obj = self.env['product.uom'] - product_purchase_uom = uom_obj.browse( - vals['product_purchase_uom_id']) - to_uom = uom_obj.search( - [('category_id', '=', product_purchase_uom.category_id.id), - ('uom_type', '=', 'reference')], limit=1) - vals['product_qty'] = uom_obj._compute_qty( - vals['product_purchase_uom_id'], - vals['product_purchase_qty'], - to_uom.id) - return super(PurchaseOrderLine, self).create(self.update_vals(vals)) - - @api.multi - def write(self, vals): - return super(PurchaseOrderLine, self).write(self.update_vals(vals)) - - -class ProcurementOrder(models.Model): - _inherit = 'procurement.order' - - @api.model - def _get_po_line_values_from_proc(self, procurement, partner, company, - schedule_date): - """ add packaging and update product_uom/quantity if necessary - """ - res = super(ProcurementOrder, self)._get_po_line_values_from_proc( - procurement, partner, company, schedule_date) - - uom_obj = self.env['product.uom'] - - for supplier in procurement.product_id.seller_ids: - if (supplier.name.id == partner.id): - if supplier.packaging_id: - res['packaging_id'] = supplier.packaging_id.id - new_uom_id = supplier.product_uom.id - if new_uom_id != res['product_uom']: - res['product_uom'] = new_uom_id - qty = uom_obj._compute_qty(procurement.product_uom.id, - procurement.product_qty, - new_uom_id) - res['product_qty'] = max(qty, supplier.qty) - pricelist = partner.property_product_pricelist_purchase - res['price_unit'] = pricelist.with_context( - uom=new_uom_id).price_get( - procurement.product_id.id, qty, - partner=partner.id)[pricelist.id] - break - - return res diff --git a/purchase_packaging/models/purchase_order_line.py b/purchase_packaging/models/purchase_order_line.py new file mode 100644 index 000000000..991aaeb1f --- /dev/null +++ b/purchase_packaging/models/purchase_order_line.py @@ -0,0 +1,179 @@ +# -*- coding: utf-8 -*- +# Copyright 2015-2017 ACSONE SA/NV () +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). +from odoo import api, fields, models +import odoo.addons.decimal_precision as dp + + +class PurchaseOrderLine(models.Model): + _inherit = "purchase.order.line" + + @api.model + def _default_product_purchase_uom_id(self): + return self.env.ref('product.product_uom_unit') + + product_tmpl_id = fields.Many2one( + related='product_id.product_tmpl_id', + comodel_name='product.template' + ) + packaging_id = fields.Many2one( + 'product.packaging', + 'Packaging' + ) + product_purchase_qty = fields.Float( + 'Purchase quantity', + digits=dp.get_precision('Product Unit of Measure'), + required=True, default=lambda *a: 1.0 + ) + product_purchase_uom_id = fields.Many2one( + 'product.uom', + 'Purchase Unit of Measure', + required=True, + default=_default_product_purchase_uom_id + ) + product_qty = fields.Float( + compute="_compute_product_qty", + string='Quantity', + inverse='_inverse_product_qty' + ) + + @api.multi + def _get_product_seller(self): + self.ensure_one() + return self.product_id._select_seller( + partner_id=self.order_id.partner_id, + quantity=self.product_qty, + date=self.order_id.date_order and self.order_id.date_order[:10], + uom_id=self.product_uom) + + @api.one + @api.depends('product_purchase_uom_id', 'product_purchase_qty') + def _compute_product_qty(self): + """ + Compute the total quantity + """ + uom_obj = self.env['product.uom'] + to_uom = uom_obj.search( + [('category_id', '=', self.product_purchase_uom_id.category_id.id), + ('uom_type', '=', 'reference')], limit=1) + if not self.product_purchase_uom_id: + return + self.product_qty = self.product_purchase_uom_id._compute_quantity( + self.product_purchase_qty, + to_uom) + + @api.one + def _inverse_product_qty(self): + """ If product_quantity is set compute the purchase_qty + """ + if self.product_id: + supplier = self._get_product_seller() + if supplier: + product_purchase_uom = supplier.min_qty_uom_id + uom_obj = self.env['product.uom'] + from_uom = uom_obj.search( + [('category_id', '=', + product_purchase_uom.category_id.id), + ('uom_type', '=', 'reference')], limit=1) + self.product_purchase_qty = from_uom._compute_quantity( + self.product_qty, + product_purchase_uom) + self.product_purchase_uom_id = product_purchase_uom.id + else: + self.product_purchase_qty = self.product_qty + + @api.onchange("packaging_id") + def _onchange_packaging_id(self): + if self.packaging_id: + self.product_uom = self.packaging_id.uom_id + + @api.onchange('product_id') + def onchange_product_id(self): + """ set domain on product_purchase_uom_id and packaging_id + set the first packagigng, purchase_uom and purchase_qty + """ + domain = {} + # call default implementation + # restore default values + defaults = self.default_get( + ['packaging_id', 'product_purchase_uom_id']) + self.packaging_id = self.packaging_id.browse( + defaults.get('packaging_id', [])) + self.product_purchase_uom_id = self.product_purchase_uom_id.browse( + defaults.get('product_purchase_uom_id', [])) + # add default domains + if self.product_id and self.partner_id: + domain['packaging_id'] = [ + ('id', 'in', self.product_id.mapped( + 'seller_ids.packaging_id.id'))] + domain['product_purchase_uom_id'] = \ + [('id', 'in', self.product_id.mapped( + 'seller_ids.min_qty_uom_id.id'))] + res = super(PurchaseOrderLine, self).onchange_product_id() + if self.product_id: + supplier = self._get_product_seller() + else: + supplier = self.product_id.seller_ids.browse([]) + if supplier.product_uom: + # use the uom from the suppleir + self.product_uom = supplier.product_uom + if supplier.min_qty_uom_id: + # if the supplier requires some min qty/uom, + self.product_purchase_qty = supplier.min_qty + self.product_purchase_uom_id = supplier.min_qty_uom_id + domain['product_purchase_uom_id'] = \ + [('id', '=', supplier.min_qty_uom_id.id)] + to_uom = self.env['product.uom'].search([ + ('category_id', '=', + supplier.min_qty_uom_id.category_id.id), + ('uom_type', '=', 'reference')], limit=1) + to_uom = to_uom and to_uom[0] + self.product_qty = supplier.min_qty_uom_id._compute_quantity( + supplier.min_qty, to_uom + ) + self.packaging_id = supplier.packaging_id + if domain: + if res.get('domain'): + res['domain'].update(domain) + else: + res['domain'] = domain + return res + + @api.multi + def _prepare_stock_moves(self, picking): + self.ensure_one() + val = super(PurchaseOrderLine, self)._prepare_stock_moves(picking) + for v in val: + v['product_packaging'] = self.packaging_id.id + return val + + @api.model + def update_vals(self, vals): + """ + When packaging_id is set, uom_id is readonly, + so we need to reset the uom value in the vals dict + """ + if vals.get('packaging_id'): + vals['product_uom'] = self.env['product.packaging'].browse( + vals['packaging_id']).uom_id.id + return vals + + @api.model + @api.returns('self', lambda rec: rec.id) + def create(self, vals): + if 'product_qty' not in vals and 'product_purchase_qty' in vals: + # compute product_qty to avoid inverse computation and reset to 1 + uom_obj = self.env['product.uom'] + product_purchase_uom = uom_obj.browse( + vals['product_purchase_uom_id']) + to_uom = uom_obj.search( + [('category_id', '=', product_purchase_uom.category_id.id), + ('uom_type', '=', 'reference')], limit=1) + vals['product_qty'] = to_uom._compute_quantity( + vals['product_purchase_qty'], + to_uom) + return super(PurchaseOrderLine, self).create(self.update_vals(vals)) + + @api.multi + def write(self, vals): + return super(PurchaseOrderLine, self).write(self.update_vals(vals)) diff --git a/purchase_packaging/static/description/icon.png b/purchase_packaging/static/description/icon.png new file mode 100644 index 0000000000000000000000000000000000000000..3a0328b516c4980e8e44cdb63fd945757ddd132d GIT binary patch literal 9455 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 z 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 Xviia!U7SGha1wx#SCgwmn*{w2TRX*I literal 0 HcmV?d00001 diff --git a/purchase_packaging/tests/__init__.py b/purchase_packaging/tests/__init__.py index c8a922e42..5eebe12ac 100644 --- a/purchase_packaging/tests/__init__.py +++ b/purchase_packaging/tests/__init__.py @@ -1,3 +1,7 @@ # -*- coding: utf-8 -*- +# Copyright 2015-2017 ACSONE SA/NV () +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). -from . import test_packaging +from . import test_product_supplier_info +from . import test_purchase_order_line +from . import test_procurement_order diff --git a/purchase_packaging/tests/test_packaging.py b/purchase_packaging/tests/test_packaging.py deleted file mode 100644 index 2af091910..000000000 --- a/purchase_packaging/tests/test_packaging.py +++ /dev/null @@ -1,338 +0,0 @@ -# -*- coding: utf-8 -*- -############################################################################## -# -# Authors: Laetitia Gangloff -# Copyright (c) 2015 Acsone SA/NV (http://www.acsone.eu) -# -# This program is free software: you can redistribute it and/or modify -# it under the terms of the GNU Affero General Public License as -# published by the Free Software Foundation, either version 3 of the -# License, or (at your option) any later version. -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU Affero General Public License for more details. -# -# You should have received a copy of the GNU Affero General Public License -# along with this program. If not, see . -# -############################################################################## - -import openerp.tests.common as common - - -class TestPackaging(common.TransactionCase): - - def setUp(self): - """ Create a packagings with uom product_uom_dozen on - * product_product_34 (uom is product_uom_unit) - """ - super(TestPackaging, self).setUp() - self.product_packaging_34 = self.env['product.packaging'].create( - {'product_tmpl_id': self.env.ref('product.product_product_34' - ).product_tmpl_id.id, - 'uom_id': self.env.ref('product.product_uom_dozen').id}) - self.sp_30 = self.env.ref('product.product_supplierinfo_30') - self.product_uom_8 = self.env['product.uom'].create( - {'category_id': self.env.ref('product.product_uom_categ_unit').id, - 'name': 'COL8', - 'factor_inv': 8, - 'uom_type': 'bigger', - 'rounding': 1.0, - }) - - def test_supplierinfo_product_uom(self): - """ Check product_uom of product_supplierinfo_30 is product_uom_unit - Set packaging_id product_packaging_34 on product_supplierinfo_30 - Check product_uom of product_supplierinfo_30 is product_uom_dozen - """ - self.assertEqual(self.sp_30.product_uom.id, - self.env.ref('product.product_uom_unit').id) - self.sp_30.packaging_id = self.product_packaging_34 - self.assertEqual(self.sp_30.product_uom.id, - self.env.ref('product.product_uom_dozen').id) - - def test_po_line(self): - """ On supplierinfo set product_uom_8 as min_qty_uom_id - On supplierinfo set 2 as min_qty - Create purchase order line with product product_product_34 - Check packaging_id is product_packaging_34 - Check product_purchase_uom_id is product_uom_8 - Check product_purchase_qty is 2 - Check product_qty is 8*2 = 16 - Check price_unit is 12*38 = 456 - Check product_uom is product_uom_dozen - Confirm po - Check stock move packaging is product_packaging_34 - Check stock move product_uom is product_uom_dozen - Check stock move product_qty is 16 - """ - self.sp_30.min_qty_uom_id = self.product_uom_8 - self.sp_30.min_qty = 2 - self.sp_30.packaging_id = self.product_packaging_34 - - po = self.env['purchase.order'].create( - {'partner_id': self.env.ref('base.res_partner_16').id, - 'location_id': self.env.ref('stock.stock_location_stock').id, - 'pricelist_id': self.env.ref('purchase.list0').id - }) - - vals = self.env['purchase.order.line'].onchange_product_id( - [], - po.pricelist_id.id, self.env.ref('product.product_product_34').id, - 0, False, po.partner_id.id, date_order=po.date_order, - fiscal_position_id=po.fiscal_position.id, date_planned=False, - name=False, price_unit=False, state=po.state) - vals['value']['order_id'] = po.id - vals['value']['product_id'] = self.env.ref( - 'product.product_product_34').id - po_line = self.env['purchase.order.line'].create(vals['value']) - self.assertEqual(po_line.packaging_id.id, - self.product_packaging_34.id) - self.assertEqual(po_line.product_purchase_uom_id.id, - self.product_uom_8.id) - self.assertAlmostEqual(po_line.product_purchase_qty, 2) - self.assertAlmostEqual(po_line.product_qty, 16) - self.assertAlmostEqual(po_line.price_unit, 456) - self.assertEqual(po_line.product_uom.id, - self.env.ref('product.product_uom_dozen').id) - po.signal_workflow('purchase_confirm') - sm = po.picking_ids[0].move_lines[0] - self.assertEqual(sm.product_packaging.id, - self.product_packaging_34.id) - self.assertEqual(sm.product_uom.id, - self.env.ref('product.product_uom_dozen').id) - self.assertAlmostEqual(sm.product_uom_qty, 16) - - def test_procurement(self): - """ On product set sale_price to 3 - On supplierinfo set min_qty as 0 - Create procurement line with rule buy and quantity 17 - run procurement - Check product_purchase_uom_id is product_uom_unit - Check product_purchase_qty is 17 - Check product_qty is 17 - Check packaging_id is False - Check product_uom is product_uom_unit - Check price_unit is 3 - Confirm Purchase Order to avoid group - Create procurement line with rule buy and quantity 1 dozen - run procurement - Check product_purchase_uom_id is product_uom_unit - Check product_purchase_qty is 12 - Check product_qty is 12 - Check packaging_id is False - Check product_uom is product_uom_unit - Check price_unit is 3 - Confirm Purchase Order to avoid group - On supplierinfo set product_uom_8 as min_qty_uom_id - Create procurement line with rule buy and quantity 17 - run procurement - Check product_purchase_uom_id is product_uom_8 - Check product_purchase_qty is 3 - Check product_qty is 8*3 = 24 - Check packaging_id is False - Check product_uom is product_uom_unit - Check price_unit is 3 - Confirm Purchase Order to avoid group - Create procurement line with rule buy and quantity 1 dozen - run procurement - Check product_purchase_uom_id is product_uom_8 - Check product_purchase_qty is 2 - Check product_qty is 8*2 = 16 - Check packaging_id is False - Check product_uom is product_uom_unit - Check price_unit is 3 - Confirm Purchase Order to avoid group - On supplierinfo set packaging product_packaging_34 (dozen) - Create procurement line with rule buy and quantity 17 - run procurement - Check product_purchase_uom_id is product_uom_8 - Check product_purchase_qty is 1 - Check product_qty is 8*1 = 8 - Check packaging_id is product_packaging_34 - Check product_uom is product_uom_dozen - Check price_unit is 3*12 = 36 - Confirm Purchase Order to avoid group - Create procurement line with rule buy and quantity 1 dozen - run procurement - Check product_purchase_uom_id is product_uom_8 - Check product_purchase_qty is 1 - Check product_qty is 8*1 = 8 - Check packaging_id is product_packaging_34 - Check product_uom is product_uom_dozen - Check price_unit is 3*12 = 36 - Confirm Purchase Order to avoid group - On supplierinfo set product_uom_unit as min_qty_uom_id - Create procurement line with rule buy and quantity 17 - run procurement - Check product_purchase_uom_id is product_uom_unit - Check product_purchase_qty is 2 - Check product_qty is 2 - Check packaging_id is product_packaging_34 - Check product_uom is product_uom_dozen - Check price_unit is 3*12 = 36 - Confirm Purchase Order to avoid group - Create procurement line with rule buy and quantity 1 dozen - run procurement - Check product_purchase_uom_id is product_uom_unit - Check product_purchase_qty is 1 - Check product_qty is 1 - Check packaging_id is product_packaging_34 - Check product_uom is product_uom_dozen - Check price_unit is 3*12 = 36 - Confirm Purchase Order to avoid group - """ - self.env.ref('product.product_product_34').route_ids = [( - 4, self.env.ref("purchase.route_warehouse0_buy").id)] - self.env.ref('product.product_product_34').standard_price = 3 - self.env.ref('product.product_uom_unit').rounding = 1 - procurement_obj = self.env['procurement.order'] - - self.sp_30.min_qty = 0 - - proc1 = procurement_obj.create( - {'name': 'test_procurement', - 'location_id': self.env.ref('stock.stock_location_stock').id, - 'product_id': self.env.ref('product.product_product_34').id, - 'product_qty': 17, - 'product_uom': self.env.ref('product.product_uom_unit').id}) - procurement_obj.run_scheduler() - self.assertEqual(self.env.ref('product.product_uom_unit'), - proc1.purchase_line_id.product_purchase_uom_id) - self.assertEqual(17, proc1.purchase_line_id.product_purchase_qty) - self.assertEqual(17, proc1.purchase_line_id.product_qty) - self.assertFalse(proc1.purchase_line_id.packaging_id) - self.assertEqual(self.env.ref('product.product_uom_unit'), - proc1.purchase_line_id.product_uom) - proc1.purchase_id.signal_workflow('purchase_confirm') - - proc1 = procurement_obj.create( - {'name': 'test_procurement', - 'location_id': self.env.ref('stock.stock_location_stock').id, - 'product_id': self.env.ref('product.product_product_34').id, - 'product_qty': 1, - 'product_uom': self.env.ref('product.product_uom_dozen').id}) - procurement_obj.run_scheduler() - self.assertEqual(self.env.ref('product.product_uom_unit'), - proc1.purchase_line_id.product_purchase_uom_id) - self.assertEqual(12, proc1.purchase_line_id.product_purchase_qty) - self.assertEqual(12, proc1.purchase_line_id.product_qty) - self.assertFalse(proc1.purchase_line_id.packaging_id) - self.assertEqual(self.env.ref('product.product_uom_unit'), - proc1.purchase_line_id.product_uom) - self.assertEqual(3, proc1.purchase_line_id.price_unit) - proc1.purchase_id.signal_workflow('purchase_confirm') - - self.sp_30.min_qty_uom_id = self.product_uom_8 - - proc1 = procurement_obj.create( - {'name': 'test_procurement', - 'location_id': self.env.ref('stock.stock_location_stock').id, - 'product_id': self.env.ref('product.product_product_34').id, - 'product_qty': 17, - 'product_uom': self.env.ref('product.product_uom_unit').id}) - procurement_obj.run_scheduler() - self.assertEqual(self.product_uom_8, - proc1.purchase_line_id.product_purchase_uom_id) - self.assertEqual(3, proc1.purchase_line_id.product_purchase_qty) - self.assertEqual(24, proc1.purchase_line_id.product_qty) - self.assertFalse(proc1.purchase_line_id.packaging_id) - self.assertEqual(self.env.ref('product.product_uom_unit'), - proc1.purchase_line_id.product_uom) - self.assertEqual(3, proc1.purchase_line_id.price_unit) - proc1.purchase_id.signal_workflow('purchase_confirm') - - proc1 = procurement_obj.create( - {'name': 'test_procurement', - 'location_id': self.env.ref('stock.stock_location_stock').id, - 'product_id': self.env.ref('product.product_product_34').id, - 'product_qty': 1, - 'product_uom': self.env.ref('product.product_uom_dozen').id}) - procurement_obj.run_scheduler() - self.assertEqual(self.product_uom_8, - proc1.purchase_line_id.product_purchase_uom_id) - self.assertEqual(2, proc1.purchase_line_id.product_purchase_qty) - self.assertEqual(16, proc1.purchase_line_id.product_qty) - self.assertFalse(proc1.purchase_line_id.packaging_id) - self.assertEqual(self.env.ref('product.product_uom_unit'), - proc1.purchase_line_id.product_uom) - self.assertEqual(3, proc1.purchase_line_id.price_unit) - proc1.purchase_id.signal_workflow('purchase_confirm') - - self.sp_30.packaging_id = self.product_packaging_34 - - proc1 = procurement_obj.create( - {'name': 'test_procurement', - 'location_id': self.env.ref('stock.stock_location_stock').id, - 'product_id': self.env.ref('product.product_product_34').id, - 'product_qty': 17, - 'product_uom': self.env.ref('product.product_uom_unit').id}) - procurement_obj.run_scheduler() - self.assertEqual(self.product_uom_8, - proc1.purchase_line_id.product_purchase_uom_id) - self.assertEqual(1, proc1.purchase_line_id.product_purchase_qty) - self.assertEqual(8, proc1.purchase_line_id.product_qty) - self.assertEqual(self.product_packaging_34, - proc1.purchase_line_id.packaging_id) - self.assertEqual(self.env.ref('product.product_uom_dozen'), - proc1.purchase_line_id.product_uom) - self.assertEqual(36, proc1.purchase_line_id.price_unit) - proc1.purchase_id.signal_workflow('purchase_confirm') - - proc1 = procurement_obj.create( - {'name': 'test_procurement', - 'location_id': self.env.ref('stock.stock_location_stock').id, - 'product_id': self.env.ref('product.product_product_34').id, - 'product_qty': 1, - 'product_uom': self.env.ref('product.product_uom_dozen').id}) - procurement_obj.run_scheduler() - self.assertEqual(self.product_uom_8, - proc1.purchase_line_id.product_purchase_uom_id) - self.assertEqual(1, proc1.purchase_line_id.product_purchase_qty) - self.assertEqual(8, proc1.purchase_line_id.product_qty) - self.assertEqual(self.product_packaging_34, - proc1.purchase_line_id.packaging_id) - self.assertEqual(self.env.ref('product.product_uom_dozen'), - proc1.purchase_line_id.product_uom) - self.assertEqual(36, proc1.purchase_line_id.price_unit) - proc1.purchase_id.signal_workflow('purchase_confirm') - - self.sp_30.min_qty_uom_id = self.env.ref('product.product_uom_unit') - - proc1 = procurement_obj.create( - {'name': 'test_procurement', - 'location_id': self.env.ref('stock.stock_location_stock').id, - 'product_id': self.env.ref('product.product_product_34').id, - 'product_qty': 17, - 'product_uom': self.env.ref('product.product_uom_unit').id}) - procurement_obj.run_scheduler() - self.assertEqual(self.env.ref('product.product_uom_unit'), - proc1.purchase_line_id.product_purchase_uom_id) - self.assertEqual(2, proc1.purchase_line_id.product_purchase_qty) - self.assertEqual(2, proc1.purchase_line_id.product_qty) - self.assertEqual(self.product_packaging_34, - proc1.purchase_line_id.packaging_id) - self.assertEqual(self.env.ref('product.product_uom_dozen'), - proc1.purchase_line_id.product_uom) - self.assertEqual(36, proc1.purchase_line_id.price_unit) - proc1.purchase_id.signal_workflow('purchase_confirm') - - proc1 = procurement_obj.create( - {'name': 'test_procurement', - 'location_id': self.env.ref('stock.stock_location_stock').id, - 'product_id': self.env.ref('product.product_product_34').id, - 'product_qty': 1, - 'product_uom': self.env.ref('product.product_uom_dozen').id}) - procurement_obj.run_scheduler() - self.assertEqual(self.env.ref('product.product_uom_unit'), - proc1.purchase_line_id.product_purchase_uom_id) - self.assertEqual(1, proc1.purchase_line_id.product_purchase_qty) - self.assertEqual(1, proc1.purchase_line_id.product_qty) - self.assertEqual(self.product_packaging_34, - proc1.purchase_line_id.packaging_id) - self.assertEqual(self.env.ref('product.product_uom_dozen'), - proc1.purchase_line_id.product_uom) - self.assertEqual(36, proc1.purchase_line_id.price_unit) - proc1.purchase_id.signal_workflow('purchase_confirm') diff --git a/purchase_packaging/tests/test_procurement_order.py b/purchase_packaging/tests/test_procurement_order.py new file mode 100644 index 000000000..584e44f62 --- /dev/null +++ b/purchase_packaging/tests/test_procurement_order.py @@ -0,0 +1,261 @@ +# -*- coding: utf-8 -*- +# Copyright 2015-2017 ACSONE SA/NV () +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). +import odoo.tests.common as common + + +class TestProcurementOrder(common.TransactionCase): + + def setUp(self): + """ Create a packagings with uom product_uom_dozen on + * product_product_3 (uom is product_uom_unit) + """ + super(TestProcurementOrder, self).setUp() + self.product_packaging_3 = self.env['product.packaging'].create( + {'product_tmpl_id': self.env.ref('product.product_product_3' + ).product_tmpl_id.id, + 'uom_id': self.env.ref('product.product_uom_dozen').id, + 'name': 'Packaging Dozen'}) + self.sp_30 = self.env.ref('product.product_supplierinfo_1') + self.sp_30.product_tmpl_id = self.product_packaging_3.product_tmpl_id + self.sp_30.currency_id = self.env.user.company_id.currency_id + self.product_uom_8 = self.env['product.uom'].create( + {'category_id': self.env.ref('product.product_uom_categ_unit').id, + 'name': 'COL8', + 'factor_inv': 8, + 'uom_type': 'bigger', + 'rounding': 1.0, + }) + self.env['purchase.order'].search( + [("state", "=", "draft")]).button_cancel() + + def test_procurement(self): + # On supplierinfo set price to 3 + # On supplierinfo set min_qty as 0 + # Create procurement line with rule buy and quantity 17 + # run procurement + self.env.ref('product.product_product_3').route_ids = [( + 4, self.env.ref("purchase.route_warehouse0_buy").id)] + self.env.ref('product.product_uom_unit').rounding = 1 + procurement_obj = self.env['procurement.order'] + + self.sp_30.min_qty = 0 + self.sp_30.price = 3 + + proc1 = procurement_obj.create( + {'name': 'test_procurement', + 'location_id': self.env.ref('stock.stock_location_stock').id, + 'product_id': self.env.ref('product.product_product_3').id, + 'product_qty': 17, + 'product_uom': self.env.ref('product.product_uom_unit').id}) + procurement_obj.run_scheduler() + # Check product_purchase_uom_id is product_uom_unit + # Check product_purchase_qty is 17 + # Check product_qty is 17 + # Check packaging_id is False + # Check product_uom is product_uom_unit + # Check price_unit is 3 + self.assertEqual(self.env.ref('product.product_uom_unit'), + proc1.purchase_line_id.product_purchase_uom_id) + self.assertEqual(17, proc1.purchase_line_id.product_purchase_qty) + self.assertEqual(17, proc1.purchase_line_id.product_qty) + self.assertFalse(proc1.purchase_line_id.packaging_id) + self.assertEqual(self.env.ref('product.product_uom_unit'), + proc1.purchase_line_id.product_uom) + self.assertEqual(3, proc1.purchase_line_id.price_unit) + # Confirm Purchase Order to avoid group + proc1.purchase_id.button_confirm() + + # Create procurement line with rule buy and quantity 1 dozen + # run procurement + proc1 = procurement_obj.create( + {'name': 'test_procurement', + 'location_id': self.env.ref('stock.stock_location_stock').id, + 'product_id': self.env.ref('product.product_product_3').id, + 'product_qty': 1, + 'product_uom': self.env.ref('product.product_uom_dozen').id}) + procurement_obj.run_scheduler() + # Check product_purchase_uom_id is product_uom_unit + # Check product_purchase_qty is 12 + # Check product_qty is 12 + # Check packaging_id is False + # Check product_uom is product_uom_unit + # Check price_unit is 3 + self.assertEqual(self.env.ref('product.product_uom_unit'), + proc1.purchase_line_id.product_purchase_uom_id) + self.assertEqual(12, proc1.purchase_line_id.product_purchase_qty) + self.assertEqual(12, proc1.purchase_line_id.product_qty) + self.assertFalse(proc1.purchase_line_id.packaging_id) + self.assertEqual(self.env.ref('product.product_uom_unit'), + proc1.purchase_line_id.product_uom) + self.assertEqual(3, proc1.purchase_line_id.price_unit) + # Confirm Purchase Order to avoid group + proc1.purchase_id.button_confirm() + + # On supplierinfo set product_uom_8 as min_qty_uom_id + # Create procurement line with rule buy and quantity 17 + # run procurement + self.sp_30.min_qty_uom_id = self.product_uom_8 + + proc1 = procurement_obj.create( + {'name': 'test_procurement', + 'location_id': self.env.ref('stock.stock_location_stock').id, + 'product_id': self.env.ref('product.product_product_3').id, + 'product_qty': 17, + 'product_uom': self.env.ref('product.product_uom_unit').id}) + procurement_obj.run_scheduler() + # Check product_purchase_uom_id is product_uom_8 + # Check product_purchase_qty is 3 + # Check product_qty is 8*3 = 24 + # Check packaging_id is False + # Check product_uom is product_uom_unit + # Check price_unit is 3 + self.assertEqual(self.product_uom_8, + proc1.purchase_line_id.product_purchase_uom_id) + self.assertEqual(3, proc1.purchase_line_id.product_purchase_qty) + self.assertEqual(24, proc1.purchase_line_id.product_qty) + self.assertFalse(proc1.purchase_line_id.packaging_id) + self.assertEqual(self.env.ref('product.product_uom_unit'), + proc1.purchase_line_id.product_uom) + self.assertEqual(3, proc1.purchase_line_id.price_unit) + # Confirm Purchase Order to avoid group + proc1.purchase_id.button_confirm() + + # Create procurement line with rule buy and quantity 1 dozen + # run procurement + proc1 = procurement_obj.create( + {'name': 'test_procurement', + 'location_id': self.env.ref('stock.stock_location_stock').id, + 'product_id': self.env.ref('product.product_product_3').id, + 'product_qty': 1, + 'product_uom': self.env.ref('product.product_uom_dozen').id}) + procurement_obj.run_scheduler() + # Check product_purchase_uom_id is product_uom_8 + # Check product_purchase_qty is 2 + # Check product_qty is 8*2 = 16 + # Check packaging_id is False + # Check product_uom is product_uom_unit + # Check price_unit is 3 + self.assertEqual(self.product_uom_8, + proc1.purchase_line_id.product_purchase_uom_id) + self.assertEqual(2, proc1.purchase_line_id.product_purchase_qty) + self.assertEqual(16, proc1.purchase_line_id.product_qty) + self.assertFalse(proc1.purchase_line_id.packaging_id) + self.assertEqual(self.env.ref('product.product_uom_unit'), + proc1.purchase_line_id.product_uom) + self.assertEqual(3, proc1.purchase_line_id.price_unit) + # Confirm Purchase Order to avoid group + proc1.purchase_id.button_confirm() + + # On supplierinfo set packaging product_packaging_3 (dozen) + # Create procurement line with rule buy and quantity 17 + # run procurement + self.sp_30.packaging_id = self.product_packaging_3 + + proc1 = procurement_obj.create( + {'name': 'test_procurement', + 'location_id': self.env.ref('stock.stock_location_stock').id, + 'product_id': self.env.ref('product.product_product_3').id, + 'product_qty': 17, + 'product_uom': self.env.ref('product.product_uom_unit').id}) + procurement_obj.run_scheduler() + # Check product_purchase_uom_id is product_uom_8 + # Check product_purchase_qty is 1 + # Check product_qty is 8*1 = 8 + # Check packaging_id is product_packaging_3 + # Check product_uom is product_uom_dozen + # Check price_unit is 3*12 = 36 + self.assertEqual(self.product_uom_8, + proc1.purchase_line_id.product_purchase_uom_id) + self.assertEqual(1, proc1.purchase_line_id.product_purchase_qty) + self.assertEqual(8, proc1.purchase_line_id.product_qty) + self.assertEqual(self.product_packaging_3, + proc1.purchase_line_id.packaging_id) + self.assertEqual(self.env.ref('product.product_uom_dozen'), + proc1.purchase_line_id.product_uom) + self.assertEqual(3, proc1.purchase_line_id.price_unit) + # Confirm Purchase Order to avoid group + proc1.purchase_id.button_confirm() + + # Create procurement line with rule buy and quantity 1 dozen + # run procurement + proc1 = procurement_obj.create( + {'name': 'test_procurement', + 'location_id': self.env.ref('stock.stock_location_stock').id, + 'product_id': self.env.ref('product.product_product_3').id, + 'product_qty': 1, + 'product_uom': self.env.ref('product.product_uom_dozen').id}) + procurement_obj.run_scheduler() + # Check product_purchase_uom_id is product_uom_8 + # Check product_purchase_qty is 1 + # Check product_qty is 8*1 = 8 + # Check packaging_id is product_packaging_3 + # Check product_uom is product_uom_dozen + self.assertEqual(self.product_uom_8, + proc1.purchase_line_id.product_purchase_uom_id) + self.assertEqual(1, proc1.purchase_line_id.product_purchase_qty) + self.assertEqual(8, proc1.purchase_line_id.product_qty) + self.assertEqual(self.product_packaging_3, + proc1.purchase_line_id.packaging_id) + self.assertEqual(self.env.ref('product.product_uom_dozen'), + proc1.purchase_line_id.product_uom) + self.assertEqual(3, proc1.purchase_line_id.price_unit) + # Confirm Purchase Order to avoid group + proc1.purchase_id.button_confirm() + + # On supplierinfo set product_uom_unit as min_qty_uom_id + # Create procurement line with rule buy and quantity 17 + # run procurement + self.sp_30.min_qty_uom_id = self.env.ref('product.product_uom_unit') + + proc1 = procurement_obj.create( + {'name': 'test_procurement', + 'location_id': self.env.ref('stock.stock_location_stock').id, + 'product_id': self.env.ref('product.product_product_3').id, + 'product_qty': 17, + 'product_uom': self.env.ref('product.product_uom_unit').id}) + procurement_obj.run_scheduler() + # Check product_purchase_uom_id is product_uom_unit + # Check product_purchase_qty is 2 + # Check product_qty is 2 + # Check packaging_id is product_packaging_3 + # Check product_uom is product_uom_dozen + self.assertEqual(self.env.ref('product.product_uom_unit'), + proc1.purchase_line_id.product_purchase_uom_id) + self.assertEqual(2, proc1.purchase_line_id.product_purchase_qty) + self.assertEqual(2, proc1.purchase_line_id.product_qty) + self.assertEqual(self.product_packaging_3, + proc1.purchase_line_id.packaging_id) + self.assertEqual(self.env.ref('product.product_uom_dozen'), + proc1.purchase_line_id.product_uom) + self.assertEqual(3, proc1.purchase_line_id.price_unit) + # Confirm Purchase Order to avoid group + proc1.purchase_id.button_confirm() + + # Create procurement line with rule buy and quantity 1 dozen + # set purcahse price to 36 + # run procurement + self.sp_30.price = 36 + proc1 = procurement_obj.create( + {'name': 'test_procurement', + 'location_id': self.env.ref('stock.stock_location_stock').id, + 'product_id': self.env.ref('product.product_product_3').id, + 'product_qty': 1, + 'product_uom': self.env.ref('product.product_uom_dozen').id}) + procurement_obj.run_scheduler() + # Check product_purchase_uom_id is product_uom_unit + # Check product_purchase_qty is 1 + # Check product_qty is 1 + # Check packaging_id is product_packaging_3 + # Check product_uom is product_uom_dozen + # Check price_unit is 3*12 = 36 + self.assertEqual(self.env.ref('product.product_uom_unit'), + proc1.purchase_line_id.product_purchase_uom_id) + self.assertEqual(1, proc1.purchase_line_id.product_purchase_qty) + self.assertEqual(1, proc1.purchase_line_id.product_qty) + self.assertEqual(self.product_packaging_3, + proc1.purchase_line_id.packaging_id) + self.assertEqual(self.env.ref('product.product_uom_dozen'), + proc1.purchase_line_id.product_uom) + self.assertEqual(36, proc1.purchase_line_id.price_unit) + proc1.purchase_id.button_confirm() diff --git a/purchase_packaging/tests/test_product_supplier_info.py b/purchase_packaging/tests/test_product_supplier_info.py new file mode 100644 index 000000000..4c01e4446 --- /dev/null +++ b/purchase_packaging/tests/test_product_supplier_info.py @@ -0,0 +1,35 @@ +# -*- coding: utf-8 -*- +# Copyright 2015-2017 ACSONE SA/NV () +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). +import odoo.tests.common as common + + +class TestProductSupplierInfo(common.TransactionCase): + + def setUp(self): + """ Create a packagings with uom product_uom_dozen on + product_supplierinfo_1'product (uom is product_uom_unit) + """ + super(TestProductSupplierInfo, self).setUp() + self.product_supplier_info = self.env.ref( + 'product.product_supplierinfo_1') + self.product_tmpl_id = self.product_supplier_info.product_tmpl_id + self.product_supplier_info.product_tmpl_id.uom_po_id = self.env.ref( + 'product.product_uom_unit') + self.product_packaging_dozen = self.env['product.packaging'].create( + {'product_tmpl_id': self.product_tmpl_id.id, + 'uom_id': self.env.ref('product.product_uom_dozen').id, + 'name': 'Packaging Dozen'} + ) + + def test_supplierinfo_product_uom(self): + """ Check product_uom of product_supplierinfo_30 is product_uom_unit + Set packaging_id product_packaging_3 on product_supplierinfo_30 + Check product_uom of product_supplierinfo_30 is product_uom_dozen + """ + self.assertEqual(self.product_supplier_info.product_uom.id, + self.env.ref('product.product_uom_unit').id) + self.product_supplier_info.write( + {'packaging_id': self.product_packaging_dozen.id}) + self.assertEqual(self.product_supplier_info.product_uom.id, + self.env.ref('product.product_uom_dozen').id) diff --git a/purchase_packaging/tests/test_purchase_order_line.py b/purchase_packaging/tests/test_purchase_order_line.py new file mode 100644 index 000000000..4f3468f0c --- /dev/null +++ b/purchase_packaging/tests/test_purchase_order_line.py @@ -0,0 +1,81 @@ +# -*- coding: utf-8 -*- +# Copyright 2015-2017 ACSONE SA/NV () +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). +import odoo.tests.common as common + + +class TestPurchaseOrderLine(common.TransactionCase): + + def setUp(self): + """ Create a packagings with uom product_uom_dozen on + product_supplierinfo_1'product (uom is product_uom_unit) + """ + super(TestPurchaseOrderLine, self).setUp() + self.product_supplier_info = self.env.ref( + 'product.product_supplierinfo_1') + self.product_tmpl_id = self.product_supplier_info.product_tmpl_id + self.product_supplier_info.product_tmpl_id.uom_po_id = self.env.ref( + 'product.product_uom_unit') + self.product_supplier_info.min_qty = 1 + self.product_packaging_dozen = self.env['product.packaging'].create( + {'product_tmpl_id': self.product_tmpl_id.id, + 'uom_id': self.env.ref('product.product_uom_dozen').id, + 'name': 'Packaging Dozen'} + ) + self.product_uom_8 = self.env['product.uom'].create( + {'category_id': self.env.ref('product.product_uom_categ_unit').id, + 'name': 'COL8', + 'factor_inv': 8, + 'uom_type': 'bigger', + 'rounding': 1.0, + }) + + def test_po_line(self): + """ On supplierinfo set product_uom_8 as min_qty_uom_id + On supplierinfo set 2 as min_qty + Create purchase order line with product product_product_3 + Check packaging_id is product_packaging_dozen + Check product_purchase_uom_id is product_uom_8 + Check product_purchase_qty is 2 + Check product_qty is 8*2 = 16 + Check price_unit is 12*38 = 456 + Check product_uom is product_uom_dozen + Confirm po + Check stock move packaging is product_packaging_dozen + Check stock move product_uom is product_uom_dozen + Check stock move product_qty is 16 + """ + self.product_supplier_info.min_qty_uom_id = self.product_uom_8 + self.product_supplier_info.min_qty = 2 + self.product_supplier_info.packaging_id = self.product_packaging_dozen + + po = self.env['purchase.order'].create( + {'partner_id': self.product_supplier_info.name.id}) + po_line = po.order_line.new({ + 'product_id': self.product_tmpl_id.product_variant_id, + 'product_purchase_qty': 1.0, + 'product_purchase_uom_id': + po.order_line._default_product_purchase_uom_id(), + 'order_id': po + }) + po_line.onchange_product_id() + self.assertEqual(po_line.packaging_id.id, + self.product_packaging_dozen.id) + self.assertEqual(po_line.product_purchase_uom_id.id, + self.product_uom_8.id) + self.assertAlmostEqual(po_line.product_purchase_qty, 2) + self.assertAlmostEqual(po_line.product_qty, 16) + self.assertTrue(po_line.price_unit) + self.assertEqual(po_line.product_uom.id, + self.env.ref('product.product_uom_dozen').id) + values = po_line._convert_to_write( + {name: po_line[name] for name in po_line._cache}) + po.order_line.create(values) + # check that all the packaging informations are on the created picking + po._create_picking() + sm = po.picking_ids[0].move_lines[0] + self.assertEqual(sm.product_packaging.id, + self.product_packaging_dozen.id) + self.assertEqual(sm.product_uom.id, + self.env.ref('product.product_uom_dozen').id) + self.assertAlmostEqual(sm.product_uom_qty, 16) diff --git a/purchase_packaging/views/product_views.xml b/purchase_packaging/views/product_supplier_info_view.xml similarity index 92% rename from purchase_packaging/views/product_views.xml rename to purchase_packaging/views/product_supplier_info_view.xml index 08c59616b..bce0de3ff 100644 --- a/purchase_packaging/views/product_views.xml +++ b/purchase_packaging/views/product_supplier_info_view.xml @@ -1,4 +1,6 @@ + diff --git a/purchase_packaging/views/purchase_order_line_view.xml b/purchase_packaging/views/purchase_order_line_view.xml new file mode 100644 index 000000000..625ec03c9 --- /dev/null +++ b/purchase_packaging/views/purchase_order_line_view.xml @@ -0,0 +1,26 @@ + + + + + purchase.order.line.form (purchase_packaging) + purchase.order.line + + + + + 1 + + + + {'readonly' : [('packaging_id', '!=', False)]} + + + + + diff --git a/purchase_packaging/views/purchase_order_view.xml b/purchase_packaging/views/purchase_order_view.xml new file mode 100644 index 000000000..98562c6cd --- /dev/null +++ b/purchase_packaging/views/purchase_order_view.xml @@ -0,0 +1,44 @@ + + + + + purchase.order.form (purchase_packaging) + purchase.order + + + + + + + + + + 1 + + + + + + + {'readonly' : [('packaging_id', '!=', False)]} + + + + + + + + 1 + + + + + + + {'readonly' : [('packaging_id', '!=', False)]} + + + + + diff --git a/purchase_packaging/views/purchase_views.xml b/purchase_packaging/views/purchase_views.xml deleted file mode 100644 index e0ae6f8cf..000000000 --- a/purchase_packaging/views/purchase_views.xml +++ /dev/null @@ -1,52 +0,0 @@ - - - - - - purchase.order.form (purchase_packaging) - purchase.order - - - - - - - - - 1 - - - - - - - {'readonly' : [('packaging_id', '!=', False)]} - - - - - - - purchase.order.line.form (purchase_packaging) - purchase.order.line - - - - - 1 - - - - {'readonly' : [('packaging_id', '!=', False)]} - - - - - - -