diff --git a/.tx/config b/.tx/config index ea2491f247e..c8d50c1b6e3 100644 --- a/.tx/config +++ b/.tx/config @@ -2098,6 +2098,15 @@ resource_name = stock_landed_costs replace_edited_strings = false keep_translations = false +[o:odoo:p:odoo-17:r:stock_landed_costs_company] +file_filter = addons/stock_landed_costs_company/i18n/.po +source_file = addons/stock_landed_costs_company/i18n/stock_landed_costs_company.pot +type = PO +minimum_perc = 0 +resource_name = stock_landed_costs_company +replace_edited_strings = false +keep_translations = false + [o:odoo:p:odoo-17:r:stock_picking_batch] file_filter = addons/stock_picking_batch/i18n/.po source_file = addons/stock_picking_batch/i18n/stock_picking_batch.pot diff --git a/addons/stock_landed_costs_company/__init__.py b/addons/stock_landed_costs_company/__init__.py new file mode 100644 index 00000000000..ef61079ec47 --- /dev/null +++ b/addons/stock_landed_costs_company/__init__.py @@ -0,0 +1,12 @@ +# -*- coding: utf-8 -*- +# Part of Odoo. See LICENSE file for full copyright and licensing details. + +from . import models + +def _stock_landed_costs_company_post_init(env): + env.cr.execute(""" + UPDATE stock_landed_cost cost + SET company_id = journal.company_id + FROM account_journal journal + WHERE cost.account_journal_id = journal.id + """) diff --git a/addons/stock_landed_costs_company/__manifest__.py b/addons/stock_landed_costs_company/__manifest__.py new file mode 100644 index 00000000000..1adf7ead9ae --- /dev/null +++ b/addons/stock_landed_costs_company/__manifest__.py @@ -0,0 +1,18 @@ +# -*- coding: utf-8 -*- +# Part of Odoo. See LICENSE file for full copyright and licensing details. + +{ + 'name': 'Landed Costs for company\'s branches', + 'version': '1.0', + 'description': """ + This module is a patch that stores the company_id field on the landed cost model. + That way, it is possible to create/use landed costs from a branch. + """, + 'depends': ['stock_landed_costs'], + 'category': 'Inventory/Inventory', + 'sequence': 16, + 'installable': True, + 'auto_install': True, + 'post_init_hook': '_stock_landed_costs_company_post_init', + 'license': 'LGPL-3', +} diff --git a/addons/stock_landed_costs_company/i18n/stock_landed_costs_company.pot b/addons/stock_landed_costs_company/i18n/stock_landed_costs_company.pot new file mode 100644 index 00000000000..6efa3b1c8cb --- /dev/null +++ b/addons/stock_landed_costs_company/i18n/stock_landed_costs_company.pot @@ -0,0 +1,26 @@ +# Translation of Odoo Server. +# This file contains the translation of the following modules: +# * stock_landed_costs_company +# +msgid "" +msgstr "" +"Project-Id-Version: Odoo Server 17.0+e\n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2024-01-25 07:53+0000\n" +"PO-Revision-Date: 2024-01-25 07:53+0000\n" +"Last-Translator: \n" +"Language-Team: \n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: \n" +"Plural-Forms: \n" + +#. module: stock_landed_costs_company +#: model:ir.model.fields,field_description:stock_landed_costs_company.field_stock_landed_cost__company_id +msgid "Company" +msgstr "" + +#. module: stock_landed_costs_company +#: model:ir.model,name:stock_landed_costs_company.model_stock_landed_cost +msgid "Stock Landed Cost" +msgstr "" diff --git a/addons/stock_landed_costs_company/models/__init__.py b/addons/stock_landed_costs_company/models/__init__.py new file mode 100644 index 00000000000..cc408347617 --- /dev/null +++ b/addons/stock_landed_costs_company/models/__init__.py @@ -0,0 +1,4 @@ +# -*- coding: utf-8 -*- +# Part of Odoo. See LICENSE file for full copyright and licensing details. + +from . import stock_landed_cost diff --git a/addons/stock_landed_costs_company/models/stock_landed_cost.py b/addons/stock_landed_costs_company/models/stock_landed_cost.py new file mode 100644 index 00000000000..bf438a8c148 --- /dev/null +++ b/addons/stock_landed_costs_company/models/stock_landed_cost.py @@ -0,0 +1,10 @@ +# -*- coding: utf-8 -*- +# Part of Odoo. See LICENSE file for full copyright and licensing details. + +from odoo import fields, models + + +class StockLandedCost(models.Model): + _inherit = 'stock.landed.cost' + + company_id = fields.Many2one('res.company', 'Company', required=True, related=False, default=lambda self: self.env.company) diff --git a/addons/stock_landed_costs_company/tests/__init__.py b/addons/stock_landed_costs_company/tests/__init__.py new file mode 100644 index 00000000000..a7377466cf9 --- /dev/null +++ b/addons/stock_landed_costs_company/tests/__init__.py @@ -0,0 +1,3 @@ +# -*- coding: utf-8 -*- + +from . import test_stock_landed_costs_branches diff --git a/addons/stock_landed_costs_company/tests/test_stock_landed_costs_branches.py b/addons/stock_landed_costs_company/tests/test_stock_landed_costs_branches.py new file mode 100644 index 00000000000..42b07e0cfc4 --- /dev/null +++ b/addons/stock_landed_costs_company/tests/test_stock_landed_costs_branches.py @@ -0,0 +1,103 @@ +# -*- coding: utf-8 -*- +# Part of Odoo. See LICENSE file for full copyright and licensing details. + +from odoo.addons.stock_landed_costs.tests.test_stockvaluationlayer import TestStockValuationLCCommon +from odoo.tests import tagged, Form + + +@tagged('post_install', '-at_install') +class TestStockLandedCostsBranches(TestStockValuationLCCommon): + + @classmethod + def setUpClass(cls, chart_template_ref=None): + super().setUpClass(chart_template_ref=chart_template_ref) + + cls.company = cls.env.company + cls.branch = cls.env['res.company'].create({ + 'name': 'Branch', + 'parent_id': cls.company.id, + }) + cls.env['account.chart.template'].try_loading(cls.company.chart_template, company=cls.branch, install_demo=False) + cls.env.user.company_id = cls.branch + + cls.vendor1 = cls.env['res.partner'].create({'name': 'vendor1'}) + + cls.product1.categ_id.property_cost_method = 'fifo' + + def test_create_lc_from_branch(self): + """ + From a company's branch, create a LC and ensure it impacts the SVL + """ + warehouse = self.env['stock.warehouse'].search([('company_id', '=', self.branch.id)], limit=1) + supplier_location = self.env.ref('stock.stock_location_suppliers') + + receipt = self.env['stock.picking'].create({ + 'location_id': supplier_location.id, + 'location_dest_id': warehouse.lot_stock_id.id, + 'picking_type_id': warehouse.in_type_id.id, + 'move_ids': [(0, 0, { + 'name': self.product1.name, + 'location_id': supplier_location.id, + 'location_dest_id': warehouse.lot_stock_id.id, + 'picking_type_id': warehouse.in_type_id.id, + 'product_id': self.product1.id, + 'product_uom_qty': 1, + 'product_uom': self.product1.uom_id.id, + 'price_unit': 10, + })], + }) + receipt.action_confirm() + receipt.action_assign() + receipt.move_line_ids.quantity = 1 + receipt.button_validate() + + lc_form = Form(self.env['stock.landed.cost']) + lc_form.picking_ids.add(receipt) + with lc_form.cost_lines.new() as cost_line: + cost_line.product_id = self.productlc1 + cost_line.price_unit = 5 + lc = lc_form.save() + lc.compute_landed_cost() + lc.button_validate() + + self.assertEqual(self.product1.value_svl, 15) + self.assertEqual(self.product1.quantity_svl, 1) + self.assertEqual(self.product1.standard_price, 15) + + def test_lc_generated_from_bill(self): + """ + Confirm PO, receive products, post bill and generate LC + """ + po_form = Form(self.env['purchase.order']) + po_form.partner_id = self.vendor1 + with po_form.order_line.new() as po_line: + po_line.product_id = self.product1 + po_line.product_qty = 1 + po_line.price_unit = 10 + po_line.taxes_id.clear() + po = po_form.save() + po.button_confirm() + + receipt = po.picking_ids + receipt.move_line_ids.quantity = 1 + receipt.button_validate() + + action = po.action_create_invoice() + bill = self.env['account.move'].browse(action['res_id']) + bill_form = Form(bill) + bill_form.invoice_date = bill_form.date + with bill_form.invoice_line_ids.new() as inv_line: + inv_line.product_id = self.productlc1 + inv_line.price_unit = 5 + bill = bill_form.save() + bill.action_post() + + action = bill.button_create_landed_costs() + lc_form = Form(self.env[action['res_model']].browse(action['res_id'])) + lc_form.picking_ids.add(receipt) + lc = lc_form.save() + lc.button_validate() + + self.assertEqual(self.product1.value_svl, 15) + self.assertEqual(self.product1.quantity_svl, 1) + self.assertEqual(self.product1.standard_price, 15)