# Copyright 2023 Ecosoft Co., Ltd. (http://ecosoft.co.th)
# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl.html).

from odoo import Command, fields
from odoo.exceptions import UserError, ValidationError
from odoo.tests import Form, TransactionCase


class TestAccountManualCurrency(TransactionCase):
    @classmethod
    def setUpClass(cls):
        super().setUpClass()
        cls.inv_model = cls.env["account.move"]
        cls.payment_model = cls.env["account.payment"]
        cls.payment_register_model = cls.env["account.payment.register"]
        cls.register_view_id = "account.view_account_payment_register_form"
        # Activate Multi Currency
        cls.usd_currency = cls.env.ref("base.USD")
        cls.eur_currency = cls.env.ref("base.EUR")
        cls.eur_currency.active = True

        cls.partner1 = cls.env.ref("base.res_partner_3")

        cls.cash = cls.env["account.journal"].create(
            {"name": "Cash Test", "type": "cash", "code": "CT"}
        )

    def _create_invoice(
        self,
        partner,
        invoice_type,
        currency,
        manual_currency=False,
        manual_currency_rate=0.0,
        type_currency="company_rate",
    ):
        inv_line = [
            Command.create(
                {
                    "product_id": self.env.ref("product.product_product_8").id,
                    "name": "Test Invoice Line",
                    "quantity": 1.0,
                    "price_unit": 100.0,
                    "tax_ids": False,
                },
            )
        ]
        invoice = self.inv_model.create(
            {
                "partner_id": partner.id,
                "invoice_date": fields.Date.today(),
                "move_type": invoice_type,
                "currency_id": currency.id,
                "manual_currency": manual_currency,
                "type_currency": type_currency,
                "manual_currency_rate": manual_currency_rate,
                "invoice_line_ids": inv_line,
            }
        )
        return invoice

    def test_01_account_move_inverse_currency(self):
        # Create invoice with custom rate: 10 USD = 1 EUR
        invoice1 = self._create_invoice(
            self.partner1,
            "in_invoice",
            self.eur_currency,
            True,
            10,
            "inverse_company_rate",
        )
        invoice1.get_view()
        self.assertTrue(invoice1.is_manual)
        # Currency will recompute to default
        self.assertTrue(invoice1.manual_currency)
        self.assertAlmostEqual(invoice1.manual_currency_rate, 10)
        self.assertAlmostEqual(invoice1.invoice_currency_rate, 0.1)  # Inverse rate
        invoice1.action_refresh_currency()
        self.assertNotEqual(invoice1.manual_currency_rate, 10)
        total_currency_not_manual = invoice1.total_company_currency
        self.assertAlmostEqual(
            total_currency_not_manual,
            sum(
                invoice1.line_ids.filtered(lambda line: line.balance > 0).mapped(
                    "balance"
                )
            ),
        )
        # Use manual currency
        with Form(invoice1) as inv:
            inv.manual_currency = True
            inv.type_currency = "inverse_company_rate"
            inv.manual_currency_rate = 20  # Convert rate: 20 USD = 1 EUR
        inv.save()
        # After manual currency, total currency will recompute
        self.assertNotEqual(invoice1.total_company_currency, total_currency_not_manual)
        self.assertAlmostEqual(
            invoice1.total_company_currency,
            sum(
                invoice1.line_ids.filtered(lambda line: line.balance > 0).mapped(
                    "balance"
                )
            ),
        )
        invoice1.action_post()
        self.assertEqual(invoice1.state, "posted")
        # Can't refresh rate when state is not draft
        with self.assertRaises(ValidationError):
            invoice1.action_refresh_currency()

        # Create invoice
        invoice2 = self._create_invoice(self.partner1, "in_invoice", self.usd_currency)
        invoice2.action_post()
        self.assertEqual(invoice2.state, "posted")

        # Register Payment
        # Can't register payment with not same manual currency
        ctx = {
            "active_ids": (invoice1 + invoice2).mapped("line_ids").ids,
            "active_model": "account.move.line",
        }
        with self.assertRaises(UserError):
            Form(
                self.payment_register_model.with_context(**ctx),
                view=self.register_view_id,
            ).save()
        ctx = {
            "active_ids": [invoice1.id],
            "active_model": "account.move",
        }
        with Form(
            self.payment_register_model.with_context(**ctx),
            view=self.register_view_id,
        ) as f:
            f.amount = invoice1.amount_total
        wiz = f.save()
        wiz.action_create_payments()

    def test_02_manual_currency_rate_on_inv_payment(self):
        # Create invoice with custom rate: 10 EUR = 1 USD
        invoice1 = self._create_invoice(
            self.partner1,
            "in_invoice",
            self.eur_currency,
            True,
            10,
            "company_rate",
        )
        invoice1.action_post()
        self.assertTrue(invoice1.manual_currency)
        self.assertAlmostEqual(invoice1.manual_currency_rate, 10.0)
        self.assertAlmostEqual(invoice1.invoice_currency_rate, 10.0)  # Inverse rate
        self.assertAlmostEqual(invoice1.invoice_line_ids.balance, 10.0)
        # Register with manual currency
        # Convert rate: 20 EUR = 1 USD
        ctx = {
            "active_ids": [invoice1.id],
            "active_model": "account.move",
        }
        with Form(
            self.payment_register_model.with_context(**ctx),
            view=self.register_view_id,
        ) as f:
            f.amount = invoice1.amount_total
            f.manual_currency = True
            f.type_currency = "company_rate"
            f.manual_currency_rate = 20
        wiz = f.save()
        action_payment = wiz.action_create_payments()
        payment = self.payment_model.browse(action_payment["res_id"])
        move_payment = payment.move_id
        self.assertAlmostEqual(move_payment.manual_currency_rate, 20)
        self.assertAlmostEqual(move_payment.invoice_currency_rate, 20)
        self.assertAlmostEqual(abs(move_payment.line_ids[0].balance), 5)
