diff --git a/purchase_stock_vendor_bill_breakdown/README.rst b/purchase_stock_vendor_bill_breakdown/README.rst new file mode 100644 index 00000000000..86be5b34fd6 --- /dev/null +++ b/purchase_stock_vendor_bill_breakdown/README.rst @@ -0,0 +1,3 @@ +============================================ +Purchase Stock Vendor Bill Product Breakdown +============================================ diff --git a/purchase_stock_vendor_bill_breakdown/__init__.py b/purchase_stock_vendor_bill_breakdown/__init__.py new file mode 100644 index 00000000000..0650744f6bc --- /dev/null +++ b/purchase_stock_vendor_bill_breakdown/__init__.py @@ -0,0 +1 @@ +from . import models diff --git a/purchase_stock_vendor_bill_breakdown/__manifest__.py b/purchase_stock_vendor_bill_breakdown/__manifest__.py new file mode 100644 index 00000000000..adf224cb492 --- /dev/null +++ b/purchase_stock_vendor_bill_breakdown/__manifest__.py @@ -0,0 +1,12 @@ +{ + "name": "Purchase Stock Vendor Bill Product Breakdown", + "summary": "Purchase Stock Vendor Bill Product Breakdown", + "author": "Ooops, Cetmix, Odoo Community Association (OCA)", + "version": "14.0.1.0.0", + "category": "Purchase Management", + "website": "https://github.com/OCA/purchase-workflow", + "depends": ["purchase_vendor_bill_breakdown", "stock"], + "maintainers": ["geomer198", "CetmixGitDrone"], + "license": "AGPL-3", + "installable": True, +} diff --git a/purchase_stock_vendor_bill_breakdown/models/__init__.py b/purchase_stock_vendor_bill_breakdown/models/__init__.py new file mode 100644 index 00000000000..fa6c0e40fde --- /dev/null +++ b/purchase_stock_vendor_bill_breakdown/models/__init__.py @@ -0,0 +1 @@ +from . import purchase_order_line diff --git a/purchase_stock_vendor_bill_breakdown/models/purchase_order_line.py b/purchase_stock_vendor_bill_breakdown/models/purchase_order_line.py new file mode 100644 index 00000000000..397af7e6750 --- /dev/null +++ b/purchase_stock_vendor_bill_breakdown/models/purchase_order_line.py @@ -0,0 +1,12 @@ +from odoo import api, models + + +class PurchaseOrderLine(models.Model): + _inherit = "purchase.order.line" + + @api.depends("move_ids.state", "move_ids.product_uom_qty", "move_ids.product_uom") + def _compute_qty_received(self): + super(PurchaseOrderLine, self)._compute_qty_received() + for line in self.filtered(lambda l: l.qty_received and l._has_components()): + # update product components qty + line.component_ids._update_qty(line.qty_received - line.last_qty_invoiced) diff --git a/purchase_stock_vendor_bill_breakdown/readme/CONTRIBUTORS.rst b/purchase_stock_vendor_bill_breakdown/readme/CONTRIBUTORS.rst new file mode 100644 index 00000000000..e3a1f2ff2f0 --- /dev/null +++ b/purchase_stock_vendor_bill_breakdown/readme/CONTRIBUTORS.rst @@ -0,0 +1,2 @@ +* Ooops404 +* Cetmix diff --git a/purchase_stock_vendor_bill_breakdown/readme/DESCRIPTION.rst b/purchase_stock_vendor_bill_breakdown/readme/DESCRIPTION.rst new file mode 100644 index 00000000000..567f1841115 --- /dev/null +++ b/purchase_stock_vendor_bill_breakdown/readme/DESCRIPTION.rst @@ -0,0 +1 @@ +This module allows to use module ‘purchase_vendor_bill_breakdown’ along with the module 'stock'. diff --git a/purchase_stock_vendor_bill_breakdown/tests/__init__.py b/purchase_stock_vendor_bill_breakdown/tests/__init__.py new file mode 100644 index 00000000000..745855671cb --- /dev/null +++ b/purchase_stock_vendor_bill_breakdown/tests/__init__.py @@ -0,0 +1 @@ +from . import test_stock_purchase_order_line diff --git a/purchase_stock_vendor_bill_breakdown/tests/test_stock_purchase_order_line.py b/purchase_stock_vendor_bill_breakdown/tests/test_stock_purchase_order_line.py new file mode 100644 index 00000000000..e44830e13ac --- /dev/null +++ b/purchase_stock_vendor_bill_breakdown/tests/test_stock_purchase_order_line.py @@ -0,0 +1,145 @@ +from odoo.tests import Form, tagged + +from odoo.addons.purchase_vendor_bill_breakdown.tests.common import ( + PurchaseTransactionCase, +) + + +@tagged("post_install", "-at_install", "test_stock_purchase_order_line") +class TestStockPurchaseOrderLine(PurchaseTransactionCase): + def test_stock_purchase_order_line_with_bill_components(self): + StockBackorderConfirmation = self.env["stock.backorder.confirmation"] + order = self.purchase_order_test_1 + with Form(order) as form, form.order_line.new() as line: + line.product_id = self.product_product_test_1 + line.product_qty = 10 + order.button_confirm() + self.assertTrue( + order.use_product_components, + msg="Purchase Order Vendor Bill Breakdown must be True", + ) + self.assertEqual( + order.order_line.qty_received, + 0, + msg="Received Qty must be equal to 0", + ) + + components = order.order_line.mapped("component_ids") + self.assertEqual(len(components), 2, msg="Components count must be equal to 2") + component_1, component_2 = order.order_line.mapped("component_ids") + self.assertEqual(component_1.total_qty, 0, msg="Total Qty must be equal to 0") + self.assertEqual(component_2.total_qty, 0, msg="Total Qty must be equal to 0") + + picking = order.picking_ids.sorted("id") + # Progress 6.0 out of the 10.0 ordered qty + picking.move_lines.quantity_done = 6 + result_dict = picking.button_validate() + # Create backorder + StockBackorderConfirmation.with_context(**result_dict["context"]).process() + + self.assertEqual( + order.order_line.qty_received, + 6, + msg="Received Qty must be equal to 6", + ) + + # Get Product Components + self.assertEqual( + component_1.total_qty, 30.0, msg="Total Qty must be equal to 30.0" + ) + self.assertEqual( + component_2.total_qty, 18.0, msg="Total Qty must be equal to 18.0" + ) + order.action_create_invoice() + self.assertEqual( + len(order.invoice_ids), + 1, + msg="Invoice Count must be equal to 1", + ) + line = order.order_line + self.assertEqual(line.qty_invoiced, 6, msg="Qty Invoiced must be equal to 6") + inv_component_1, inv_component_2 = line.invoice_lines + self.assertEqual( + round(inv_component_1.quantity, 2), 30, msg="Qty must be equal to 30" + ) + self.assertEqual( + round(inv_component_2.quantity, 2), 18, msg="Qty must be equal to 18" + ) + + *_, picking = order.picking_ids.sorted("id") + # Progress 4.0 out of the 4.0 ordered qty + picking.move_lines.quantity_done = 4.0 + result_dict = picking.button_validate() + self.assertTrue(result_dict, msg="Result must be True") + + self.assertEqual( + order.order_line.qty_received, + 10, + msg="Received Qty must be equal to 10", + ) + + order.order_line._compute_qty_received() + component_1, component_2 = order.order_line.mapped("component_ids") + self.assertEqual( + component_1.total_qty, 50.0, msg="Total Qty must be equal to 50.0" + ) + self.assertEqual( + component_2.total_qty, 30.0, msg="Total Qty must be equal to 30.0" + ) + order.action_create_invoice() + + self.assertEqual( + len(order.invoice_ids), + 2, + msg="Invoice Count must be equal to 2", + ) + line = order.order_line + self.assertEqual(line.qty_invoiced, 10, msg="Qty Invoiced must be equal to 10") + ( + *_, + invoice_line_component_1, + invoice_line_component_2, + ) = line.invoice_lines.sorted("id") + self.assertEqual( + round(invoice_line_component_1.quantity, 2), + 20, + msg="Qty must be equal to 20", + ) + self.assertEqual( + round(invoice_line_component_2.quantity, 2), + 12, + msg="Qty must be equal to 12", + ) + + def test_stock_purchase_order_line_without_bill_components(self): + order = self.purchase_order_test_2 + with Form(order) as form, form.order_line.new() as line: + line.product_id = self.product_product_test_1 + line.product_qty = 10 + order.button_confirm() + self.assertEqual( + order.order_line.qty_received, + 0, + msg="Received Qty must be equal to 0", + ) + + components = order.order_line.mapped("component_ids") + self.assertEqual(len(components), 0, msg="Components count must be equal to 0") + picking = order.picking_ids + # Progress 10.0 out of the 10.0 ordered qty + picking.move_lines.quantity_done = 10.0 + result_dict = picking.button_validate() + order.action_create_invoice() + self.assertTrue(result_dict, msg="Result must be True") + self.assertEqual( + order.order_line.qty_received, + 10, + msg="Received Qty must be equal to 0", + ) + invoice_line_product = order.order_line.invoice_lines.sorted("id") + self.assertEqual( + len(invoice_line_product), 1, msg="Invoice Qty must be equal to 1" + ) + self.assertEqual( + round(invoice_line_product.quantity, 2), 10, msg="Qty must be equal to 10" + ) diff --git a/setup/purchase_stock_vendor_bill_breakdown/odoo/addons/purchase_stock_vendor_bill_breakdown b/setup/purchase_stock_vendor_bill_breakdown/odoo/addons/purchase_stock_vendor_bill_breakdown new file mode 120000 index 00000000000..cf8d8f8e2a7 --- /dev/null +++ b/setup/purchase_stock_vendor_bill_breakdown/odoo/addons/purchase_stock_vendor_bill_breakdown @@ -0,0 +1 @@ +../../../../purchase_stock_vendor_bill_breakdown \ No newline at end of file diff --git a/setup/purchase_stock_vendor_bill_breakdown/setup.py b/setup/purchase_stock_vendor_bill_breakdown/setup.py new file mode 100644 index 00000000000..28c57bb6403 --- /dev/null +++ b/setup/purchase_stock_vendor_bill_breakdown/setup.py @@ -0,0 +1,6 @@ +import setuptools + +setuptools.setup( + setup_requires=['setuptools-odoo'], + odoo_addon=True, +)