# Copyright 2020 Tecnativa - David Vidal
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl).

from odoo import exceptions
from odoo.tests import Form, tagged

from odoo.addons.account.tests.common import AccountTestInvoicingCommon


@tagged("post_install", "-at_install")
class TestSaleGlobalDiscount(AccountTestInvoicingCommon):
    @classmethod
    def setUpClass(cls, chart_template_ref=None):
        super().setUpClass()
        cls.chart_template = chart_template_ref
        cls.env.ref("base_global_discount.group_global_discount").write(
            {"users": [(4, cls.env.user.id)]}
        )
        cls.main_company = cls.env.ref("base.main_company")
        cls.account = cls.env["account.account"].create(
            {
                "name": "Test account Global Discount",
                "code": "TEST99999",
                "account_type": "asset_current",
                "reconcile": True,
            }
        )
        cls.global_discount_obj = cls.env["global.discount"]
        cls.global_discount_1 = cls.global_discount_obj.create(
            {
                "name": "Test Discount 1",
                "sequence": 1,
                "discount_scope": "sale",
                "discount": 20,
                "account_id": cls.account.id,
            }
        )
        cls.global_discount_2 = cls.global_discount_obj.create(
            {
                "name": "Test Discount 2",
                "sequence": 2,
                "discount_scope": "sale",
                "discount": 30,
                "account_id": cls.account.id,
            }
        )
        cls.global_discount_3 = cls.global_discount_obj.create(
            {
                "name": "Test Discount 3",
                "sequence": 3,
                "discount_scope": "sale",
                "discount": 50,
                "account_id": cls.account.id,
            }
        )
        cls.pricelist = cls.env["product.pricelist"].create(
            {"name": "Public Pricelist", "sequence": 1}
        )
        cls.partner_1 = cls.env["res.partner"].create(
            {"name": "Mr. Odoo", "property_product_pricelist": cls.pricelist.id}
        )
        cls.partner_2 = cls.env["res.partner"].create(
            {"name": "Mrs. Odoo", "property_product_pricelist": cls.pricelist.id}
        )
        cls.partner_2.customer_global_discount_ids = (
            cls.global_discount_2 + cls.global_discount_3
        )
        cls.product_1 = cls.env["product.product"].create(
            {"name": "Test Product 1", "type": "service"}
        )
        cls.product_2 = cls.env["product.product"].create(
            {"name": "Test Product 2", "type": "service"}
        )
        cls.tax_group_5pc = cls.env["account.tax.group"].create(
            {"name": "Test Tax Group 5%", "sequence": 1}
        )
        cls.tax_group_15pc = cls.env["account.tax.group"].create(
            {"name": "Test Tax Group 15%", "sequence": 2}
        )
        cls.tax_1 = cls.tax_sale_a
        cls.tax_1.amount = 15.0
        cls.tax_2 = cls.tax_sale_b
        cls.tax_2.amount = 5.0
        cls.sale_journal0 = cls.env["account.journal"].create(
            {
                "name": "Sale Journal",
                "type": "sale",
                "code": "SJT0",
            }
        )
        sale_form = Form(cls.env["sale.order"])
        sale_form.partner_id = cls.partner_1
        with sale_form.order_line.new() as order_line:
            order_line.product_id = cls.product_1
            order_line.tax_id.clear()
            order_line.tax_id.add(cls.tax_1)
            order_line.tax_id.add(cls.tax_2)
            order_line.product_uom_qty = 2
            order_line.price_unit = 75
        with sale_form.order_line.new() as order_line:
            order_line.product_id = cls.product_2
            order_line.tax_id.clear()
            order_line.tax_id.add(cls.tax_1)
            order_line.tax_id.add(cls.tax_2)
            order_line.product_uom_qty = 3
            order_line.price_unit = 33.33
        cls.sale = sale_form.save()

    @staticmethod
    def get_taxes_widget_total_tax(order):
        tax_amount_total = 0.0
        for subtotal in order.tax_totals["subtotals"]:
            tax_amount_total = sum(
                tax_group["tax_amount"] for tax_group in subtotal["tax_groups"]
            )
        return tax_amount_total

    def test_01_global_sale_successive_discounts(self):
        """Add global discounts to the sale order"""
        self.assertAlmostEqual(self.sale.amount_total, 299.99)
        self.assertAlmostEqual(self.sale.amount_tax, 50)
        self.assertAlmostEqual(
            self.get_taxes_widget_total_tax(self.sale), self.sale.amount_tax
        )
        self.assertAlmostEqual(self.sale.amount_untaxed, 249.99)
        # Apply a single 20% global discount
        self.sale.global_discount_ids = self.global_discount_1
        self.sale._compute_tax_totals()
        # Discount is computed over the base and global taxes are computed
        # according to it line by line with the core method
        self.assertAlmostEqual(self.sale.amount_global_discount, 50)
        self.assertAlmostEqual(self.sale.amount_untaxed, 199.99)
        self.assertAlmostEqual(self.sale.amount_untaxed_before_global_discounts, 249.99)
        self.assertAlmostEqual(self.sale.amount_total, 239.99)
        self.assertAlmostEqual(self.sale.amount_total_before_global_discounts, 299.99)
        self.assertAlmostEqual(self.sale.amount_tax, 40)
        self.assertAlmostEqual(
            self.get_taxes_widget_total_tax(self.sale), self.sale.amount_tax
        )
        # Apply an additional 30% global discount
        self.sale.global_discount_ids += self.global_discount_2
        self.sale._compute_tax_totals()
        self.assertAlmostEqual(self.sale.amount_global_discount, 110)
        self.assertAlmostEqual(self.sale.amount_untaxed, 139.99)
        self.assertAlmostEqual(self.sale.amount_untaxed_before_global_discounts, 249.99)
        self.assertAlmostEqual(self.sale.amount_total, 167.99)
        self.assertAlmostEqual(self.sale.amount_total_before_global_discounts, 299.99)
        self.assertAlmostEqual(self.sale.amount_tax, 28)
        self.assertAlmostEqual(
            self.get_taxes_widget_total_tax(self.sale), self.sale.amount_tax
        )
        # The account move should look like this
        #   credit    debit  name
        # ========  =======  ===============================================
        #      150        0  Test Product 1
        #    99.99        0  Test Product 2
        #    13.13        0  Test TAX 15%
        #     4.38        0  TAX 5%
        #        0   105.01
        #        0       75  Test Discount 2 (30.00%) - Test TAX 15%, TAX 5%
        #        0    87.49  Test Discount 3 (50.00%) - Test TAX 15%, TAX 5%
        # ========  =======  ===============================================

    def test_02_global_sale_discounts_from_partner(self):
        """Change the partner and his global discounts go to the invoice"""
        # (30% then 50%)
        self.sale.partner_id = self.partner_2
        self.sale._compute_tax_totals()
        self.assertAlmostEqual(self.sale.amount_global_discount, 162.49)
        self.assertAlmostEqual(self.sale.amount_untaxed, 87.5)
        self.assertAlmostEqual(self.sale.amount_untaxed_before_global_discounts, 249.99)
        self.assertAlmostEqual(self.sale.amount_total, 105.01)
        self.assertAlmostEqual(self.sale.amount_total_before_global_discounts, 299.99)
        self.assertAlmostEqual(self.sale.amount_tax, 17.51)
        self.assertAlmostEqual(
            self.get_taxes_widget_total_tax(self.sale), self.sale.amount_tax
        )

    def test_03_global_sale_discounts_to_invoice(self):
        """All the discounts go to the invoice"""
        self.sale.partner_id = self.partner_2
        self.sale._compute_tax_totals()
        self.sale.order_line.mapped("product_id").write({"invoice_policy": "order"})
        self.sale.action_confirm()
        move = self.sale._create_invoices()
        # Check the invoice relevant fields
        self.assertEqual(len(move.invoice_global_discount_ids), 2)
        discount_amount = sum(
            move.invoice_global_discount_ids.mapped("discount_amount")
        )
        self.assertAlmostEqual(discount_amount, 162.49)
        self.assertAlmostEqual(move.amount_untaxed_before_global_discounts, 249.99)
        self.assertAlmostEqual(move.amount_untaxed, 87.5)
        self.assertAlmostEqual(move.amount_total, 105.01)
        # Expected Journal Entry
        # credit    debit    account
        # ========  =======  =========
        #      150        0  400000 (line 1)
        #    99.99        0  400000 (line 2)
        #    13.13        0  400000 (line_tax_1)
        #     4.38        0  400000 (line_tax_2)
        #        0   105.01  121000 (Base)
        #        0       75  TEST99999 (Global discount 1)
        #        0    87.49  TEST99999 (Global discount 2)
        #   267.50   267.50  <- Balance
        line_tax_1 = move.line_ids.filtered(lambda x: x.tax_line_id == self.tax_1)
        line_tax_2 = move.line_ids.filtered(lambda x: x.tax_line_id == self.tax_2)
        self.assertAlmostEqual(line_tax_1.credit, 13.13)
        self.assertAlmostEqual(line_tax_2.credit, 4.38)
        term_line = move.line_ids.filtered(
            lambda x: x.account_id.account_type == "asset_receivable"
        )
        self.assertAlmostEqual(term_line.debit, 105.01)
        discount_lines = move.line_ids.filtered("invoice_global_discount_id")
        self.assertEqual(len(discount_lines), 2)
        self.assertAlmostEqual(sum(discount_lines.mapped("debit")), 162.49)

    def test_04_incompatible_taxes(self):
        # Line 1 with tax 1 and tax 2
        # Line 2 with only tax 2
        self.sale.order_line[1].tax_id = [(6, 0, self.tax_1.ids)]
        with self.assertRaises(exceptions.UserError):
            self.sale.global_discount_ids = self.global_discount_1
            self.sale._compute_amounts()

    def test_05_no_taxes(self):
        self.sale.order_line[1].tax_id = False
        with self.assertRaises(exceptions.UserError):
            self.sale.global_discount_ids = self.global_discount_1
            self.sale._compute_amounts()

    def test_06_discounted_line(self):
        self.sale.global_discount_ids = self.global_discount_1
        line = self.sale.order_line[0]
        line.discount = 10
        self.assertAlmostEqual(line.price_subtotal, 135)
        self.assertAlmostEqual(self.sale.amount_untaxed_before_global_discounts, 234.99)
        self.assertAlmostEqual(self.sale.amount_untaxed, 187.99)

    def test_07_bypass_global_discounts(self):
        self.sale.global_discount_ids = self.global_discount_2 + self.global_discount_3
        self.sale.order_line[0].product_id.bypass_global_discount = True
        self.assertAlmostEqual(self.sale.amount_untaxed_before_global_discounts, 249.99)
        self.assertAlmostEqual(self.sale.amount_untaxed, 185.00)
        self.assertAlmostEqual(self.sale.amount_total, 222.00)
        self.assertAlmostEqual(self.sale.amount_tax, 37.00)
        self.assertAlmostEqual(
            self.get_taxes_widget_total_tax(self.sale), self.sale.amount_tax
        )

    def test_08_global_discount_with_tax_included(self):
        tax_included = self.env["account.tax"].create(
            {
                "name": "IVA 21% included",
                "amount": 21,
                "amount_type": "percent",
                "type_tax_use": "sale",
                "price_include": True,
                "include_base_amount": True,
            }
        )
        for line in self.sale.order_line:
            line.tax_id = [(6, 0, tax_included.ids)]
        self.sale._compute_tax_totals()
        self.sale.global_discount_ids = self.global_discount_1
        self.sale._compute_tax_totals()
        self.assertTrue(self.sale.amount_tax > 0)
        self.assertTrue(
            self.sale.amount_total < self.sale.amount_total_before_global_discounts
        )
        self.assertAlmostEqual(
            self.get_taxes_widget_total_tax(self.sale), self.sale.amount_tax
        )
