# -*- coding: utf-8 -*-
# Part of Odoo. See LICENSE file for full copyright and licensing details.

from odoo.addons.hr_expense.tests.common import TestExpenseCommon
from odoo.addons.sale.tests.test_sale_common import TestCommonSaleNoChart


class TestReInvoice(TestExpenseCommon, TestCommonSaleNoChart):

    @classmethod
    def setUpClass(cls):
        super(TestReInvoice, cls).setUpClass()

        cls.setUpExpenseProducts()

        cls.env.ref('product.list0').currency_id = cls.env.user.company_id.currency_id

        # partner and SO
        cls.partner_customer = cls.env['res.partner'].create({
            'name': 'Ze Client',
            'email': 'client@agrolait.com',
            'property_account_payable_id': cls.account_payable.id,
        })

        cls.sale_order = cls.env['sale.order'].with_context(mail_notrack=True, mail_create_nolog=True).create({
            'partner_id': cls.partner_customer_usd.id,
            'partner_invoice_id': cls.partner_customer_usd.id,
            'partner_shipping_id': cls.partner_customer_usd.id,
        })

    def test_at_cost(self):
        """ Test invoicing expenses at cost for product based on delivered and ordered quantities. """
        # create SO line and confirm SO (with only one line)
        sale_order_line = self.env['sale.order.line'].create({
            'name': self.product_ordered_cost.name,
            'product_id': self.product_ordered_cost.id,
            'product_uom_qty': 2,
            'product_uom': self.product_ordered_cost.uom_id.id,
            'price_unit': self.product_ordered_cost.list_price,
            'order_id': self.sale_order.id,
        })
        sale_order_line.product_id_change()
        self.sale_order.onchange_partner_id()
        self.sale_order._compute_tax_id()
        self.sale_order.action_confirm()

        self.assertTrue(self.sale_order.analytic_account_id, "Confirming SO with an expense product should trigger the analytic account creation")

        # create expense lines
        expense1 = self.env['hr.expense'].create({
            'name': 'Expense for ordered product',
            'employee_id': self.employee.id,
            'product_id': self.product_ordered_cost.id,
            'unit_amount': 12,
            'quantity': 2,
            'sheet_id': self.expense_sheet.id,
            'sale_order_id': self.sale_order.id,
            'analytic_account_id': self.sale_order.analytic_account_id.id,
        })
        expense2 = self.env['hr.expense'].create({
            'name': 'Expense for delivered product',
            'employee_id': self.employee.id,
            'product_id': self.product_deliver_cost.id,
            'unit_amount': 15,
            'quantity': 1,
            'sheet_id': self.expense_sheet.id,
            'sale_order_id': self.sale_order.id,
            'analytic_account_id': self.sale_order.analytic_account_id.id,
        })

        # approve and generate entries
        self.expense_sheet.approve_expense_sheets()
        self.expense_sheet.action_sheet_move_create()

        self.assertEqual(len(self.sale_order.order_line), 3, "There should be 3 lines on the SO")
        self.assertEqual(sale_order_line.qty_delivered, 0, "Exising SO line should not be impacted by reinvoicing product at cost")

        sol_ordered = self.sale_order.order_line.filtered(lambda sol: sol.product_id == self.product_ordered_cost and sol != sale_order_line)
        self.assertTrue(sol_ordered.is_expense, "The new SOL should be flag as coming from an expense")
        self.assertEqual(sol_ordered.qty_delivered_method, 'analytic', 'Delivered quantity of expense SO line should be computed by analytic amount')
        self.assertTrue(sol_ordered, "A new line with ordered expense should have been created on expense report posting")
        self.assertEqual(sol_ordered.price_unit, expense1.unit_amount, "The unit price of new SO line should be the one from the expense (at cost)")
        self.assertEqual(sol_ordered.product_uom_qty, 0, "The ordered quantity of new SO line should be zero")
        self.assertEqual(sol_ordered.qty_delivered, expense1.quantity, "The delivered quantity of new SO line should be the one from the expense")

        sol_deliver = self.sale_order.order_line.filtered(lambda sol: sol.product_id == self.product_deliver_cost and sol != sale_order_line)
        self.assertTrue(sol_deliver.is_expense, "The new SOL should be flag as coming from an expense")
        self.assertEqual(sol_deliver.qty_delivered_method, 'analytic', 'Delivered quantity of expense SO line should be computed by analytic amount')
        self.assertTrue(sol_deliver, "A new line with delivered expense should have been created on expense report posting")
        self.assertEqual(sol_deliver.price_unit, expense2.unit_amount, "The unit price of new SO line should be the one from the expense (at cost)")
        self.assertEqual(sol_deliver.product_uom_qty, 0, "The ordered quantity of new SO line should be zero")
        self.assertEqual(sol_deliver.qty_delivered, expense2.quantity, "The delivered quantity of new SO line should be the one from the expense")

    def test_sales_price_ordered(self):
        """ Test invoicing expenses at sales price for product based on ordered quantities. """
        # confirm SO (with no line)
        self.sale_order._compute_tax_id()
        self.sale_order.action_confirm()
        self.assertFalse(self.sale_order.analytic_account_id, "Confirming SO with no expense product should not trigger the analytic account creation")

        # create expense lines
        expense1 = self.env['hr.expense'].create({
            'name': 'Expense for ordered product at sales price',
            'employee_id': self.employee.id,
            'product_id': self.product_order_sales_price.id,
            'unit_amount': 15,
            'quantity': 2,
            'sheet_id': self.expense_sheet.id,
            'sale_order_id': self.sale_order.id,
            'analytic_account_id': self.sale_order.analytic_account_id.id,
        })

        # approve and generate entries
        self.expense_sheet.approve_expense_sheets()
        self.expense_sheet.action_sheet_move_create()

        self.assertTrue(self.sale_order.analytic_account_id, "Posting expense with an expense product should trigger the analytic account creation on SO")
        self.assertEqual(self.sale_order.analytic_account_id, expense1.analytic_account_id, "SO analytic account should be the same for the expense")
        self.assertEqual(len(self.sale_order.order_line), 1, "A new So line should have been created on expense report posting")

        sol_ordered = self.sale_order.order_line.filtered(lambda sol: sol.product_id == expense1.product_id)
        self.assertTrue(sol_ordered.is_expense, "The new SOL should be flag as coming from an expense")
        self.assertTrue(sol_ordered, "A new line with ordered expense should have been created on expense report posting")
        self.assertEqual(sol_ordered.price_unit, 10, "The unit price of new SO line should be the one from the expense (at sales price)")
        self.assertEqual(sol_ordered.product_uom_qty, 0, "The ordered quantity of new SO line should be zero")
        self.assertEqual(sol_ordered.qty_delivered, expense1.quantity, "The delivered quantity of new SO line should be the one from the expense")

    def test_sales_price_delivered(self):
        """ Test invoicing expenses at sales price for product based on delivered quantities. Check the existing SO line is not incremented, but a new
            expense SOL will be created and incremented.
        """
        # create SO line and confirm SO (with only one line)
        sale_order_line1 = self.env['sale.order.line'].create({
            'name': self.product_deliver_sales_price.name,
            'product_id': self.product_deliver_sales_price.id,
            'product_uom_qty': 2,
            'product_uom': self.product_deliver_sales_price.uom_id.id,
            'price_unit': self.product_deliver_sales_price.list_price,
            'order_id': self.sale_order.id,
        })
        sale_order_line1.product_id_change()
        self.sale_order._compute_tax_id()
        self.sale_order.action_confirm()

        self.assertTrue(self.sale_order.analytic_account_id, "Confirming SO with an expense product should trigger the analytic account creation")

        # create expense lines
        expense1 = self.env['hr.expense'].create({
            'name': 'Expense for delivered product at sales price',
            'employee_id': self.employee.id,
            'product_id': self.product_deliver_sales_price.id,
            'unit_amount': 15,
            'quantity': 3,
            'sheet_id': self.expense_sheet.id,
            'sale_order_id': self.sale_order.id,
            'analytic_account_id': self.sale_order.analytic_account_id.id,
        })

        # approve and generate entries
        self.expense_sheet.approve_expense_sheets()
        self.expense_sheet.action_sheet_move_create()

        self.assertEqual(len(self.sale_order.order_line), 2, "A new expense SOL should have been created.")

        sale_order_line2 = self.sale_order.order_line.filtered(lambda sol: sol != sale_order_line1)
        self.assertTrue(sale_order_line2.is_expense, "The new SOL should be flag as coming from an expense")
        self.assertEqual(sale_order_line2.price_unit, 10, "The unit price of SO line should be the same")
        self.assertEqual(sale_order_line2.product_uom_qty, 0, "The ordered quantity of new SO line should be zero")
        self.assertEqual(sale_order_line2.qty_delivered, expense1.quantity, "The delivered quantity of SO line should have be the same of the expense")

        # create 2nd expense lines
        expense2 = self.env['hr.expense'].create({
            'name': 'Expense for delivered product at sales price',
            'employee_id': self.employee.id,
            'product_id': self.product_deliver_sales_price.id,
            'unit_amount': 15,
            'quantity': 3,
            'sheet_id': self.expense_sheet2.id,
            'sale_order_id': self.sale_order.id,
            'analytic_account_id': self.sale_order.analytic_account_id.id,
        })

        # approve and generate entries
        self.expense_sheet2.approve_expense_sheets()
        self.expense_sheet2.action_sheet_move_create()

        self.assertEqual(len(self.sale_order.order_line), 2, "No new expense SOL should have been created.")

        self.assertEqual(sale_order_line2.price_unit, 10, "The unit price of SO line should be the same")
        self.assertEqual(sale_order_line2.product_uom_qty, 0, "The ordered quantity of new SO line should remains zero")
        self.assertEqual(sale_order_line2.qty_delivered, expense1.quantity + expense2.quantity, "The delivered quantity of SO line should have been incremented")

    def test_no_expense(self):
        """ Test invoicing expenses with no policy. Check nothing happen. """
        analytic_account = self.env['account.analytic.account'].create({
            'name': "AA to track expense",
        })
        # confirm SO
        sale_order_line = self.env['sale.order.line'].create({
            'name': self.product_no_expense.name,
            'product_id': self.product_no_expense.id,
            'product_uom_qty': 2,
            'product_uom': self.product_no_expense.uom_id.id,
            'price_unit': self.product_no_expense.list_price,
            'order_id': self.sale_order.id,
        })
        self.sale_order._compute_tax_id()
        self.sale_order.action_confirm()

        self.assertFalse(self.sale_order.analytic_account_id, "Confirming SO with an no-expense product should not trigger the analytic account creation")

        # create expense lines
        expense1 = self.env['hr.expense'].create({
            'name': 'Expense for no expense product',
            'employee_id': self.employee.id,
            'unit_amount': 15,
            'quantity': 3,
            'sheet_id': self.expense_sheet.id,
            'sale_order_id': self.sale_order.id,
            'analytic_account_id': analytic_account.id,
        })
        expense1.write({'product_id': self.product_no_expense.id})
        # approve and generate entries
        self.expense_sheet.approve_expense_sheets()
        self.expense_sheet.action_sheet_move_create()
        self.assertFalse(expense1.sale_order_id, "None reinvoicable expense can not be linked to SO")
        self.assertFalse(self.sale_order.analytic_account_id, "Posting expense with an non expense product should not trigger analytic Account creation from SO, since non reinvoicable expense can not be linked to SO")

        self.assertEqual(len(self.sale_order.order_line), 1, "No SO line should have been created (or removed) on expense report posting")
        self.assertEqual(sale_order_line.price_unit, self.product_no_expense.list_price, "The unit price of SO line should be the same")
        self.assertEqual(sale_order_line.product_uom_qty, 2, "The ordered quantity of SO line should be two")
        self.assertEqual(sale_order_line.qty_delivered, 0, "The delivered quantity of SO line should have been incremented")
