<commit_before># Copyright 2020 KMEE
# Copyright (C) 2021  <NAME> - Akretion
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl).

from odoo.tests import SavepointCase, tagged


@tagged("post_install", "-at_install")
class TestSaleStock(SavepointCase):
    @classmethod
    def setUpClass(cls):
        super().setUpClass()
        cls.invoice_model = cls.env["account.invoice"]
        cls.invoice_wizard = cls.env["stock.invoice.onshipping"]
        cls.stock_return_picking = cls.env["stock.return.picking"]
        cls.stock_picking = cls.env["stock.picking"]

        # TODO: Em uma instalção direta do modulo
        #  $ odoo -d test -i l10n_br_sale_stock --stop-after-init
        #  e depois
        #  $ odoo -d test --update=l10n_br_sale_stock --test-enable
        #  o campo do Diário não está vindo preenchido a solução e forçar
        #  preenchimento para não ter erro nos testes porem no caso dos dados
        #  demo ao testar na tela vai continuar o problema, para evita-lo é
        #  preciso instalar o l10n_br_account antes ou preencher manualmente,
        #  porém isso é um problema já que a instalação direta do modulo deve
        #  funcionar sem isso.
        #  No modulo l10n_br_sale para resolver esse problema é feito isso
        #  https://github.com/OCA/l10n-brazil/blob/12.0/l10n_br_sale/
        #  hooks.py#L35 e https://github.com/OCA/l10n-brazil/blob/12.0/
        #  l10n_br_sale/demo/fiscal_operation_simple.xml#L10 mas por algum
        #  motivo não vem carregado aqui, mesmo tendo o l10n_br_sale como
        #  dependencia.
        cls.fiscal_operation_venda = cls.env.ref("l10n_br_fiscal.fo_venda")
        if not cls.fiscal_operation_venda.journal_id:
            cls.fiscal_operation_venda.journal_id = cls.env.ref(
                "l10n_br_coa_simple.sale_journal_main_company"
            )
        cls.fiscal_operation_dev_venda = cls.env.ref(
            "l10n_br_fiscal.fo_devolucao_venda"
        )
        if not cls.fiscal_operation_dev_venda.journal_id:
            cls.fiscal_operation_dev_venda.journal_id = cls.env.ref(
                "l10n_br_coa_simple.general_journal_main_company"
            )

    def test_02_sale_stock_return(self):
        """
        Test a SO with a product invoiced on delivery. Deliver and invoice
        the SO, then do a return
        of the picking. Check that a refund invoice is well generated.
        """
        # intial so
        self.partner = self.env.ref("l10n_br_base.res_partner_address_ak2")
        self.product = self.env.ref("product.product_delivery_01")
        so_vals = {
            "partner_id": self.partner.id,
            "partner_invoice_id": self.partner.id,
            "partner_shipping_id": self.partner.id,
            "order_line": [
                (
                    0,
                    0,
                    {
                        "name": self.product.name,
                        "product_id": self.product.id,
                        "product_uom_qty": 3.0,
                        "product_uom": self.product.uom_id.id,
                        "price_unit": self.product.list_price,
                    },
                )
            ],
            "pricelist_id": self.env.ref("product.list0").id,
        }
        self.so = self.env["sale.order"].create(so_vals)

        for line in self.so.order_line:
            line._onchange_product_id_fiscal()

        # confirm our standard so, check the picking
        self.so.action_confirm()
        self.assertTrue(
            self.so.picking_ids,
            'Sale Stock: no picking created for "invoice on '
            'delivery" storable products',
        )

        # set stock.picking to be invoiced
        self.assertTrue(
            len(self.so.picking_ids) == 1,
            "More than one stock " "picking for sale.order",
        )
        self.so.picking_ids.set_to_be_invoiced()

        # validate stock.picking
        stock_picking = self.so.picking_ids
        self.env["stock.immediate.transfer"].create(
            {"pick_ids": [(4, stock_picking.id)]}
        ).process()

        # compare sale.order.line with stock.move
        stock_move = stock_picking.move_lines
        sale_order_line = self.so.order_line

        sm_fields = [key for key in self.env["stock.move"]._fields.keys()]
        sol_fields = [key for key in self.env["sale.order.line"]._fields.keys()]

        skipped_fields = [
            "id",
            "display_name",
            "state",
        ]
        common_fields = list(set(sm_fields) & set(sol_fields) - set(skipped_fields))

        for field in common_fields:
            self.assertEqual(
                stock_move[field],
                sale_order_line[field],
                "Field %s failed to transfer from "
                "sale.order.line to stock.move" % field,
            )

    def test_picking_sale_order_product_and_service(self):
        """
        Test Sale Order with product and service
        """

        sale_order_2 = self.env.ref("l10n_br_sale_stock.main_so_l10n_br_sale_stock_2")
        sale_order_2.action_confirm()

        # Metodo de criação da fatura a partir do sale.order
        # deve gerar apenas a linha de serviço
        sale_order_2.action_invoice_create(final=True)
        # Deve existir apenas a Fatura/Documento Fiscal de Serviço
        self.assertEqual(1, sale_order_2.invoice_count)
        for invoice in sale_order_2.invoice_ids:
            for line in invoice.invoice_line_ids:
                self.assertEqual(line.product_id.type, "service")
            # Confirmando a Fatura de Serviço
            invoice.action_invoice_open()
            self.assertEqual(invoice.state, "open", "Invoice should be in state Open")

        picking = sale_order_2.picking_ids
        # Check product availability
        picking.action_assign()
        # Apenas o Produto criado
        self.assertEqual(len(picking.move_ids_without_package), 1)
        self.assertEqual(picking.invoice_state, "2binvoiced")
        # Force product availability
        for move in picking.move_ids_without_package:
            move.quantity_done = move.product_uom_qty
        picking.button_validate()
        self.assertEqual(picking.state, "done")
        wizard_obj = self.invoice_wizard.with_context(
            active_ids=picking.ids,
            active_model=picking._name,
            active_id=picking.id,
        )
        fields_list = wizard_obj.fields_get().keys()
        wizard_values = wizard_obj.default_get(fields_list)
        wizard = wizard_obj.create(wizard_values)
        wizard.onchange_group()
        wizard.action_generate()
        domain = [("picking_ids", "=", picking.id)]
        invoice = self.invoice_model.search(domain)
        self.assertEqual(picking.invoice_state, "invoiced")
        self.assertIn(invoice, picking.invoice_ids)
        self.assertIn(picking, invoice.picking_ids)
        # Picking criado com o Partner Shipping da Sale Order
        self.assertEqual(picking.partner_id, sale_order_2.partner_shipping_id)
        # Fatura criada com o Partner Invoice da Sale Order
        self.assertEqual(invoice.partner_id, sale_order_2.partner_invoice_id)
        # Fatura criada com o Partner Shipping usado no Picking
        self.assertEqual(invoice.partner_shipping_id, picking.partner_id)
        # Quando informado usar o Termo de Pagto definido no Pedido de Venda
        # e não o padrão do cliente
        self.assertEqual(invoice.payment_term_id, sale_order_2.payment_term_id)

        # Apenas a Fatura com a linha do produto foi criada
        self.assertEqual(len(invoice.invoice_line_ids), 1)

        # No Pedido de Venda devem existir duas Faturas/Documentos Fiscais
        # de Serviço e Produto
        self.assertEqual(2, sale_order_2.invoice_count)

        # Confirmando a Fatura
        invoice.action_invoice_open()
        self.assertEqual(invoice.state, "open", "Invoice should be in state Open")

        # Validar Atualização da Quantidade Faturada
        for line in sale_order_2.order_line:
            # Apenas a linha de Produto tem a qtd faturada dobrada
            if line.product_id.type == "product":
                # A quantidade Faturada deve ser igual
                # a Quantidade do Produto
                self.assertEqual(line.product_uom_qty, line.qty_invoiced)

        # Teste de Retorno
        self.return_wizard = self.stock_return_picking.with_context(
            dict(active_id=picking.id)
        ).create(dict(invoice_state="2binvoiced"))

        result_wizard = self.return_wizard.create_returns()
        self.assertTrue(result_wizard, "Create returns wizard fail.")
        picking_devolution = self.stock_picking.browse(result_wizard.get("res_id"))

        self.assertEqual(picking_devolution.invoice_state, "2binvoiced")
        self.assertTrue(
            picking_devolution.fiscal_operation_id, "Missing Fiscal Operation."
        )
        for line in picking_devolution.move_lines:
            self.assertEqual(line.invoice_state, "2binvoiced")<commit_msg>[FIX] Test, field price_unit can be changed after Confimed stock.picking depending of Product Stock Valuation defined.<commit_after># Copyright 2020 KMEE
# Copyright (C) 2021  <NAME> - Akretion
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl).

from odoo.tests import SavepointCase, tagged


@tagged("post_install", "-at_install")
class TestSaleStock(SavepointCase):
    @classmethod
    def setUpClass(cls):
        super().setUpClass()
        cls.invoice_model = cls.env["account.invoice"]
        cls.invoice_wizard = cls.env["stock.invoice.onshipping"]
        cls.stock_return_picking = cls.env["stock.return.picking"]
        cls.stock_picking = cls.env["stock.picking"]

        # TODO: Em uma instalção direta do modulo
        #  $ odoo -d test -i l10n_br_sale_stock --stop-after-init
        #  e depois
        #  $ odoo -d test --update=l10n_br_sale_stock --test-enable
        #  o campo do Diário não está vindo preenchido a solução e forçar
        #  preenchimento para não ter erro nos testes porem no caso dos dados
        #  demo ao testar na tela vai continuar o problema, para evita-lo é
        #  preciso instalar o l10n_br_account antes ou preencher manualmente,
        #  porém isso é um problema já que a instalação direta do modulo deve
        #  funcionar sem isso.
        #  No modulo l10n_br_sale para resolver esse problema é feito isso
        #  https://github.com/OCA/l10n-brazil/blob/12.0/l10n_br_sale/
        #  hooks.py#L35 e https://github.com/OCA/l10n-brazil/blob/12.0/
        #  l10n_br_sale/demo/fiscal_operation_simple.xml#L10 mas por algum
        #  motivo não vem carregado aqui, mesmo tendo o l10n_br_sale como
        #  dependencia.
        cls.fiscal_operation_venda = cls.env.ref("l10n_br_fiscal.fo_venda")
        if not cls.fiscal_operation_venda.journal_id:
            cls.fiscal_operation_venda.journal_id = cls.env.ref(
                "l10n_br_coa_simple.sale_journal_main_company"
            )
        cls.fiscal_operation_dev_venda = cls.env.ref(
            "l10n_br_fiscal.fo_devolucao_venda"
        )
        if not cls.fiscal_operation_dev_venda.journal_id:
            cls.fiscal_operation_dev_venda.journal_id = cls.env.ref(
                "l10n_br_coa_simple.general_journal_main_company"
            )

    def test_02_sale_stock_return(self):
        """
        Test a SO with a product invoiced on delivery. Deliver and invoice
        the SO, then do a return
        of the picking. Check that a refund invoice is well generated.
        """
        # intial so
        self.partner = self.env.ref("l10n_br_base.res_partner_address_ak2")
        self.product = self.env.ref("product.product_delivery_01")
        so_vals = {
            "partner_id": self.partner.id,
            "partner_invoice_id": self.partner.id,
            "partner_shipping_id": self.partner.id,
            "order_line": [
                (
                    0,
                    0,
                    {
                        "name": self.product.name,
                        "product_id": self.product.id,
                        "product_uom_qty": 3.0,
                        "product_uom": self.product.uom_id.id,
                        "price_unit": self.product.list_price,
                    },
                )
            ],
            "pricelist_id": self.env.ref("product.list0").id,
        }
        self.so = self.env["sale.order"].create(so_vals)

        for line in self.so.order_line:
            line._onchange_product_id_fiscal()

        # confirm our standard so, check the picking
        self.so.action_confirm()
        self.assertTrue(
            self.so.picking_ids,
            'Sale Stock: no picking created for "invoice on '
            'delivery" storable products',
        )

        # set stock.picking to be invoiced
        self.assertTrue(
            len(self.so.picking_ids) == 1,
            "More than one stock " "picking for sale.order",
        )
        self.so.picking_ids.set_to_be_invoiced()

        # validate stock.picking
        stock_picking = self.so.picking_ids

        # compare sale.order.line with stock.move
        stock_move = stock_picking.move_lines
        sale_order_line = self.so.order_line

        sm_fields = [key for key in self.env["stock.move"]._fields.keys()]
        sol_fields = [key for key in self.env["sale.order.line"]._fields.keys()]

        skipped_fields = [
            "id",
            "display_name",
            "state",
        ]
        common_fields = list(set(sm_fields) & set(sol_fields) - set(skipped_fields))

        for field in common_fields:
            self.assertEqual(
                stock_move[field],
                sale_order_line[field],
                "Field %s failed to transfer from "
                "sale.order.line to stock.move" % field,
            )

        self.env["stock.immediate.transfer"].create(
            {"pick_ids": [(4, stock_picking.id)]}
        ).process()

        # O valor do price_unit da stock.move é alterado ao Confirmar o
        # stock.picking de acordo com a forma de valorização de estoque
        # definida( ex.: metodo _run_fifo é chamado e altera o valor do
        # price_unit https://github.com/odoo/odoo/blob/12.0/addons
        # /stock_account/models/stock.py#L255 ), por isso os campos
        # relacionados a esse valor não são iguais.
        # O teste está sendo feito novamente para essa questão ficar clara
        # em alterações e migrações.
        skipped_fields_after_confirm = [
            "price_gross",
            "amount_taxed",
            "financial_total",
            "fiscal_price",
            "amount_fiscal",
            "price_unit",
            "amount_untaxed",
            "amount_total",
        ]
        skipped_fields[len(skipped_fields) :] = skipped_fields_after_confirm

        common_fields = list(set(sm_fields) & set(sol_fields) - set(skipped_fields))

        for field in common_fields:
            self.assertEqual(
                stock_move[field],
                sale_order_line[field],
                "Field %s failed to transfer from "
                "sale.order.line to stock.move" % field,
            )

    def test_picking_sale_order_product_and_service(self):
        """
        Test Sale Order with product and service
        """

        sale_order_2 = self.env.ref("l10n_br_sale_stock.main_so_l10n_br_sale_stock_2")
        sale_order_2.action_confirm()

        # Metodo de criação da fatura a partir do sale.order
        # deve gerar apenas a linha de serviço
        sale_order_2.action_invoice_create(final=True)
        # Deve existir apenas a Fatura/Documento Fiscal de Serviço
        self.assertEqual(1, sale_order_2.invoice_count)
        for invoice in sale_order_2.invoice_ids:
            for line in invoice.invoice_line_ids:
                self.assertEqual(line.product_id.type, "service")
            # Confirmando a Fatura de Serviço
            invoice.action_invoice_open()
            self.assertEqual(invoice.state, "open", "Invoice should be in state Open")

        picking = sale_order_2.picking_ids
        # Check product availability
        picking.action_assign()
        # Apenas o Produto criado
        self.assertEqual(len(picking.move_ids_without_package), 1)
        self.assertEqual(picking.invoice_state, "2binvoiced")
        # Force product availability
        for move in picking.move_ids_without_package:
            move.quantity_done = move.product_uom_qty
            # Usado para validar a transferencia dos campos da linha
            # do Pedido de Venda para a linha da Fatura/Invoice
            sale_order_line = move.sale_line_id

        picking.button_validate()
        self.assertEqual(picking.state, "done")
        wizard_obj = self.invoice_wizard.with_context(
            active_ids=picking.ids,
            active_model=picking._name,
            active_id=picking.id,
        )
        fields_list = wizard_obj.fields_get().keys()
        wizard_values = wizard_obj.default_get(fields_list)
        wizard = wizard_obj.create(wizard_values)
        wizard.onchange_group()
        wizard.action_generate()
        domain = [("picking_ids", "=", picking.id)]
        invoice = self.invoice_model.search(domain)
        self.assertEqual(picking.invoice_state, "invoiced")
        self.assertIn(invoice, picking.invoice_ids)
        self.assertIn(picking, invoice.picking_ids)
        # Picking criado com o Partner Shipping da Sale Order
        self.assertEqual(picking.partner_id, sale_order_2.partner_shipping_id)
        # Fatura criada com o Partner Invoice da Sale Order
        self.assertEqual(invoice.partner_id, sale_order_2.partner_invoice_id)
        # Fatura criada com o Partner Shipping usado no Picking
        self.assertEqual(invoice.partner_shipping_id, picking.partner_id)
        # Quando informado usar o Termo de Pagto definido no Pedido de Venda
        # e não o padrão do cliente
        self.assertEqual(invoice.payment_term_id, sale_order_2.payment_term_id)

        # Apenas a Fatura com a linha do produto foi criada
        self.assertEqual(len(invoice.invoice_line_ids), 1)

        # No Pedido de Venda devem existir duas Faturas/Documentos Fiscais
        # de Serviço e Produto
        self.assertEqual(2, sale_order_2.invoice_count)

        # Confirmando a Fatura
        invoice.action_invoice_open()
        self.assertEqual(invoice.state, "open", "Invoice should be in state Open")

        # Validar Atualização da Quantidade Faturada
        for line in sale_order_2.order_line:
            # Apenas a linha de Produto tem a qtd faturada dobrada
            if line.product_id.type == "product":
                # A quantidade Faturada deve ser igual
                # a Quantidade do Produto
                self.assertEqual(line.product_uom_qty, line.qty_invoiced)

        # Checar se os campos das linhas do Pedido de Vendas
        # estão iguais as linhas da Fatura/Invoice.
        sol_fields = [key for key in self.env["sale.order.line"]._fields.keys()]

        acl_fields = [key for key in self.env["account.invoice.line"]._fields.keys()]

        skipped_fields = [
            "id",
            "display_name",
            "state",
            "create_date",
        ]

        common_fields = list(set(acl_fields) & set(sol_fields) - set(skipped_fields))
        invoice_lines = picking.invoice_ids.invoice_line_ids

        for field in common_fields:
            self.assertEqual(
                sale_order_line[field],
                invoice_lines[field],
                "Field %s failed to transfer from "
                "sale.order.line to account.invoice.line" % field,
            )

        # Teste de Retorno
        self.return_wizard = self.stock_return_picking.with_context(
            dict(active_id=picking.id)
        ).create(dict(invoice_state="2binvoiced"))

        result_wizard = self.return_wizard.create_returns()
        self.assertTrue(result_wizard, "Create returns wizard fail.")
        picking_devolution = self.stock_picking.browse(result_wizard.get("res_id"))

        self.assertEqual(picking_devolution.invoice_state, "2binvoiced")
        self.assertTrue(
            picking_devolution.fiscal_operation_id, "Missing Fiscal Operation."
        )
        for line in picking_devolution.move_lines:
            self.assertEqual(line.invoice_state, "2binvoiced")
{'fraction_non_alphanumeric': 0.08499219359875097, 'fraction_numerical': 0.008489461358313818, 'mean_word_length': 2.191187918418185, 'pattern_counts': {'":': 24, '<': 5, '<?xml version=': 0, '>': 5, 'https://': 5, 'lorem ipsum': 0, 'www.': 2, 'xml': 2}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '1073692', 'n_tokens_mistral': 6606, 'n_tokens_neox': 6338, 'n_words': 1355}