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

from datetime import timedelta

from odoo import fields
from odoo.addons.stock.tests.common import TestStockCommon

from odoo.tests import Form


class TestSaleMrpLeadTime(TestStockCommon):

    @classmethod
    def setUpClass(cls):
        super().setUpClass()
        cls.env.ref('stock.route_warehouse0_mto').active = True
        # Update the product_1 with type, route, Manufacturing Lead Time and Customer Lead Time
        with Form(cls.product_1) as p1:
            # `type` is invisible in the view,
            # and it's a compute field based on `type` which is the field visible in the view
            p1.is_storable = True
            p1.sale_delay = 5.0
            p1.route_ids.clear()
            p1.route_ids.add(cls.warehouse_1.manufacture_pull_id.route_id)
            p1.route_ids.add(cls.warehouse_1.mto_pull_id.route_id)

        # Update the product_2 with type
        with Form(cls.product_2) as p2:
            # `type` is invisible in the view,
            # and it's a compute field based on `type` which is the field visible in the view
            p2.type = 'consu'

        # Create Bill of materials for product_1
        with Form(cls.env['mrp.bom']) as bom:
            bom.product_tmpl_id = cls.product_1.product_tmpl_id
            bom.product_qty = 2
            bom.produce_delay = 5.0
            with bom.bom_line_ids.new() as line:
                line.product_id = cls.product_2
                line.product_qty = 4

        cls.warehouse_3_steps_pull = cls.env['stock.warehouse'].create({
            'name': 'Warehouse 3 steps',
            'code': '3S',
            'delivery_steps': 'pick_pack_ship',
        })
        delivery_route_3 = cls.warehouse_3_steps_pull.delivery_route_id
        delivery_route_3.rule_ids[0].write({
            'location_dest_id': delivery_route_3.rule_ids[1].location_src_id.id,
        })
        delivery_route_3.rule_ids[1].write({'action': 'pull'})
        delivery_route_3.rule_ids[2].write({'action': 'pull'})
        cls.warehouse_3_steps_pull.mto_pull_id.write({
            'location_dest_id': delivery_route_3.rule_ids[1].location_src_id.id,
        })

    def test_00_product_company_level_delays(self):
        """ In order to check schedule date, set product's Manufacturing Lead Time
            and Customer Lead Time and also set company's Manufacturing Lead Time
            and Sales Safety Days."""

        company = self.env.ref('base.main_company')

        # Update company with Manufacturing Lead Time and Sales Safety Days
        company.write({'manufacturing_lead': 3.0,
                       'security_lead': 3.0})

        # Create sale order of product_1
        order_form = Form(self.env['sale.order'])
        order_form.partner_id = self.partner_1
        with order_form.order_line.new() as line:
            line.product_id = self.product_1
            line.product_uom_qty = 10
        order = order_form.save()
        # Confirm sale order
        order.action_confirm()

        # Check manufacturing order created or not
        manufacturing_order = self.env['mrp.production'].search([('product_id', '=', self.product_1.id), ('move_dest_ids', 'in', order.picking_ids[0].move_ids.ids)])
        self.assertTrue(manufacturing_order, 'Manufacturing order should be created.')

        # Check schedule date of picking
        deadline_picking = fields.Datetime.from_string(order.date_order) + timedelta(days=self.product_1.sale_delay)
        out_date = deadline_picking - timedelta(days=company.security_lead)
        self.assertAlmostEqual(
            order.picking_ids[0].scheduled_date, out_date,
            delta=timedelta(seconds=1),
            msg='Schedule date of picking should be equal to: Order date + Customer Lead Time - Sales Safety Days.'
        )
        self.assertAlmostEqual(
            order.picking_ids[0].date_deadline, deadline_picking,
            delta=timedelta(seconds=1),
            msg='Deadline date of picking should be equal to: Order date + Customer Lead Time.'
        )

        # Check schedule date and deadline of manufacturing order
        mo_date_start = out_date - timedelta(days=manufacturing_order.bom_id.produce_delay) - timedelta(days=company.manufacturing_lead)
        self.assertAlmostEqual(
            fields.Datetime.from_string(manufacturing_order.date_start), mo_date_start,
            delta=timedelta(seconds=1),
            msg="Schedule date of manufacturing order should be equal to: Schedule date of picking - product's Manufacturing Lead Time - company's Manufacturing Lead Time."
        )
        self.assertAlmostEqual(
            fields.Datetime.from_string(manufacturing_order.date_deadline), deadline_picking,
            delta=timedelta(seconds=1),
            msg="Deadline date of manufacturing order should be equal to the deadline of sale picking"
        )

    def test_01_product_route_level_delays(self):
        """ In order to check schedule dates, set product's Manufacturing Lead Time
            and Customer Lead Time and also set warehouse route's delay."""

        warehouse = self.warehouse_3_steps_pull

        # Set delay on pull rule
        for pull_rule in warehouse.delivery_route_id.rule_ids:
            pull_rule.write({'delay': 2})

        # Create sale order of product_1
        order_form = Form(self.env['sale.order'])
        order_form.partner_id = self.partner_1
        order_form.warehouse_id = warehouse
        with order_form.order_line.new() as line:
            line.product_id = self.product_1
            line.product_uom_qty = 6
        order = order_form.save()
        # Confirm sale order
        order.action_confirm()

        # Run scheduler
        self.env['procurement.group'].run_scheduler()

        # Check manufacturing order created or not
        manufacturing_order = self.env['mrp.production'].search([('product_id', '=', self.product_1.id)]) 
        self.assertTrue(manufacturing_order, 'Manufacturing order should be created.')

        # Check the picking crated or not
        self.assertTrue(order.picking_ids, "Pickings should be created.")

        # Check schedule date of ship type picking
        out = order.picking_ids.filtered(lambda r: r.picking_type_id == warehouse.out_type_id)
        out_min_date = fields.Datetime.from_string(out.scheduled_date)
        out_date = fields.Datetime.from_string(order.date_order) + timedelta(days=self.product_1.sale_delay) - timedelta(days=out.move_ids[0].rule_id.delay)
        self.assertAlmostEqual(
            out_min_date, out_date,
            delta=timedelta(seconds=10),
            msg='Schedule date of ship type picking should be equal to: order date + Customer Lead Time - pull rule delay.'
        )

        # Check schedule date of pack type picking
        pack = order.picking_ids.filtered(lambda r: r.picking_type_id == warehouse.pack_type_id)
        pack_min_date = fields.Datetime.from_string(pack.scheduled_date)
        pack_date = out_date - timedelta(days=pack.move_ids[0].rule_id.delay)
        self.assertAlmostEqual(
            pack_min_date, pack_date,
            delta=timedelta(seconds=10),
            msg='Schedule date of pack type picking should be equal to: Schedule date of ship type picking - pull rule delay.'
        )

        # Check schedule date of pick type picking
        pick = order.picking_ids.filtered(lambda r: r.picking_type_id == warehouse.pick_type_id)
        pick_min_date = fields.Datetime.from_string(pick.scheduled_date)
        self.assertAlmostEqual(
            pick_min_date, pack_date,
            delta=timedelta(seconds=10),
            msg='Schedule date of pick type picking should be equal to: Schedule date of pack type picking.'
        )

        # Check schedule date and deadline date of manufacturing order
        mo_date_start = out_date - timedelta(days=manufacturing_order.bom_id.produce_delay) - timedelta(days=warehouse.delivery_route_id.rule_ids[0].delay) - timedelta(days=self.env.ref('base.main_company').manufacturing_lead)
        self.assertAlmostEqual(
            fields.Datetime.from_string(manufacturing_order.date_start), mo_date_start,
            delta=timedelta(seconds=1),
            msg="Schedule date of manufacturing order should be equal to: Schedule date of picking - product's Manufacturing Lead Time- delay pull_rule."
        )
        self.assertAlmostEqual(
            manufacturing_order.date_deadline, order.picking_ids[0].date_deadline,
            delta=timedelta(seconds=1),
            msg="Deadline date of manufacturing order should be equal to the deadline of sale picking"
        )
