# Copyright 2020 Tecnativa - Ernesto Tejeda
# Copyright 2022 Tecnativa - Víctor Martínez
# Copyright 2023 Tecnativa - Pedro M. Baeza
# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl).

from odoo.exceptions import ValidationError
from odoo.tests import Form, tagged
from odoo.tests.common import users

from odoo.addons.base.tests.common import BaseCommon


class TestRmaSaleBase(BaseCommon):
    @classmethod
    def setUpClass(cls):
        super().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)
        cls.res_partner = cls.env["res.partner"]
        cls.product_product = cls.env["product.product"]
        cls.so_model = cls.env["sale.order"]

        cls.product_1 = cls.product_product.create(
            {"name": "Product test 1", "type": "product"}
        )
        cls.product_2 = cls.product_product.create(
            {"name": "Product test 2", "type": "product"}
        )
        cls.partner = cls.res_partner.create(
            {"name": "Partner test", "email": "partner@rma"}
        )
        cls.report_model = cls.env["ir.actions.report"]
        cls.rma_operation_model = cls.env["rma.operation"]
        cls.operation = cls.env.ref("rma.rma_operation_replace")
        cls._partner_portal_wizard(cls, cls.partner)
        cls.wh = cls.env.ref("stock.warehouse0")
        cls.env["stock.quant"]._update_available_quantity(
            cls.product_1, cls.wh.lot_stock_id, 20
        )
        cls.env["stock.quant"]._update_available_quantity(
            cls.product_2, cls.wh.lot_stock_id, 20
        )

    def _create_sale_order(self, products):
        order_form = Form(self.so_model)
        order_form.partner_id = self.partner
        for product_info in products:
            with order_form.order_line.new() as line_form:
                line_form.product_id = product_info[0]
                line_form.product_uom_qty = product_info[1]
        return order_form.save()

    def _partner_portal_wizard(self, partner):
        wizard_all = (
            self.env["portal.wizard"]
            .with_context(**{"active_ids": [partner.id]})
            .create({})
        )
        wizard_all.user_ids.action_grant_access()

    def _rma_sale_wizard(self, order):
        wizard_id = order.action_create_rma()["res_id"]
        wizard = self.env["sale.order.rma.wizard"].browse(wizard_id)
        wizard.operation_id = self.operation
        return wizard


@tagged("-at_install", "post_install")
class TestRmaSale(TestRmaSaleBase):
    @classmethod
    def setUpClass(cls):
        super().setUpClass()
        cls.sale_order = cls._create_sale_order(cls, [[cls.product_1, 5]])
        cls.sale_order.action_confirm()
        # Maybe other modules create additional lines in the create
        # method in sale.order model, so let's find the correct line.
        cls.order_line = cls.sale_order.order_line.filtered(
            lambda r: r.product_id == cls.product_1
        )
        cls.order_out_picking = cls.sale_order.picking_ids
        cls.order_out_picking.move_ids.quantity_done = 5
        cls.order_out_picking.button_validate()

    def test_rma_sale_computes_onchange(self):
        rma = self.env["rma"].new()
        # No m2m values when everything is selectable
        self.assertFalse(rma.allowed_picking_ids)
        self.assertFalse(rma.allowed_move_ids)
        self.assertFalse(rma.allowed_product_ids)
        # Partner selected
        rma.order_id = self.sale_order
        rma.partner_id = self.partner
        self.assertFalse(rma.order_id)
        self.assertEqual(rma.allowed_picking_ids._origin, self.order_out_picking)
        # Order selected
        rma.order_id = self.sale_order
        self.assertEqual(rma.allowed_picking_ids._origin, self.order_out_picking)
        rma.picking_id = self.order_out_picking
        self.assertEqual(rma.allowed_move_ids._origin, self.order_out_picking.move_ids)
        self.assertEqual(rma.allowed_product_ids._origin, self.product_1)
        # Onchanges
        rma.product_id = self.product_1
        rma._onchange_order_id()
        self.assertFalse(rma.product_id)
        self.assertFalse(rma.picking_id)

    def test_create_rma_with_so(self):
        rma_vals = {
            "partner_id": self.partner.id,
            "order_id": self.sale_order.id,
            "product_id": self.product_1.id,
            "product_uom_qty": 5,
            "location_id": self.sale_order.warehouse_id.rma_loc_id.id,
            "operation_id": self.operation.id,
        }
        rma = self.env["rma"].create(rma_vals)
        rma.action_confirm()
        self.assertTrue(rma.reception_move_id)
        self.assertFalse(rma.reception_move_id.origin_returned_move_id)
        # Receive the product
        rma.reception_move_id.quantity_done = rma.product_uom_qty
        rma.reception_move_id.picking_id._action_done()
        # Now do a replacement for testing the issue of a new SO line created for P2
        delivery_form = Form(
            self.env["rma.delivery.wizard"].with_context(
                active_ids=rma.ids, rma_delivery_type="replace"
            )
        )
        delivery_form.product_id = self.product_2
        delivery_form.product_uom_qty = 5
        delivery_wizard = delivery_form.save()
        delivery_wizard.action_deliver()
        rma.delivery_move_ids.quantity_done = rma.product_uom_qty
        rma.delivery_move_ids.picking_id._action_done()
        self.assertEqual(len(self.sale_order.order_line), 1)

    def test_create_rma_from_so(self):
        order = self.sale_order
        wizard = self._rma_sale_wizard(order)
        rma = self.env["rma"].browse(wizard.create_and_open_rma()["res_id"])
        self.assertEqual(rma.partner_id, order.partner_id)
        self.assertEqual(rma.order_id, order)
        self.assertEqual(rma.picking_id, self.order_out_picking)
        self.assertEqual(rma.move_id, self.order_out_picking.move_ids)
        self.assertEqual(rma.product_id, self.product_1)
        self.assertEqual(rma.product_uom_qty, self.order_line.product_uom_qty)
        self.assertEqual(rma.product_uom, self.order_line.product_uom)
        self.assertEqual(rma.state, "confirmed")
        self.assertEqual(
            rma.reception_move_id.origin_returned_move_id,
            self.order_out_picking.move_ids,
        )
        self.assertEqual(
            rma.reception_move_id.picking_id + self.order_out_picking,
            order.picking_ids,
        )
        user = self.env["res.users"].create(
            {"login": "test_refund_with_so", "name": "Test"}
        )
        order.user_id = user.id
        # Receive the RMA
        rma.action_confirm()
        rma.reception_move_id.quantity_done = rma.product_uom_qty
        rma.reception_move_id.picking_id._action_done()
        # Refund the RMA
        rma.action_refund()
        self.assertEqual(self.order_line.qty_delivered, 0)
        self.assertEqual(self.order_line.qty_invoiced, -5)
        self.assertEqual(rma.refund_id.user_id, user)
        self.assertEqual(rma.refund_id.invoice_line_ids.sale_line_ids, self.order_line)
        # Cancel the refund
        rma.refund_id.button_cancel()
        self.assertEqual(self.order_line.qty_delivered, 5)
        self.assertEqual(self.order_line.qty_invoiced, 0)
        # And put it to draft again
        rma.refund_id.button_draft()
        self.assertEqual(self.order_line.qty_delivered, 0)
        self.assertEqual(self.order_line.qty_invoiced, -5)

    @users("partner@rma")
    def test_create_rma_from_so_portal_user(self):
        order = self.sale_order
        wizard_obj = (
            self.env["sale.order.rma.wizard"].sudo().with_context(active_id=order.id)
        )
        operation = self.rma_operation_model.sudo().search([], limit=1)
        line_vals = [
            (
                0,
                0,
                {
                    "product_id": order.order_line.product_id.id,
                    "sale_line_id": order.order_line.id,
                    "quantity": order.order_line.product_uom_qty,
                    "allowed_quantity": order.order_line.qty_delivered,
                    "uom_id": order.order_line.product_uom.id,
                    "picking_id": order.picking_ids[0].id,
                    "operation_id": operation.id,
                },
            )
        ]
        wizard = wizard_obj.create(
            {
                "line_ids": line_vals,
                "location_id": order.warehouse_id.rma_loc_id.id,
            }
        )
        rma = wizard.sudo().create_rma(from_portal=True)
        self.assertEqual(rma.order_id, order)
        self.assertIn(order.partner_id, rma.message_partner_ids)
        self.assertEqual(order.rma_count, 1)

    def test_create_recurrent_rma(self):
        """An RMA of a product that had an RMA in the past should be possible"""
        wizard = self._rma_sale_wizard(self.sale_order)
        rma = self.env["rma"].browse(wizard.create_and_open_rma()["res_id"])
        rma.reception_move_id.quantity_done = rma.product_uom_qty
        rma.reception_move_id.picking_id._action_done()
        wizard = self._rma_sale_wizard(self.sale_order)
        self.assertEqual(
            wizard.line_ids.quantity,
            0,
            "There shouldn't be any allowed quantities for RMAs",
        )
        delivery_form = Form(
            self.env["rma.delivery.wizard"].with_context(
                active_ids=rma.ids,
                rma_delivery_type="return",
            )
        )
        delivery_form.product_uom_qty = rma.product_uom_qty
        delivery_wizard = delivery_form.save()
        delivery_wizard.action_deliver()
        picking = rma.delivery_move_ids.picking_id
        picking.move_ids.quantity_done = rma.product_uom_qty
        picking._action_done()
        # The product is returned to the customer, so we should be able to make
        # another RMA in the future
        wizard = self._rma_sale_wizard(self.sale_order)
        self.assertEqual(
            wizard.line_ids.quantity,
            rma.product_uom_qty,
            "We should be allowed to return the product again",
        )

    def test_report_rma(self):
        wizard = self._rma_sale_wizard(self.sale_order)
        rma = self.env["rma"].browse(wizard.create_and_open_rma()["res_id"])
        operation = self.rma_operation_model.sudo().search([], limit=1)
        rma.operation_id = operation.id
        res = self.env["ir.actions.report"]._render_qweb_html("rma.report_rma", rma.ids)
        res = str(res[0])
        self.assertRegex(res, self.sale_order.name)
        self.assertRegex(res, operation.name)

    def test_manual_refund_no_quantity_impact(self):
        """If the operation is meant for a manual refund, the delivered quantity
        should not be updated."""
        self.operation.action_create_refund = "manual_after_receipt"
        order = self.sale_order
        order_line = order.order_line
        self.assertEqual(order_line.qty_delivered, 5)
        wizard = self._rma_sale_wizard(order)
        rma = self.env["rma"].browse(wizard.create_and_open_rma()["res_id"])
        self.assertFalse(rma.reception_move_id.sale_line_id)
        rma.action_confirm()
        rma.reception_move_id.quantity_done = rma.product_uom_qty
        rma.reception_move_id.picking_id._action_done()
        self.assertEqual(order.order_line.qty_delivered, 5)

    def test_no_manual_refund_quantity_impact(self):
        """If the operation is meant for a manual refund, the delivered quantity
        should not be updated."""
        self.operation.action_create_refund = "update_quantity"
        order = self.sale_order
        order_line = order.order_line
        self.assertEqual(order_line.qty_delivered, 5)
        wizard = self._rma_sale_wizard(order)
        rma = self.env["rma"].browse(wizard.create_and_open_rma()["res_id"])
        self.assertEqual(rma.reception_move_id.sale_line_id, order_line)
        self.assertFalse(rma.can_be_refunded)
        rma.reception_move_id.quantity_done = rma.product_uom_qty
        rma.reception_move_id.picking_id._action_done()
        self.assertEqual(order.order_line.qty_delivered, 0)
        delivery_form = Form(
            self.env["rma.delivery.wizard"].with_context(
                active_ids=rma.ids,
                rma_delivery_type="return",
            )
        )
        delivery_form.product_uom_qty = rma.product_uom_qty
        delivery_wizard = delivery_form.save()
        delivery_wizard.action_deliver()
        picking = rma.delivery_move_ids.picking_id
        picking.move_ids.quantity_done = rma.product_uom_qty
        picking._action_done()
        self.assertEqual(order.order_line.qty_delivered, 5)

    def test_return_different_product(self):
        self.operation.action_create_delivery = False
        self.operation.different_return_product = True
        self.operation.action_create_refund = "update_quantity"
        order = self.sale_order
        order_line = order.order_line
        self.assertEqual(order_line.qty_delivered, 5)
        wizard = self._rma_sale_wizard(order)
        with self.assertRaises(
            ValidationError, msg="Complete the replacement information"
        ):
            rma = self.env["rma"].browse(wizard.create_and_open_rma()["res_id"])
        return_product = self.product_product.create(
            {"name": "return Product test 1", "type": "product"}
        )
        wizard.line_ids.return_product_id = return_product
        rma = self.env["rma"].browse(wizard.create_and_open_rma()["res_id"])
        self.assertEqual(rma.reception_move_id.sale_line_id, order_line)
        self.assertEqual(rma.reception_move_id.product_id, return_product)
        self.assertFalse(rma.can_be_refunded)
        rma.reception_move_id.quantity_done = rma.product_uom_qty
        rma.reception_move_id.picking_id._action_done()
        self.assertEqual(order.order_line.qty_delivered, 5)

    def test_grouping_reception(self):
        sale_order = self._create_sale_order([[self.product_1, 5], [self.product_2, 3]])
        sale_order.action_confirm()
        sale_order.picking_ids.action_set_quantities_to_reservation()
        sale_order.picking_ids.button_validate()
        wizard = self._rma_sale_wizard(sale_order)
        rmas = self.env["rma"].search(wizard.create_and_open_rma()["domain"])
        self.assertEqual(len(rmas.reception_move_id.group_id), 1)
        self.assertEqual(len(rmas.reception_move_id.picking_id), 1)
