# Part of Odoo. See LICENSE file for full copyright and licensing details.

""" Implementation of "INVENTORY VALUATION TESTS (With valuation layers)" spreadsheet. """

from unittest import skip

from odoo import fields
from odoo.tests import Form, tagged
from odoo.addons.stock_landed_costs.tests.common import TestStockLandedCostsCommon
from freezegun import freeze_time
import time


class TestStockValuationLCCommon(TestStockLandedCostsCommon):

    @classmethod
    def setUpClass(cls):
        super().setUpClass()

        cls.product1 = cls.env['product.product'].create({
            'name': 'product1',
            'is_storable': True,
            'categ_id': cls.stock_account_product_categ.id,
        })
        cls.productlc1 = cls.env['product.product'].create({
            'name': 'product1',
            'type': 'service',
            'categ_id': cls.stock_account_product_categ.id,
            'landed_cost_ok': True,
        })

    def setUp(self):
        super().setUp()
        self.days = 0

    def _get_stock_input_move_lines(self):
        return self.env['account.move.line'].search([
            ('account_id', '=', self.company_data['default_account_stock_in'].id),
        ], order='id')

    def _get_stock_output_move_lines(self):
        return self.env['account.move.line'].search([
            ('account_id', '=', self.company_data['default_account_stock_out'].id),
        ], order='id')

    def _get_stock_valuation_move_lines(self):
        return self.env['account.move.line'].search([
            ('account_id', '=', self.company_data['default_account_stock_valuation'].id),
        ], order='id')

    def _get_payable_move_lines(self):
        return self.env['account.move.line'].search([
            ('account_id', '=', self.company_data['default_account_payable'].id),
        ], order='id')

    def _get_expense_move_lines(self):
        return self.env['account.move.line'].search([
            ('account_id', '=', self.company_data['default_account_expense'].id),
        ], order='id')

    def _make_lc(self, move, amount):
        picking = move.picking_id
        lc = Form(self.env['stock.landed.cost'])
        lc.account_journal_id = self.stock_journal
        lc.picking_ids.add(move.picking_id)
        with lc.cost_lines.new() as cost_line:
            cost_line.product_id = self.productlc1
            cost_line.price_unit = amount
        lc = lc.save()
        lc.compute_landed_cost()
        lc.button_validate()
        return lc

    def _make_in_move(self, product, quantity, unit_cost=None, create_picking=False, product_uom=False):
        """ Helper to create and validate a receipt move.
        """
        unit_cost = unit_cost or product.standard_price
        in_move = self.env['stock.move'].create({
            'product_id': product.id,
            'location_id': self.env.ref('stock.stock_location_suppliers').id,
            'location_dest_id': self.company_data['default_warehouse'].lot_stock_id.id,
            'product_uom': product_uom.id if product_uom else self.env.ref('uom.product_uom_unit').id,
            'product_uom_qty': quantity,
            'price_unit': unit_cost,
            'picking_type_id': self.company_data['default_warehouse'].in_type_id.id,
        })

        if create_picking:
            picking = self.env['stock.picking'].create({
                'picking_type_id': in_move.picking_type_id.id,
                'location_id': in_move.location_id.id,
                'location_dest_id': in_move.location_dest_id.id,
            })
            in_move.write({'picking_id': picking.id})

        in_move._action_confirm()
        in_move._action_assign()
        in_move.move_line_ids.quantity = quantity
        in_move.picked = True
        in_move._action_done()

        self.days += 1
        return in_move.with_context(svl=True)

    def _make_out_move(self, product, quantity, force_assign=None, create_picking=False):
        """ Helper to create and validate a delivery move.
        """
        out_move = self.env['stock.move'].create({
            'product_id': product.id,
            'location_id': self.company_data['default_warehouse'].lot_stock_id.id,
            'location_dest_id': self.env.ref('stock.stock_location_customers').id,
            'product_uom': self.env.ref('uom.product_uom_unit').id,
            'product_uom_qty': quantity,
            'picking_type_id': self.company_data['default_warehouse'].out_type_id.id,
        })

        if create_picking:
            picking = self.env['stock.picking'].create({
                'picking_type_id': out_move.picking_type_id.id,
                'location_id': out_move.location_id.id,
                'location_dest_id': out_move.location_dest_id.id,
            })
            out_move.write({'picking_id': picking.id})

        out_move._action_confirm()
        out_move._action_assign()
        if force_assign:
            self.env['stock.move.line'].create({
                'move_id': out_move.id,
                'product_id': out_move.product_id.id,
                'product_uom_id': out_move.product_uom.id,
                'location_id': out_move.location_id.id,
                'location_dest_id': out_move.location_dest_id.id,
            })
        out_move.move_line_ids.quantity = quantity
        out_move.picked = True
        out_move._action_done()

        self.days += 1
        return out_move.with_context(svl=True)


@tagged('-at_install', 'post_install')
@skip('Temporary to fast merge new valuation')
class TestStockValuationLCFIFO(TestStockValuationLCCommon):
    @classmethod
    def setUpClass(cls):
        super().setUpClass()
        cls.product1.product_tmpl_id.categ_id.property_cost_method = 'fifo'
        cls.product1.product_tmpl_id.categ_id.property_valuation = 'real_time'

    def test_normal_1(self):
        move1 = self._make_in_move(self.product1, 10, unit_cost=10, create_picking=True)
        move2 = self._make_in_move(self.product1, 10, unit_cost=20)
        lc = self._make_lc(move1, 100)
        move3 = self._make_out_move(self.product1, 1)

        self.assertEqual(self.product1.value_svl, 380)
        self.assertEqual(self.product1.quantity_svl, 19)
        self.assertEqual(self.product1.standard_price, 20)

    def test_negative_1(self):
        self.product1.standard_price = 10
        move1 = self._make_out_move(self.product1, 2, force_assign=True)
        move2 = self._make_in_move(self.product1, 10, unit_cost=15, create_picking=True)
        lc = self._make_lc(move2, 100)

        self.assertEqual(self.product1.value_svl, 200)
        self.assertEqual(self.product1.quantity_svl, 8)

    def test_alreadyout_1(self):
        move1 = self._make_in_move(self.product1, 10, unit_cost=10, create_picking=True)
        move2 = self._make_out_move(self.product1, 10)
        lc = self._make_lc(move1, 100)

        self.assertEqual(self.product1.value_svl, 0)
        self.assertEqual(self.product1.quantity_svl, 0)

    def test_alreadyout_2(self):
        move1 = self._make_in_move(self.product1, 10, unit_cost=10, create_picking=True)
        move2 = self._make_in_move(self.product1, 10, unit_cost=20)
        move2 = self._make_out_move(self.product1, 1)
        lc = self._make_lc(move1, 100)

        self.assertEqual(self.product1.value_svl, 380)
        self.assertEqual(self.product1.quantity_svl, 19)

    def test_alreadyout_3(self):
        move1 = self._make_in_move(self.product1, 10, unit_cost=10, create_picking=True)
        move2 = self._make_out_move(self.product1, 10)
        move1.move_line_ids.quantity = 15
        lc = self._make_lc(move1, 60)

        self.assertEqual(self.product1.value_svl, 70)
        self.assertEqual(self.product1.quantity_svl, 5)

    def test_fifo_to_standard_1(self):
        move1 = self._make_in_move(self.product1, 10, unit_cost=10)
        move2 = self._make_in_move(self.product1, 10, unit_cost=15)
        move3 = self._make_out_move(self.product1, 5)
        lc = self._make_lc(move1, 100)
        self.product1.product_tmpl_id.categ_id.property_cost_method = 'standard'
        out_svl = self.product1.stock_valuation_layer_ids.sorted()[-2]
        in_svl = self.product1.stock_valuation_layer_ids.sorted()[-1]

        self.assertEqual(out_svl.value, -250)
        # 15 * 16.66
        self.assertAlmostEqual(in_svl.value, 250.0, 2)

    def test_rounding_1(self):
        """3@100, out 1, out 1, out 1"""
        move1 = self._make_in_move(self.product1, 3, unit_cost=20, create_picking=True)
        lc = self._make_lc(move1, 40)
        move2 = self._make_out_move(self.product1, 1)
        move3 = self._make_out_move(self.product1, 1)
        move4 = self._make_out_move(self.product1, 1)

        self.assertEqual(self.product1.stock_valuation_layer_ids.mapped('value'), [60.0, 40.0, -33.33, -33.34, -33.33])
        self.assertEqual(self.product1.value_svl, 0)
        self.assertEqual(self.product1.quantity_svl, 0)

    def test_rounding_2(self):
        """3@98, out 1, out 1, out 1"""
        move1 = self._make_in_move(self.product1, 3, unit_cost=20, create_picking=True)
        lc = self._make_lc(move1, 38)
        move2 = self._make_out_move(self.product1, 1)
        move3 = self._make_out_move(self.product1, 1)
        move4 = self._make_out_move(self.product1, 1)

        self.assertEqual(move2.stock_valuation_layer_ids.value, -32.67)
        self.assertEqual(move3.stock_valuation_layer_ids.value, -32.67)
        self.assertAlmostEqual(move4.stock_valuation_layer_ids.value, -32.66, delta=0.01)  # self.env.company.currency_id.round(-32.66) -> -32.660000000000004
        self.assertEqual(self.product1.value_svl, 0)
        self.assertEqual(self.product1.quantity_svl, 0)

    def test_rounding_3(self):
        """3@4.85, out 1, out 1, out 1"""
        move1 = self._make_in_move(self.product1, 3, unit_cost=1, create_picking=True)
        lc = self._make_lc(move1, 1.85)
        move2 = self._make_out_move(self.product1, 1)
        move3 = self._make_out_move(self.product1, 1)
        move4 = self._make_out_move(self.product1, 1)

        self.assertEqual(self.product1.stock_valuation_layer_ids.mapped('value'), [3.0, 1.85, -1.62, -1.62, -1.61])
        self.assertEqual(self.product1.value_svl, 0)
        self.assertEqual(self.product1.quantity_svl, 0)

    def test_in_and_out_1(self):
        move1 = self._make_in_move(self.product1, 10, unit_cost=100, create_picking=True)
        self.assertEqual(move1.stock_valuation_layer_ids[0].remaining_value, 1000)
        lc1 = self._make_lc(move1, 100)
        self.assertEqual(move1.stock_valuation_layer_ids[0].remaining_value, 1100)
        lc2 = self._make_lc(move1, 50)
        self.assertEqual(move1.stock_valuation_layer_ids[0].remaining_value, 1150)
        self.assertEqual(self.product1.value_svl, 1150)
        self.assertEqual(self.product1.quantity_svl, 10)
        move2 = self._make_out_move(self.product1, 1)
        self.assertEqual(move2.stock_valuation_layer_ids.value, -115)

    def test_landed_cost_different_uom(self):
        """
        Check that the SVL is correctly updated with the landed cost divided by the quantity in the product UOM.
        """
        uom_gram = self.env.ref('uom.product_uom_gram')
        uom_kgm = self.env.ref('uom.product_uom_kgm')
        # the product uom is in gram but the transfer is in kg
        self.product1.uom_id = uom_gram
        move1 = self._make_in_move(self.product1, 1, unit_cost=10, create_picking=True, product_uom=uom_kgm)
        self.assertEqual(move1.stock_valuation_layer_ids[0].remaining_value, 10000)
        self.assertEqual(move1.stock_valuation_layer_ids[0].remaining_qty, 1000)
        self._make_lc(move1, 250)
        self.assertEqual(move1.stock_valuation_layer_ids[0].remaining_value, 10250)


@tagged('-at_install', 'post_install')
@skip('Temporary to fast merge new valuation')
class TestStockValuationLCAVCO(TestStockValuationLCCommon):
    @classmethod
    def setUpClass(cls):
        super().setUpClass()
        cls.product1.product_tmpl_id.categ_id.property_cost_method = 'average'
        cls.product1.product_tmpl_id.categ_id.property_valuation = 'real_time'

    def test_normal_1(self):
        move1 = self._make_in_move(self.product1, 10, unit_cost=10, create_picking=True)
        move2 = self._make_in_move(self.product1, 10, unit_cost=20)
        lc = self._make_lc(move1, 100)
        move3 = self._make_out_move(self.product1, 1)

        self.assertEqual(self.product1.value_svl, 380)

    def test_negative_1(self):
        self.product1.standard_price = 10
        move1 = self._make_out_move(self.product1, 2, force_assign=True)
        move2 = self._make_in_move(self.product1, 10, unit_cost=15, create_picking=True)
        lc = self._make_lc(move2, 100)

        self.assertEqual(self.product1.value_svl, 200)
        self.assertEqual(self.product1.quantity_svl, 8)

    def test_alreadyout_1(self):
        move1 = self._make_in_move(self.product1, 10, unit_cost=10, create_picking=True)
        move2 = self._make_out_move(self.product1, 10)
        lc = self._make_lc(move1, 100)

        self.assertEqual(len(self.product1.stock_valuation_layer_ids), 2)
        self.assertEqual(self.product1.value_svl, 0)
        self.assertEqual(self.product1.quantity_svl, 0)

    def test_alreadyout_2(self):
        move1 = self._make_in_move(self.product1, 10, unit_cost=10, create_picking=True)
        move2 = self._make_in_move(self.product1, 10, unit_cost=20)
        move2 = self._make_out_move(self.product1, 1)
        lc = self._make_lc(move1, 100)

        self.assertEqual(self.product1.value_svl, 375)
        self.assertEqual(self.product1.quantity_svl, 19)

    def test_lc_generated_from_bill_multi_comapnies(self):
        """
        In a multi-company environment:
        Confirm PO, receive products, post bill and generate LC
        """
        company = self.env.company
        self.env.user.company_id = self.env['res.company'].create({
            'name': 'Another Company',
        })

        po_form = Form(self.env['purchase.order'])
        po_form.company_id = company
        po_form.partner_id = self.partner_a
        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.tax_ids.clear()
        po = po_form.save()
        po.button_confirm()

        receipt = po.picking_ids
        receipt.move_line_ids.quantity = 1
        receipt.button_validate()

        bill_form = Form.from_action(self.env, po.action_create_invoice())
        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
            inv_line.is_landed_costs_line = True
        bill = bill_form.save()
        bill.action_post()

        lc_form = Form.from_action(self.env, bill.button_create_landed_costs())
        lc_form.picking_ids.add(receipt)
        lc = lc_form.save()
        lc.button_validate()

        product = self.product1.with_company(company)
        self.assertEqual(product.value_svl, 15)
        self.assertEqual(product.quantity_svl, 1)
        self.assertEqual(product.standard_price, 15)

@tagged('-at_install', 'post_install')
@skip('Temporary to fast merge new valuation')
class TestStockValuationLCFIFOVB(TestStockValuationLCCommon):
    @classmethod
    def setUpClass(cls):
        super(TestStockValuationLCFIFOVB, cls).setUpClass()
        cls.vendor1 = cls.env['res.partner'].create({'name': 'vendor1'})
        cls.vendor1.property_account_payable_id = cls.company_data['default_account_payable']
        cls.vendor2 = cls.env['res.partner'].create({'name': 'vendor2'})
        cls.vendor2.property_account_payable_id = cls.company_data['default_account_payable']
        cls.product1.product_tmpl_id.categ_id.property_cost_method = 'fifo'
        cls.product1.product_tmpl_id.categ_id.property_valuation = 'real_time'

    def test_vendor_bill_flow_anglo_saxon_1(self):
        """In anglo saxon accounting, receive 10@10 and invoice. Then invoice 1@50 as a landed costs
        and create a linked landed costs record.
        """
        self.env.company.anglo_saxon_accounting = True

        # Create an RFQ for self.product1, 10@10
        rfq = Form(self.env['purchase.order'])
        rfq.partner_id = self.vendor1

        with rfq.order_line.new() as po_line:
            po_line.product_id = self.product1
            po_line.product_qty = 10
            po_line.price_unit = 10
            po_line.tax_ids.clear()

        rfq = rfq.save()
        rfq.button_confirm()

        # Process the receipt
        receipt = rfq.picking_ids
        receipt.button_validate()
        self.assertEqual(rfq.order_line.qty_received, 10)

        input_aml = self._get_stock_input_move_lines()[-1]
        self.assertEqual(input_aml.debit, 0)
        self.assertEqual(input_aml.credit, 100)
        valuation_aml = self._get_stock_valuation_move_lines()[-1]
        self.assertEqual(valuation_aml.debit, 100)
        self.assertEqual(valuation_aml.credit, 0)

        # Create a vendor bill for the RFQ
        action = rfq.action_create_invoice()
        vb = self.env['account.move'].browse(action['res_id'])
        vb.invoice_date = vb.date
        vb.action_post()

        input_aml = self._get_stock_input_move_lines()[-1]
        self.assertEqual(input_aml.debit, 100)
        self.assertEqual(input_aml.credit, 0)
        payable_aml = self._get_payable_move_lines()[-1]
        self.assertEqual(payable_aml.debit, 0)
        self.assertEqual(payable_aml.credit, 100)

        # Create a vendor bill for a landed cost product, post it and validate a landed cost
        # linked to this vendor bill. LC; 1@50
        lcvb = Form(self.env['account.move'].with_context(default_move_type='in_invoice'))
        lcvb.invoice_date = lcvb.date
        lcvb.partner_id = self.vendor2
        with lcvb.invoice_line_ids.new() as inv_line:
            inv_line.product_id = self.productlc1
            inv_line.price_unit = 50
            inv_line.is_landed_costs_line = True
        with lcvb.invoice_line_ids.edit(0) as inv_line:
            inv_line.tax_ids.clear()
        lcvb = lcvb.save()
        lcvb.action_post()

        input_aml = self._get_stock_input_move_lines()[-1]
        self.assertEqual(input_aml.debit, 50)
        self.assertEqual(input_aml.credit, 0)
        payable_aml = self._get_payable_move_lines()[-1]
        self.assertEqual(payable_aml.debit, 0)
        self.assertEqual(payable_aml.credit, 50)

        lc = Form.from_action(self.env, lcvb.button_create_landed_costs())
        lc.picking_ids.add(receipt)
        lc = lc.save()
        lc.button_validate()

        self.assertEqual(lc.cost_lines.price_unit, 50)
        self.assertEqual(lc.cost_lines.product_id, self.productlc1)

        input_aml = self._get_stock_input_move_lines()[-1]
        self.assertEqual(input_aml.debit, 0)
        self.assertEqual(input_aml.credit, 50)
        valuation_aml = self._get_stock_valuation_move_lines()[-1]
        self.assertEqual(valuation_aml.debit, 50)
        self.assertEqual(valuation_aml.credit, 0)

        # Check reconciliation of input aml of lc
        lc_input_aml = lc.account_move_id.line_ids.filtered(lambda aml: aml.account_id == self.company_data['default_account_stock_in'])
        self.assertTrue(len(lc_input_aml.full_reconcile_id), 1)

        self.assertEqual(self.product1.quantity_svl, 10)
        self.assertEqual(self.product1.value_svl, 150)

    def test_vendor_bill_flow_anglo_saxon_2(self):
        """In anglo saxon accounting, receive 10@10 and invoice with the addition of 1@50 as a
        landed costs and create a linked landed costs record.
        """
        self.env.company.anglo_saxon_accounting = True

        # Create an RFQ for self.product1, 10@10
        rfq = Form(self.env['purchase.order'])
        rfq.partner_id = self.vendor1

        with rfq.order_line.new() as po_line:
            po_line.product_id = self.product1
            po_line.product_qty = 10
            po_line.price_unit = 10
            po_line.tax_ids.clear()

        rfq = rfq.save()
        rfq.button_confirm()

        # Process the receipt
        receipt = rfq.picking_ids
        receipt.button_validate()
        self.assertEqual(rfq.order_line.qty_received, 10)

        input_aml = self._get_stock_input_move_lines()[-1]
        self.assertEqual(input_aml.debit, 0)
        self.assertEqual(input_aml.credit, 100)
        valuation_aml = self._get_stock_valuation_move_lines()[-1]
        self.assertEqual(valuation_aml.debit, 100)
        self.assertEqual(valuation_aml.credit, 0)

        # Create a vendor bill for the RFQ and add to it the landed cost
        vb = Form(self.env['account.move'].with_context(default_move_type='in_invoice'))
        vb.partner_id = self.vendor1
        vb.invoice_date = vb.date
        self.productlc1.landed_cost_ok = True
        with vb.invoice_line_ids.new() as inv_line:
            inv_line.product_id = self.productlc1
            inv_line.price_unit = 50
            inv_line.is_landed_costs_line = True
        vb = vb.save()
        vb.action_post()

        lc = Form.from_action(self.env, vb.button_create_landed_costs())
        lc.picking_ids.add(receipt)
        lc = lc.save()
        lc.button_validate()

        # Check reconciliation of input aml of lc
        lc_input_aml = lc.account_move_id.line_ids.filtered(lambda aml: aml.account_id == self.company_data['default_account_stock_in'])
        self.assertTrue(len(lc_input_aml.full_reconcile_id), 1)

    def test_vendor_bill_flow_continental_1(self):
        """In continental accounting, receive 10@10 and invoice. Then invoice 1@50 as a landed costs
        and create a linked landed costs record.
        """
        self.env.company.anglo_saxon_accounting = False

        # Create an RFQ for self.product1, 10@10
        rfq = Form(self.env['purchase.order'])
        rfq.partner_id = self.vendor1

        with rfq.order_line.new() as po_line:
            po_line.product_id = self.product1
            po_line.product_qty = 10
            po_line.price_unit = 10
            po_line.tax_ids.clear()

        rfq = rfq.save()
        rfq.button_confirm()

        # Process the receipt
        receipt = rfq.picking_ids
        receipt.button_validate()
        self.assertEqual(rfq.order_line.qty_received, 10)

        input_aml = self._get_stock_input_move_lines()[-1]
        self.assertEqual(input_aml.debit, 0)
        self.assertEqual(input_aml.credit, 100)
        valuation_aml = self._get_stock_valuation_move_lines()[-1]
        self.assertEqual(valuation_aml.debit, 100)
        self.assertEqual(valuation_aml.credit, 0)

        # Create a vebdor bill for the RFQ
        action = rfq.action_create_invoice()
        vb = self.env['account.move'].browse(action['res_id'])
        vb.invoice_date = vb.date
        vb.action_post()

        expense_aml = self._get_expense_move_lines()[-1]
        self.assertEqual(expense_aml.debit, 100)
        self.assertEqual(expense_aml.credit, 0)

        payable_aml = self._get_payable_move_lines()[-1]
        self.assertEqual(payable_aml.debit, 0)
        self.assertEqual(payable_aml.credit, 100)

        # Create a vendor bill for a landed cost product, post it and validate a landed cost
        # linked to this vendor bill. LC; 1@50
        lcvb = Form(self.env['account.move'].with_context(default_move_type='in_invoice'))
        lcvb.partner_id = self.vendor2
        lcvb.invoice_date = lcvb.date
        with lcvb.invoice_line_ids.new() as inv_line:
            inv_line.product_id = self.productlc1
            inv_line.price_unit = 50
            inv_line.is_landed_costs_line = True
        with lcvb.invoice_line_ids.edit(0) as inv_line:
            inv_line.tax_ids.clear()
        lcvb = lcvb.save()
        lcvb.action_post()

        expense_aml = self._get_expense_move_lines()[-1]
        self.assertEqual(expense_aml.debit, 50)
        self.assertEqual(expense_aml.credit, 0)
        payable_aml = self._get_payable_move_lines()[-1]
        self.assertEqual(payable_aml.debit, 0)
        self.assertEqual(payable_aml.credit, 50)

        lc = Form.from_action(self.env, lcvb.button_create_landed_costs())
        lc.picking_ids.add(receipt)
        lc = lc.save()
        lc.button_validate()

        self.assertEqual(lc.cost_lines.price_unit, 50)
        self.assertEqual(lc.cost_lines.product_id, self.productlc1)

        input_aml = self._get_stock_input_move_lines()[-1]
        self.assertEqual(input_aml.debit, 0)
        self.assertEqual(input_aml.credit, 50)
        valuation_aml = self._get_stock_valuation_move_lines()[-1]
        self.assertEqual(valuation_aml.debit, 50)
        self.assertEqual(valuation_aml.credit, 0)

        self.assertEqual(self.product1.quantity_svl, 10)
        self.assertEqual(self.product1.value_svl, 150)

    def test_create_landed_cost_from_bill_multi_currencies(self):
        # create a vendor bill in EUR where base currency in USD
        company = self.env.user.company_id
        currency_grp = self.env.ref('base.group_multi_currency')
        self.env.user.write({'group_ids': [(4, currency_grp.id)]})
        usd_currency = self.env.ref('base.USD')
        eur_currency = self.env.ref('base.EUR')
        eur_currency.active = True

        company.currency_id = usd_currency

        invoice_date = '2023-01-01'
        accounting_date = '2024-01-31'

        self.cr.execute("UPDATE res_company SET currency_id = %s WHERE id = %s", (usd_currency.id, company.id))
        self.env['res.currency.rate'].search([]).unlink()
        self.env['res.currency.rate'].create({
            'name': invoice_date,
            'rate': 1.0,
            'currency_id': usd_currency.id,
            'company_id': company.id,
        })

        self.env['res.currency.rate'].create({
            'name': invoice_date,
            'rate': 0.5,
            'currency_id': eur_currency.id,
            'company_id': company.id,
        })

        self.env['res.currency.rate'].create({
            'name': accounting_date,
            'rate': 0.25,
            'currency_id': eur_currency.id,
            'company_id': company.id,
        })

        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 = 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 = invoice_date
        bill_form.date = accounting_date
        bill_form.currency_id = eur_currency

        with bill_form.invoice_line_ids.new() as inv_line:
            inv_line.product_id = self.productlc1
            inv_line.price_unit = 5
            inv_line.currency_id = eur_currency

        bill = bill_form.save()
        bill.action_post()

        lc_form = Form.from_action(self.env, bill.button_create_landed_costs())
        lc_form.picking_ids.add(receipt)
        lc = lc_form.save()
        lc.button_validate()

        self.assertEqual(lc.cost_lines.price_unit, 10)


@tagged('-at_install', 'post_install')
@skip('Temporary to fast merge new valuation')
class TestAccountInvoicingWithCOA(TestStockValuationLCCommon):
    def setUp(self):
        self.usd = self.env.ref('base.USD')
        self.eur = self.env.ref('base.EUR')
        self.env.company.currency_id = self.usd
        self.env['res.currency.rate'].search([]).unlink()

    def create_rate(self, inv_rate):
        return self.env['res.currency.rate'].create({
            'name': time.strftime('%Y-%m-%d'),
            'inverse_company_rate': inv_rate,
            'currency_id': self.eur.id,
            'company_id': self.env.company.id,
        })

    def _bill(self, po, qty=None, price=None):
        action = po.action_create_invoice()
        bill = self.env["account.move"].browse(action["res_id"])
        bill.invoice_date = fields.Date.today()
        if qty is not None:
            bill.invoice_line_ids.quantity = qty
        if price is not None:
            bill.invoice_line_ids.price_unit = price
        bill.action_post()
        return bill

    def _return(self, picking, qty):
        wizard_form = Form(self.env['stock.return.picking'].with_context(active_ids=picking.ids, active_id=picking.id, active_model='stock.picking'))
        wizard = wizard_form.save()
        wizard.product_return_moves.quantity = qty
        return_picking = wizard._create_return()
        return_picking.move_ids.quantity = qty
        return_picking.button_validate()
        return return_picking

    def _purchase_receipt(self, product, qty, price, curr):
        po_form = Form(self.env['purchase.order'])
        po_form.partner_id = self.env['res.partner'].browse(self.supplier_id)
        po_form.currency_id = curr
        with po_form.order_line.new() as po_line:
            po_line.product_id = product
            po_line.product_qty = qty
            po_line.price_unit = price
        po = po_form.save()
        po.button_confirm()

        receipt = po.picking_ids
        receipt.move_ids.quantity = qty
        receipt.button_validate()

        return po, receipt

    def test_fifo_return_twice_and_bill_with_landed_cost_and_multi_currency(self):
        """This check ensure that the landed cost does not prevent '_generate_price_difference_vals' to compute
        the correct 'quantity already out' when handling a Return of a Return of a Receipt.
        An inccorect value of 'quantity already out' would generate COGS lines in the vendor bill.
        """
        self.product1.categ_id.property_cost_method = 'fifo'
        self.product1.categ_id.property_valuation = 'real_time'
        self.eur.active = True

        with freeze_time('2025-01-01'):
            self.create_rate(1.0)
            po1, _ = self._purchase_receipt(self.product1, 5, 10, self.eur)
            self._bill(po1)

        with freeze_time('2025-01-02'):
            self.create_rate(1.5)
            po2, receipt02 = self._purchase_receipt(self.product1, 10, 10, self.eur)
            self._make_lc(receipt02.move_ids, 10)
            receipt_return = self._return(receipt02, 10)
            self._return(receipt_return, 10)

        with freeze_time('2025-01-03'):
            self.create_rate(2.0)
            bill2 = self._bill(po2)

        in_acc_id = self.company_data['default_account_stock_in'].id
        tax_acc_id = self.company_data['default_account_tax_purchase'].id
        payable_acc_id = self.company_data['default_account_payable'].id
        self.assertRecordValues(bill2.line_ids, [
            {'account_id': in_acc_id, 'balance': 200.0, 'amount_currency': 100},
            {'account_id': tax_acc_id, 'balance': 30.0, 'amount_currency': 15},
            {'account_id': payable_acc_id, 'balance': -230.0, 'amount_currency': -115},
        ])
