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

from datetime import datetime

import odoo.tests.common as common


class TestAccountNetting(common.SavepointCase):
    @classmethod
    def setUpClass(cls):
        super(TestAccountNetting, cls).setUpClass()
        res_users_account_manager = cls.env.ref("account.group_account_manager")
        partner_manager = cls.env.ref("base.group_partner_manager")
        cls.env.user.write(
            {"groups_id": [(6, 0, [res_users_account_manager.id, partner_manager.id])]}
        )
        company = cls.env.ref("base.main_company")
        # only adviser can create an account
        cls.account_receivable = cls.env["account.account"].create(
            {
                "code": "cust_acc",
                "name": "customer account",
                "user_type_id": cls.env.ref("account.data_account_type_receivable").id,
                "reconcile": True,
                "company_id": company.id,
            }
        )
        cls.account_payable = cls.env["account.account"].create(
            {
                "code": "supp_acc",
                "name": "supplier account",
                "user_type_id": cls.env.ref("account.data_account_type_payable").id,
                "reconcile": True,
                "company_id": company.id,
            }
        )
        cls.account_revenue = cls.env["account.account"].search(
            [
                (
                    "user_type_id",
                    "=",
                    cls.env.ref("account.data_account_type_revenue").id,
                ),
                ("company_id", "=", company.id),
            ],
            limit=1,
        )
        cls.account_expense = cls.env["account.account"].search(
            [
                (
                    "user_type_id",
                    "=",
                    cls.env.ref("account.data_account_type_expenses").id,
                ),
                ("company_id", "=", company.id),
            ],
            limit=1,
        )
        cls.partner = cls.env["res.partner"].create(
            {
                "name": "Supplier/Customer",
                "property_account_receivable_id": cls.account_receivable.id,
                "property_account_payable_id": cls.account_payable.id,
            }
        )
        cls.partner1 = cls.env["res.partner"].create(
            {
                "name": "Supplier/Customer 1",
                "property_account_receivable_id": cls.account_receivable.id,
                "property_account_payable_id": cls.account_payable.id,
            }
        )
        cls.journal = cls.env["account.journal"].create(
            {
                "name": "Test sale journal",
                "type": "sale",
                "code": "TEST",
                "company_id": company.id,
            }
        )
        cls.expenses_journal = cls.env["account.journal"].create(
            {
                "name": "Test expense journal",
                "type": "purchase",
                "code": "EXP",
                "company_id": company.id,
            }
        )
        cls.miscellaneous_journal = cls.env["account.journal"].create(
            {
                "name": "Miscellaneus journal",
                "type": "general",
                "code": "OTHER",
                "company_id": company.id,
            }
        )
        cls.customer_invoice = cls.env["account.move"].create(
            {
                "journal_id": cls.journal.id,
                "move_type": "out_invoice",
                "partner_id": cls.partner.id,
                "company_id": company.id,
                "invoice_line_ids": [
                    (
                        0,
                        0,
                        {
                            "name": "Test",
                            "price_unit": 100.0,
                            "account_id": cls.account_revenue.id,
                        },
                    )
                ],
            }
        )
        cls.customer_invoice.action_post()
        customer_move = cls.customer_invoice
        cls.move_line_1 = customer_move.line_ids.filtered(
            lambda x: x.account_id == cls.account_receivable
        )
        cls.supplier_invoice = cls.env["account.move"].create(
            {
                "journal_id": cls.expenses_journal.id,
                "move_type": "in_invoice",
                "partner_id": cls.partner.id,
                "company_id": company.id,
                "invoice_date": datetime.now(),
                "invoice_line_ids": [
                    (
                        0,
                        0,
                        {
                            "name": "Test",
                            "price_unit": 1200.0,
                            "account_id": cls.account_expense.id,
                        },
                    )
                ],
            }
        )
        cls.supplier_invoice.action_post()
        supplier_move = cls.supplier_invoice
        cls.move_line_2 = supplier_move.line_ids.filtered(
            lambda x: x.account_id == cls.account_payable
        )
        cls.move_line_3 = supplier_move.line_ids.filtered(
            lambda x: x.account_id == cls.account_expense
        )
        cls.supplier_invoice = cls.env["account.move"].create(
            {
                "journal_id": cls.expenses_journal.id,
                "move_type": "in_invoice",
                "partner_id": cls.partner1.id,
                "company_id": company.id,
                "invoice_date": datetime.now(),
                "invoice_line_ids": [
                    (
                        0,
                        0,
                        {
                            "name": "Test",
                            "price_unit": 200.0,
                            "account_id": cls.account_expense.id,
                        },
                    )
                ],
            }
        )
        cls.supplier_invoice.action_post()
        supplier_move = cls.supplier_invoice
        cls.move_line_4 = supplier_move.line_ids.filtered(
            lambda x: x.account_id == cls.account_payable
        )
        cls.supplier_invoice = cls.env["account.move"].create(
            {
                "journal_id": cls.expenses_journal.id,
                "move_type": "in_refund",
                "partner_id": cls.partner1.id,
                "company_id": company.id,
                "invoice_date": datetime.now(),
                "invoice_line_ids": [
                    (
                        0,
                        0,
                        {
                            "name": "Test",
                            "price_unit": 200.0,
                            "account_id": cls.account_expense.id,
                        },
                    )
                ],
            }
        )
        cls.supplier_invoice.action_post()
        supplier_move = cls.supplier_invoice
        cls.move_line_5 = supplier_move.line_ids.filtered(
            lambda x: x.account_id == cls.account_payable
        )
        cls.supplier_invoice = cls.env["account.move"].create(
            {
                "journal_id": cls.expenses_journal.id,
                "move_type": "in_refund",
                "partner_id": cls.partner1.id,
                "company_id": company.id,
                "invoice_date": datetime.now(),
                "invoice_line_ids": [
                    (
                        0,
                        0,
                        {
                            "name": "Test",
                            "price_unit": 200.0,
                            "account_id": cls.account_expense.id,
                        },
                    )
                ],
            }
        )
        cls.supplier_invoice.action_post()
        supplier_move = cls.supplier_invoice
        cls.move_line_6 = supplier_move.line_ids.filtered(
            lambda x: x.account_id == cls.account_payable
        )

    def test_compensation(self):
        # Test exception line 33 from account_move_make_netting
        obj = self.env["account.move.make.netting"].with_context(
            active_ids=[self.move_line_1.id]
        )
        with self.assertRaises(Exception):
            wizard = obj.create(
                {
                    "move_line_ids": [(6, 0, [self.move_line_1.id])],
                    "journal_id": self.miscellaneous_journal.id,
                }
            )
        # Test exception line 39 from account_move_make_netting
        obj = self.env["account.move.make.netting"].with_context(
            active_ids=[self.move_line_1.id, self.move_line_3.id]
        )
        with self.assertRaises(Exception):
            wizard = obj.create(
                {
                    "move_line_ids": [
                        (6, 0, [self.move_line_1.id, self.move_line_3.id])
                    ],
                    "journal_id": self.miscellaneous_journal.id,
                }
            )
        # Test exception line 45 from account_move_make_netting
        obj = self.env["account.move.make.netting"].with_context(
            active_ids=[self.move_line_4.id, self.move_line_5.id]
        )
        with self.assertRaises(Exception):
            wizard = obj.create(
                {
                    "move_line_ids": [
                        (6, 0, [self.move_line_4.id, self.move_line_5.id])
                    ],
                    "journal_id": self.miscellaneous_journal.id,
                }
            )
        # Test exception line 42 from account_move_make_netting
        moves = self.env["account.move.line"].browse(
            [self.move_line_4.id, self.move_line_5.id]
        )
        moves.reconcile()
        obj = self.env["account.move.make.netting"].with_context(
            active_ids=[self.move_line_4.id, self.move_line_5.id]
        )
        with self.assertRaises(Exception):
            wizard = obj.create(
                {
                    "move_line_ids": [
                        (6, 0, [self.move_line_4.id, self.move_line_5.id])
                    ],
                    "journal_id": self.miscellaneous_journal.id,
                }
            )
        # Test exception line 52 from account_move_make_netting
        obj = self.env["account.move.make.netting"].with_context(
            active_ids=[self.move_line_1.id, self.move_line_6.id]
        )
        with self.assertRaises(Exception):
            wizard = obj.create(
                {
                    "move_line_ids": [
                        (6, 0, [self.move_line_1.id, self.move_line_6.id])
                    ],
                    "journal_id": self.miscellaneous_journal.id,
                }
            )
        obj = self.env["account.move.make.netting"].with_context(
            active_ids=[self.move_line_1.id, self.move_line_2.id]
        )
        wizard = obj.create(
            {
                "move_line_ids": [(6, 0, [self.move_line_1.id, self.move_line_2.id])],
                "journal_id": self.miscellaneous_journal.id,
            }
        )
        res = wizard.button_compensate()
        move = self.env["account.move"].browse(res["res_id"])
        self.assertEqual(
            len(move.line_ids), 2, "AR/AP netting move has an incorrect line number"
        )
        move_line_receivable = move.line_ids.filtered(
            lambda x: x.account_id == self.account_receivable
        )
        self.assertEqual(
            move_line_receivable.credit,
            100,
            "Incorrect credit amount for receivable move line",
        )
        self.assertTrue(
            move_line_receivable.reconciled and move_line_receivable.full_reconcile_id,
            "Receivable move line should be totally reconciled",
        )
        move_line_payable = move.line_ids.filtered(
            lambda x: x.account_id == self.account_payable
        )
        self.assertEqual(
            move_line_payable.debit, 100, "Incorrect debit amount for payable move line"
        )
        self.assertTrue(
            move_line_payable.reconciled and not move_line_payable.full_reconcile_id,
            "Receivable move line should be partially reconciled",
        )
