# Copyright 2018 Tecnativa - Vicent Cubells
# Copyright 2018 Tecnativa - Pedro M. Baeza
# Copyright 2024 Tecnativa - Carolina Fernandez
# License AGPL-3 - See http://www.gnu.org/licenses/agpl-3.0.html

from datetime import datetime

from odoo import fields
from odoo.exceptions import UserError
from odoo.tests import common, tagged


@tagged("post_install", "-at_install")
class TestPurchaseLandedCost(common.TransactionCase):
    @classmethod
    def setUpClass(cls):
        super(TestPurchaseLandedCost, cls).setUpClass()
        if not cls.env.company.chart_template_id:
            # Load a CoA if there's none in current company
            coa = cls.env.ref("l10n_generic_coa.configurable_chart_template", False)
            if not coa:
                # Load the first available CoA
                coa = cls.env["account.chart.template"].search(
                    [("visible", "=", True)], limit=1
                )
            coa.try_loading(company=cls.env.company, install_demo=False)
        expense_type_obj = cls.env["purchase.expense.type"]
        cls.type_amount = expense_type_obj.create(
            {
                "name": "Type Amount",
                "calculation_method": "amount",
                "default_amount": True,
            }
        )
        cls.type_price = expense_type_obj.create(
            {"name": "Type Price", "calculation_method": "price"}
        )
        cls.type_qty = expense_type_obj.create(
            {"name": "Type Qty", "calculation_method": "qty"}
        )
        cls.type_weight = expense_type_obj.create(
            {"name": "Type Weight", "calculation_method": "weight"}
        )
        cls.type_volume = expense_type_obj.create(
            {"name": "Type Volume", "calculation_method": "volume"}
        )
        cls.type_equal = expense_type_obj.create(
            {"name": "Type Equal", "calculation_method": "equal"}
        )
        cls.distribution = cls.env["purchase.cost.distribution"].create({"name": "/"})
        cls.product_category = cls.env["product.category"].create(
            {
                "name": "Landed Cost",
                "property_valuation": "manual_periodic",
                "property_cost_method": "average",
            }
        )
        cls.product = cls.env["product.product"].create(
            {"name": "Product", "type": "product", "categ_id": cls.product_category.id}
        )
        cls.supplier = cls.env["res.partner"].create({"name": "Supplier"})
        cls.purchase_order = cls.env["purchase.order"].create(
            {
                "partner_id": cls.supplier.id,
                "order_line": [
                    (
                        0,
                        0,
                        {
                            "product_id": cls.product.id,
                            "product_qty": 5.0,
                            "name": cls.product.name,
                            "product_uom": cls.product.uom_id.id,
                            "price_unit": 3.0,
                            "date_planned": fields.Date.today(),
                        },
                    )
                ],
            }
        )
        cls.purchase_order.button_confirm()
        cls.picking = cls.purchase_order.picking_ids
        cls.env["stock.immediate.transfer"].create(
            {"pick_ids": [(4, cls.picking.id)]}
        ).process()
        cls.picking.action_confirm()
        cls.picking.move_ids.write({"quantity_done": 5.0})
        cls.picking.button_validate()
        account = cls.env["account.account"].create(
            {"name": "Account", "code": "CODE", "account_type": "expense"}
        )
        cls.invoice = cls.env["account.move"].create(
            {
                "partner_id": cls.supplier.id,
                "move_type": "in_invoice",
                "invoice_date": datetime.now(),
                "invoice_line_ids": [
                    (
                        0,
                        0,
                        {
                            "name": "Test service",
                            "account_id": account.id,
                            "price_unit": 10.0,
                        },
                    )
                ],
            }
        )
        cls.invoice._post()
        wiz = (
            cls.env["import.invoice.line.wizard"]
            .with_context(active_id=cls.distribution.id)
            .create(
                {
                    "supplier": cls.supplier.id,
                    "invoice": cls.invoice.id,
                    "invoice_line": cls.invoice.invoice_line_ids[:1].id,
                    "expense_type": cls.type_qty.id,
                }
            )
        )
        wiz.action_import_invoice_line()

    def test_distribution_without_lines(self):
        self.assertNotEqual(self.distribution.name, "/")
        with self.assertRaises(UserError):
            self.distribution.action_calculate()
        self.assertEqual(self.distribution.state, "draft")

    def test_distribution_import_shipment(self):
        self.assertEqual(self.picking.state, "done")
        wiz = (
            self.env["picking.import.wizard"]
            .with_context(active_id=self.distribution.id)
            .create(
                {"supplier": self.supplier.id, "pickings": [(6, 0, self.picking.ids)]}
            )
        )
        wiz.action_import_picking()
        self.assertEqual(len(self.distribution.cost_lines.ids), 1)
        self.assertAlmostEqual(self.distribution.total_uom_qty, 5.0)
        self.assertAlmostEqual(self.distribution.total_purchase, 15.0)
        self.assertAlmostEqual(self.distribution.amount_total, 25.0)
        # Expense part
        self.assertEqual(len(self.distribution.expense_lines.ids), 1)
        self.assertAlmostEqual(self.distribution.total_uom_qty, 5.0)
        self.distribution.action_calculate()
        self.assertAlmostEqual(self.distribution.cost_lines[0].cost_ratio, 2)
        self.assertAlmostEqual(self.distribution.total_expense, 10.0)
        self.assertEqual(self.distribution.state, "calculated")
        self.distribution.action_done()
        self.assertEqual(self.distribution.state, "done")

    def test_distribution_two_moves(self):
        order2 = self.purchase_order.copy()
        order2.order_line.price_unit = 2
        order2.button_confirm()
        picking2 = order2.picking_ids
        self.env["stock.immediate.transfer"].create(
            {"pick_ids": [(4, picking2.id)]}
        ).process()
        picking2.button_validate()
        wiz = (
            self.env["picking.import.wizard"]
            .with_context(active_id=self.distribution.id)
            .create(
                {
                    "supplier": self.supplier.id,
                    "pickings": [(6, 0, (self.picking + picking2).ids)],
                }
            )
        )
        wiz.action_import_picking()
        self.assertEqual(len(self.distribution.cost_lines.ids), 2)
        self.assertAlmostEqual(self.distribution.total_uom_qty, 10.0)
        self.assertAlmostEqual(self.distribution.total_purchase, 25.0)
        self.assertAlmostEqual(self.distribution.amount_total, 35.0)
        self.distribution.action_calculate()
        self.assertAlmostEqual(self.distribution.cost_lines[0].cost_ratio, 1)
        self.assertAlmostEqual(self.distribution.total_expense, 10.0)
        self.assertEqual(self.distribution.state, "calculated")

    def test_distribution_two_moves_existing_stock(self):
        order2 = self.purchase_order.copy()
        order2.order_line.price_unit = 2
        order2.button_confirm()
        picking2 = order2.picking_ids
        order3 = self.purchase_order.copy()
        order3.order_line.price_unit = 1
        order3.button_confirm()
        picking3 = order3.picking_ids
        self.env["stock.immediate.transfer"].create(
            {"pick_ids": [(6, 0, (picking2 + picking3).ids)]}
        ).process()
        (picking2 + picking3).button_validate()
        wiz = (
            self.env["picking.import.wizard"]
            .with_context(active_id=self.distribution.id)
            .create(
                {
                    "supplier": self.supplier.id,
                    "pickings": [(6, 0, (picking2 + picking3).ids)],
                }
            )
        )
        wiz.action_import_picking()
        self.assertAlmostEqual(self.distribution.total_uom_qty, 10.0)
        self.assertAlmostEqual(self.distribution.total_purchase, 15.0)
        self.assertAlmostEqual(self.distribution.amount_total, 25.0)
        self.distribution.action_calculate()
        self.assertAlmostEqual(self.distribution.cost_lines[0].cost_ratio, 1)
        self.assertAlmostEqual(self.distribution.total_expense, 10.0)
        self.assertEqual(self.distribution.state, "calculated")
