diff --git a/addons/point_of_sale/models/pos_order.py b/addons/point_of_sale/models/pos_order.py index 38188be0cd4..5a89d2d81de 100644 --- a/addons/point_of_sale/models/pos_order.py +++ b/addons/point_of_sale/models/pos_order.py @@ -1551,7 +1551,7 @@ class PosOrderLine(models.Model): for line in self.filtered(lambda l: not l.is_total_cost_computed): product = line.product_id if line._is_product_storable_fifo_avco() and stock_moves: - product_cost = product._compute_average_price(0, line.qty, stock_moves.filtered(lambda ml: ml.product_id == product)) + product_cost = product._compute_average_price(0, line.qty, self._get_stock_moves_to_consider(stock_moves, product)) else: product_cost = product.standard_price line.total_cost = line.qty * product.cost_currency_id._convert( @@ -1563,6 +1563,9 @@ class PosOrderLine(models.Model): ) line.is_total_cost_computed = True + def _get_stock_moves_to_consider(self, stock_moves, product): + return stock_moves.filtered(lambda ml: ml.product_id.id == product.id) + @api.depends('price_subtotal', 'total_cost') def _compute_margin(self): for line in self: diff --git a/addons/pos_mrp/__init__.py b/addons/pos_mrp/__init__.py new file mode 100644 index 00000000000..dc5e6b693d1 --- /dev/null +++ b/addons/pos_mrp/__init__.py @@ -0,0 +1,4 @@ +# -*- coding: utf-8 -*- +# Part of Odoo. See LICENSE file for full copyright and licensing details. + +from . import models diff --git a/addons/pos_mrp/__manifest__.py b/addons/pos_mrp/__manifest__.py new file mode 100644 index 00000000000..7b5b999165e --- /dev/null +++ b/addons/pos_mrp/__manifest__.py @@ -0,0 +1,18 @@ +# -*- coding: utf-8 -*- +# Part of Odoo. See LICENSE file for full copyright and licensing details. + + +{ + 'name': 'pos_mrp', + 'version': '1.0', + 'category': 'Hidden', + 'sequence': 6, + 'summary': 'Link module between Point of Sale and Mrp', + 'description': """ + This is a link module between Point of Sale and Mrp. +""", + 'depends': ['point_of_sale', 'mrp'], + 'installable': True, + 'auto_install': True, + 'license': 'LGPL-3', +} diff --git a/addons/pos_mrp/models/__init__.py b/addons/pos_mrp/models/__init__.py new file mode 100644 index 00000000000..04f8c2cc55d --- /dev/null +++ b/addons/pos_mrp/models/__init__.py @@ -0,0 +1,4 @@ +# -*- coding: utf-8 -*- +# Part of Odoo. See LICENSE file for full copyright and licensing details. + +from . import pos_order diff --git a/addons/pos_mrp/models/pos_order.py b/addons/pos_mrp/models/pos_order.py new file mode 100644 index 00000000000..af58a1027dd --- /dev/null +++ b/addons/pos_mrp/models/pos_order.py @@ -0,0 +1,14 @@ +# -*- coding: utf-8 -*- +# Part of Odoo. See LICENSE file for full copyright and licensing details. + +from odoo import models + +class PosOrderLine(models.Model): + _inherit = "pos.order.line" + + def _get_stock_moves_to_consider(self, stock_moves, product): + bom = product.env['mrp.bom']._bom_find(product, company_id=stock_moves.company_id.id, bom_type='phantom')[product] + if not bom: + return super()._get_stock_moves_to_consider(stock_moves, product) + ml_product_to_consider = (product.bom_ids and bom.bom_line_ids.mapped('product_id').mapped('id')) or [product.id] + return stock_moves.filtered(lambda ml: ml.product_id.id in ml_product_to_consider and ml.bom_line_id) diff --git a/addons/pos_mrp/tests/__init__.py b/addons/pos_mrp/tests/__init__.py new file mode 100644 index 00000000000..4f3016c7759 --- /dev/null +++ b/addons/pos_mrp/tests/__init__.py @@ -0,0 +1,4 @@ +# -*- coding: utf-8 -*- +# Part of Odoo. See LICENSE file for full copyright and licensing details. + +from . import test_pos_mrp_flow diff --git a/addons/pos_mrp/tests/test_pos_mrp_flow.py b/addons/pos_mrp/tests/test_pos_mrp_flow.py new file mode 100644 index 00000000000..3dfcc8ef4b0 --- /dev/null +++ b/addons/pos_mrp/tests/test_pos_mrp_flow.py @@ -0,0 +1,83 @@ +# -*- coding: utf-8 -*- +# Part of Odoo. See LICENSE file for full copyright and licensing details. + +import odoo + +from odoo.addons.point_of_sale.tests.common import TestPointOfSaleCommon +from odoo.tests.common import Form + +@odoo.tests.tagged('post_install', '-at_install') +class TestPosMrp(TestPointOfSaleCommon): + def test_bom_kit_order_total_cost(self): + #create a product category that use fifo + category = self.env['product.category'].create({ + 'name': 'Category for kit', + 'property_cost_method': 'fifo', + }) + + self.kit = self.env['product.product'].create({ + 'name': 'Kit Product', + 'available_in_pos': True, + 'type': 'product', + 'lst_price': 10.0, + 'categ_id': category.id, + }) + + self.component_a = self.env['product.product'].create({ + 'name': 'Comp A', + 'type': 'product', + 'available_in_pos': True, + 'lst_price': 10.0, + 'standard_price': 5.0, + }) + + self.component_b = self.env['product.product'].create({ + 'name': 'Comp B', + 'type': 'product', + 'available_in_pos': True, + 'lst_price': 10.0, + 'standard_price': 10.0, + }) + + bom_product_form = Form(self.env['mrp.bom']) + bom_product_form.product_id = self.kit + bom_product_form.product_tmpl_id = self.kit.product_tmpl_id + bom_product_form.product_qty = 1.0 + bom_product_form.type = 'phantom' + with bom_product_form.bom_line_ids.new() as bom_line: + bom_line.product_id = self.component_a + bom_line.product_qty = 1.0 + with bom_product_form.bom_line_ids.new() as bom_line: + bom_line.product_id = self.component_b + bom_line.product_qty = 1.0 + self.bom_a = bom_product_form.save() + + self.pos_config.open_ui() + order = self.env['pos.order'].create({ + 'session_id': self.pos_config.current_session_id.id, + 'lines': [(0, 0, { + 'name': self.kit.name, + 'product_id': self.kit.id, + 'price_unit': self.kit.lst_price, + 'qty': 1, + 'tax_ids': [[6, False, []]], + 'price_subtotal': self.kit.lst_price, + 'price_subtotal_incl': self.kit.lst_price, + })], + 'pricelist_id': self.pos_config.pricelist_id.id, + 'amount_paid': self.kit.lst_price, + 'amount_total': self.kit.lst_price, + 'amount_tax': 0.0, + 'amount_return': 0.0, + 'to_invoice': False, + }) + payment_context = {"active_ids": order.ids, "active_id": order.id} + order_payment = self.PosMakePayment.with_context(**payment_context).create({ + 'amount': order.amount_total, + 'payment_method_id': self.cash_payment_method.id + }) + order_payment.with_context(**payment_context).check() + + self.pos_config.current_session_id.action_pos_session_closing_control() + pos_order = self.env['pos.order'].search([], order='id desc', limit=1) + self.assertEqual(pos_order.lines[0].total_cost, 15.0)