<commit_before># Copyright (2021) Binovo IT Human Project SL
# Copyright 2021 Digital5, S.L.
# Copyright 2021 KERNET INTERNET Y NUEVAS TECNOLOGIAS S.L.
# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl).
from odoo import api, exceptions, fields, models, _
from odoo.addons.l10n_es_ticketbai_api.models.ticketbai_invoice import (
    RefundCode,
    RefundType,
)
from odoo.addons.l10n_es_ticketbai_api.models.ticketbai_invoice_customer import (
    TicketBaiCustomerIdType,
)
from odoo.addons.l10n_es_ticketbai_api_batuz.models.lroe_operation import (
    LROEModelEnum,
    LROEOperationEnum,
)
from collections import OrderedDict
import json

LROE_COUNTRY_CODE_MAPPING = {
    "RE": "FR",
    "GP": "FR",
    "MQ": "FR",
    "GF": "FR",
}
LROE_VALID_INVOICE_STATES = ["open", "in_payment", "paid"]
LROE_STATES = [
    ("not_sent", "Not recorded"),
    ("pending", "Registration in LROE planned"),
    ("error", "Registration error"),
    ("recorded", "Recorded"),
    ("recorded_warning", "Recorded with warnings"),
    ("recorded_modified", "Registered in LROE but last modifications not recorded"),
    ("cancel", "Cancel"),
    ("cancel_modified", "Cancelled in LROE but last modifications not recorded"),
]
KEYS_TO_ROUND = [
    "BaseRectificada",
    "CuotaRectificada",
    "CuotaRecargoEquivalencia",
    "ImporteTotalFactura",
    "BaseImponibleACoste",
    "BaseImponible",
    "CuotaIVASoportada",
    "CuotaIVADeducible",
    "ImporteGastoIRPF",
    "ImporteCompensacionREAGYP",
    "TipoRecargoEquivalencia",
    "CuotaImpuesto",
]


def round_by_keys(elem, search_keys, prec=2):
    """This uses ``round`` method directly as if has been tested that Odoo's
    ``float_round`` still returns incorrect amounts for certain values. Try
    3 units x 3,77 €/unit with 10% tax and you will be hit by the error
    (on regular x86 architectures)."""
    if isinstance(elem, dict):
        for key, value in elem.items():
            if key in search_keys:
                elem[key] = str(round(elem[key], prec))
            else:
                round_by_keys(value, search_keys)
    elif isinstance(elem, list):
        for value in elem:
            round_by_keys(value, search_keys)


class AccountInvoice(models.Model):
    _inherit = "account.invoice"

    lroe_response_line_ids = fields.Many2many(
        comodel_name="lroe.operation.response.line",
        compute="_compute_lroe_response_line_ids",
        string="Responses",
    )
    lroe_operation_ids = fields.Many2many(
        comodel_name="lroe.operation",
        relation="account_invoice_lroe_operation_rel",
        column1="invoice_id",
        column2="lroe_operation_id",
        string="LROE Operations",
        copy=False,
    )
    lroe_state = fields.Selection(
        selection=LROE_STATES,
        string="LROE state",
        default="not_sent",
        copy=False,
        help="Indicates the state of this invoice in relation with LROE/Batuz",
    )
    tbai_vat_regime_purchase_key = fields.Many2one(
        comodel_name="tbai.vat.regime.key",
        string="Purchase VAT Regime Key",
        domain=[("type", "=", "purchase")],
        copy=True,
    )
    tbai_vat_regime_purchase_key2 = fields.Many2one(
        comodel_name="tbai.vat.regime.key",
        string="Purchase VAT Regime 2nd Key",
        domain=[("type", "=", "purchase")],
        copy=True,
    )
    tbai_vat_regime_purchase_key3 = fields.Many2one(
        comodel_name="tbai.vat.regime.key",
        string="Purchase VAT Regime 3rd Key",
        domain=[("type", "=", "purchase")],
        copy=True,
    )
    lroe_invoice_dict = fields.Text(
        string="LROE last content sent",
        copy=False,
        readonly=True,
    )

    @api.depends(
        'tbai_invoice_ids',
        'tbai_invoice_ids.state',
        'tbai_cancellation_ids',
        'tbai_cancellation_ids.state')
    def _compute_lroe_response_line_ids(self):
        for record in self:
            response_line_model = self.env['lroe.operation.response.line']
            response_ids = record.tbai_invoice_ids.mapped('tbai_response_ids').ids
            response_ids += record.tbai_cancellation_ids.mapped('tbai_response_ids').ids
            response_lines = response_line_model.search(
                [('tbai_response_id', 'in', response_ids)]
            )
            record.lroe_response_line_ids = [(6, 0, response_lines.ids)]

    @api.model
    def create(self, vals):
        company = self.env["res.company"].browse(
            vals.get("company_id", self.env.user.company_id.id)
        )
        tbai_tax_agency_id = company.tbai_tax_agency_id
        if (
            not company.tbai_enabled
            or not tbai_tax_agency_id
            or tbai_tax_agency_id.id
            != self.env.ref("l10n_es_ticketbai_api_batuz.tbai_tax_agency_bizkaia").id
        ):
            return super().create(vals)
        if "refund" in vals.get("type", ""):
            if not vals.get("tbai_refund_type"):
                vals["tbai_refund_type"] = RefundType.differences.value
            if "tbai_refund_key" not in vals:
                vals["tbai_refund_key"] = RefundCode.R1.value
        if "name" in vals and vals["name"]:
            vals["tbai_description_operation"] = vals["name"]

        invoice = super(AccountInvoice, self).create(vals)
        if vals.get("fiscal_position_id"):
            invoice.onchange_fiscal_position_id_lroe_vat_regime_key()
        return invoice

    @api.onchange("fiscal_position_id")
    def onchange_fiscal_position_id_lroe_vat_regime_key(self):
        if self.fiscal_position_id and ("in" in self.type):
            self.tbai_vat_regime_purchase_key = (
                self.fiscal_position_id.tbai_vat_regime_purchase_key.id
            )
            self.tbai_vat_regime_purchase_key2 = (
                self.fiscal_position_id.tbai_vat_regime_purchase_key2.id
            )
            self.tbai_vat_regime_purchase_key3 = (
                self.fiscal_position_id.tbai_vat_regime_purchase_key3.id
            )

    @api.multi
    def _get_chapter_subchapter(self):
        """ Identificamos el capitulo y subcapitulo """
        self.ensure_one()
        if self.company_id.lroe_model == LROEModelEnum.model_pj_240.value:
            chapter = self.env.ref("l10n_es_ticketbai_api_batuz.lroe_chapter_pj_240_2")
            return chapter.code, False
        elif self.company_id.lroe_model == LROEModelEnum.model_pf_140.value:
            chapter = self.env.ref("l10n_es_ticketbai_api_batuz.lroe_chapter_pf_140_2")
            subchapter = self.env.ref(
                "l10n_es_ticketbai_api_batuz.lroe_subchapter_pf_140_2_1"
            )
            return chapter.code, subchapter.code

    @api.multi
    def _get_lroe_country_code(self):
        self.ensure_one()
        country_code = (
            self.partner_id.commercial_partner_id.country_id.code
            or (self.partner_id.vat or "")[:2]
        ).upper()
        return LROE_COUNTRY_CODE_MAPPING.get(country_code, country_code)

    @api.multi
    def _get_batuz_description(self):
        """Concatenamos la referencia/descripción de la factura con el
        número interno de la misma. De esta forma es más sencillo identificarla
        en el sistema de hacienda."""
        self.ensure_one()
        description = self.number
        if self.name:
            description += ' | ' + self.name
        return description

    def batuz_get_supplier_serie_factura(self):
        """Consultamos a hacienda cómo extraer la serie de una factura de proveedor.
        Al no ser posible en algunos casos, decidimos tomar ciertos caracteres
        como serie y el resto como número.
        Aunque la serie no es obligatoria en facturas recibidas,
        sí lo es en las rectificativas, por lo que es necesario informarla siempre."""
        return self.reference[:3]

    def _get_amount_company_currency(self, amount=0.0):
        """Convertimos el importe amount a la moneda de la compañía."""
        if self.currency_id != self.company_id.currency_id:
            currency = self.currency_id.with_context(
                date=self._get_currency_rate_date(),
                company_id=self.company_id.id
                )
            amount_cur = currency.compute(
                amount,
                self.company_id.currency_id
                )
        else:
            amount_cur = amount
        return amount_cur

    def batuz_get_supplier_num_factura(self):
        return self.reference[3:]

    @api.multi
    def _get_lroe_identifier(self):
        """Get the LROE structure for a partner identifier.
        [('1', 'National'), ('2', 'Intracom'), ('3', 'Export')]
        """
        self.ensure_one()
        res = OrderedDict()
        partner = self.partner_id.commercial_partner_id
        idtype = partner.tbai_partner_idtype
        if partner.vat:
            vat = "".join(e for e in partner.vat if e.isalnum()).upper()
        else:
            vat = "NO_DISPONIBLE"
        country_code = self._get_lroe_country_code()
        if idtype == TicketBaiCustomerIdType.T02.value:
            if country_code != "ES":
                id_type = "06" if vat == "NO_DISPONIBLE" else "02"
                res["IDOtro"] = OrderedDict(
                    [
                        ("CodigoPais", country_code),
                        ("IDType", id_type),
                        ("ID", vat),
                    ]
                )
            else:
                res["NIF"] = vat[2:] if vat.startswith(country_code) else vat
        elif idtype:
            res["IDOtro"] = OrderedDict([
                ("CodigoPais", country_code),
                ("IDType", idtype),
                ("ID", vat),
            ])
        res["ApellidosNombreRazonSocial"] = \
            partner.tbai_get_value_apellidos_nombre_razon_social()
        return res

    @api.multi
    def _is_lroe_simplified_invoice(self):
        """Inheritable method to allow control when an
        invoice are simplified or normal"""
        partner = self.partner_id.commercial_partner_id
        is_simplified = partner.lroe_simplified_invoice
        return is_simplified

    @api.multi
    def _get_operation_date(self):
        """ Inheritable method to allow set operation date of an invoice """
        self.ensure_one()
        return False

    @api.multi
    def _get_last_move_number(self):
        """Inheritable method to allow set the last number of the move when an
        invoice is move summary"""
        self.ensure_one()
        return False

    @api.multi
    def _change_date_format(self, date):
        if not date:
            return False
        datetime_object = fields.Date.from_string(date)
        new_date = datetime_object.strftime("%d-%m-%Y")
        return new_date

    @api.multi
    def _get_lroe_invoice_header(self):
        self.ensure_one()
        invoice_date = self._change_date_format(self.date_invoice)
        operation_date = self._change_date_format(self._get_operation_date())
        reception_date = self._change_date_format(self.date)
        tipo_factura = "F2" if self._is_lroe_simplified_invoice() else "F1"
        last_number = self._get_last_move_number()
        header = OrderedDict()
        header["SerieFactura"] = self.batuz_get_supplier_serie_factura()[:20]
        header["NumFactura"] = self.batuz_get_supplier_num_factura()[:20]
        if last_number:
            # NumFacturaFin = número de la factura que identifica a la
            #  última factura cuando se trata de un asiento resumen de facturas
            header["NumFacturaFin"] = last_number[:20]
        header["FechaExpedicionFactura"] = invoice_date
        if operation_date:
            header["FechaOperacion"] = operation_date
        header["FechaRecepcion"] = reception_date
        header["TipoFactura"] = tipo_factura
        if self.type == "in_refund" and self.refund_invoice_id:
            origin = self.refund_invoice_id
            origin_date = self._change_date_format(self.refund_invoice_id.date_invoice)
            header["FacturaRectificativa"] = OrderedDict(
                [
                    ("Codigo", self.tbai_refund_key),
                    ("Tipo", self.tbai_refund_type),
                ]
            )
            if self.tbai_refund_type == "S":
                header["FacturaRectificativa"]["ImporteRectificacionSustitutiva"] = \
                    OrderedDict([
                        ("BaseRectificada", abs(origin.amount_untaxed_signed)),
                        ("CuotaRectificada", abs(
                            origin._get_amount_company_currency(origin.amount_total) -
                            origin.amount_untaxed_signed
                        )),
                        # (CuotaRecargoRectificada, False),
                    ])
            header["FacturasRectificadasSustituidas"] = OrderedDict([
                ("IDFacturaRectificadaSustituida",
                    OrderedDict([
                        ("SerieFactura",
                            origin.batuz_get_supplier_serie_factura()[:20]),
                        ("NumFactura", origin.batuz_get_supplier_num_factura()[:20]),
                        ("FechaExpedicionFactura", origin_date)
                    ])),
            ])
        return header

    @api.multi
    def _get_lroe_purchase_key_codes(self):
        self.ensure_one()
        regime_keys = (
            self.tbai_vat_regime_purchase_key
            | self.tbai_vat_regime_purchase_key2
            | self.tbai_vat_regime_purchase_key3
        )
        return regime_keys.mapped("code")

    def _get_lroe_claves(self):
        res = {"IDClave": []}
        keys = self._get_lroe_purchase_key_codes()
        for key in keys:
            res["IDClave"].append({"ClaveRegimenIvaOpTrascendencia": key})
        return res

    @api.multi
    def _get_lroe_taxes_map(self, codes):
        """Return the codes that correspond to that LROE map line codes.

        :param self: Invoice record
        :param codes: List of code strings to get the mapping.
        :return: Recordset with the corresponding codes
        """
        self.ensure_one()
        tbai_maps = self.env["tbai.tax.map"].search([("code", "in", codes)])
        tax_templates = tbai_maps.mapped("tax_template_ids")
        return self.env["l10n.es.aeat.report"].get_taxes_from_templates(tax_templates)

    @api.multi
    def _get_lroe_sign(self):
        self.ensure_one()
        return -1.0 if self.tbai_refund_type == "I" and "refund" in self.type else 1.0

    @api.multi
    def _get_lroe_tax_line_req(self, tax):
        """Get the invoice tax line for 'Recargo equivalencia'.

        :param self: Single invoice record.
        :param tax: Initial tax for searching for the RE twin tax.
        :return: Invoice tax line (if any) for the correspondent RE tax.
        """
        self.ensure_one()
        taxes_re = self._get_lroe_taxes_map(["RE"])
        inv_lines = self.invoice_line_ids.filtered(
            lambda x: tax in x.mapped("invoice_line_tax_ids")
        )
        re_tax = inv_lines.mapped("invoice_line_tax_ids").filtered(
            lambda x: x in taxes_re
        )
        if len(re_tax) > 1:
            raise exceptions.UserError(
                _("There's a mismatch in taxes for RE. Check them.")
            )
        return self.tax_line_ids.filtered(lambda x: x.tax_id == re_tax)

    @api.model
    def _get_lroe_tax_dict(self, tax_line, sign, deductible=True):
        """Get the LROE tax dictionary for the passed tax line.<commit_msg>[FIX] l10n_es_ticketbai_batuz: Se buscan los impuestos de la compañía de la factura en vez de los del usuario.<commit_after># Copyright (2021) Binovo IT Human Project SL
# Copyright 2021 Digital5, S.L.
# Copyright 2021 KERNET INTERNET Y NUEVAS TECNOLOGIAS S.L.
# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl).
from odoo import api, exceptions, fields, models, _
from odoo.addons.l10n_es_ticketbai_api.models.ticketbai_invoice import (
    RefundCode,
    RefundType,
)
from odoo.addons.l10n_es_ticketbai_api.models.ticketbai_invoice_customer import (
    TicketBaiCustomerIdType,
)
from odoo.addons.l10n_es_ticketbai_api_batuz.models.lroe_operation import (
    LROEModelEnum,
    LROEOperationEnum,
)
from collections import OrderedDict
import json

LROE_COUNTRY_CODE_MAPPING = {
    "RE": "FR",
    "GP": "FR",
    "MQ": "FR",
    "GF": "FR",
}
LROE_VALID_INVOICE_STATES = ["open", "in_payment", "paid"]
LROE_STATES = [
    ("not_sent", "Not recorded"),
    ("pending", "Registration in LROE planned"),
    ("error", "Registration error"),
    ("recorded", "Recorded"),
    ("recorded_warning", "Recorded with warnings"),
    ("recorded_modified", "Registered in LROE but last modifications not recorded"),
    ("cancel", "Cancel"),
    ("cancel_modified", "Cancelled in LROE but last modifications not recorded"),
]
KEYS_TO_ROUND = [
    "BaseRectificada",
    "CuotaRectificada",
    "CuotaRecargoEquivalencia",
    "ImporteTotalFactura",
    "BaseImponibleACoste",
    "BaseImponible",
    "CuotaIVASoportada",
    "CuotaIVADeducible",
    "ImporteGastoIRPF",
    "ImporteCompensacionREAGYP",
    "TipoRecargoEquivalencia",
    "CuotaImpuesto",
]


def round_by_keys(elem, search_keys, prec=2):
    """This uses ``round`` method directly as if has been tested that Odoo's
    ``float_round`` still returns incorrect amounts for certain values. Try
    3 units x 3,77 €/unit with 10% tax and you will be hit by the error
    (on regular x86 architectures)."""
    if isinstance(elem, dict):
        for key, value in elem.items():
            if key in search_keys:
                elem[key] = str(round(elem[key], prec))
            else:
                round_by_keys(value, search_keys)
    elif isinstance(elem, list):
        for value in elem:
            round_by_keys(value, search_keys)


class AccountInvoice(models.Model):
    _inherit = "account.invoice"

    lroe_response_line_ids = fields.Many2many(
        comodel_name="lroe.operation.response.line",
        compute="_compute_lroe_response_line_ids",
        string="Responses",
    )
    lroe_operation_ids = fields.Many2many(
        comodel_name="lroe.operation",
        relation="account_invoice_lroe_operation_rel",
        column1="invoice_id",
        column2="lroe_operation_id",
        string="LROE Operations",
        copy=False,
    )
    lroe_state = fields.Selection(
        selection=LROE_STATES,
        string="LROE state",
        default="not_sent",
        copy=False,
        help="Indicates the state of this invoice in relation with LROE/Batuz",
    )
    tbai_vat_regime_purchase_key = fields.Many2one(
        comodel_name="tbai.vat.regime.key",
        string="Purchase VAT Regime Key",
        domain=[("type", "=", "purchase")],
        copy=True,
    )
    tbai_vat_regime_purchase_key2 = fields.Many2one(
        comodel_name="tbai.vat.regime.key",
        string="Purchase VAT Regime 2nd Key",
        domain=[("type", "=", "purchase")],
        copy=True,
    )
    tbai_vat_regime_purchase_key3 = fields.Many2one(
        comodel_name="tbai.vat.regime.key",
        string="Purchase VAT Regime 3rd Key",
        domain=[("type", "=", "purchase")],
        copy=True,
    )
    lroe_invoice_dict = fields.Text(
        string="LROE last content sent",
        copy=False,
        readonly=True,
    )

    @api.depends(
        'tbai_invoice_ids',
        'tbai_invoice_ids.state',
        'tbai_cancellation_ids',
        'tbai_cancellation_ids.state')
    def _compute_lroe_response_line_ids(self):
        for record in self:
            response_line_model = self.env['lroe.operation.response.line']
            response_ids = record.tbai_invoice_ids.mapped('tbai_response_ids').ids
            response_ids += record.tbai_cancellation_ids.mapped('tbai_response_ids').ids
            response_lines = response_line_model.search(
                [('tbai_response_id', 'in', response_ids)]
            )
            record.lroe_response_line_ids = [(6, 0, response_lines.ids)]

    @api.model
    def create(self, vals):
        company = self.env["res.company"].browse(
            vals.get("company_id", self.env.user.company_id.id)
        )
        tbai_tax_agency_id = company.tbai_tax_agency_id
        if (
            not company.tbai_enabled
            or not tbai_tax_agency_id
            or tbai_tax_agency_id.id
            != self.env.ref("l10n_es_ticketbai_api_batuz.tbai_tax_agency_bizkaia").id
        ):
            return super().create(vals)
        if "refund" in vals.get("type", ""):
            if not vals.get("tbai_refund_type"):
                vals["tbai_refund_type"] = RefundType.differences.value
            if "tbai_refund_key" not in vals:
                vals["tbai_refund_key"] = RefundCode.R1.value
        if "name" in vals and vals["name"]:
            vals["tbai_description_operation"] = vals["name"]

        invoice = super(AccountInvoice, self).create(vals)
        if vals.get("fiscal_position_id"):
            invoice.onchange_fiscal_position_id_lroe_vat_regime_key()
        return invoice

    @api.onchange("fiscal_position_id")
    def onchange_fiscal_position_id_lroe_vat_regime_key(self):
        if self.fiscal_position_id and ("in" in self.type):
            self.tbai_vat_regime_purchase_key = (
                self.fiscal_position_id.tbai_vat_regime_purchase_key.id
            )
            self.tbai_vat_regime_purchase_key2 = (
                self.fiscal_position_id.tbai_vat_regime_purchase_key2.id
            )
            self.tbai_vat_regime_purchase_key3 = (
                self.fiscal_position_id.tbai_vat_regime_purchase_key3.id
            )

    @api.multi
    def _get_chapter_subchapter(self):
        """ Identificamos el capitulo y subcapitulo """
        self.ensure_one()
        if self.company_id.lroe_model == LROEModelEnum.model_pj_240.value:
            chapter = self.env.ref("l10n_es_ticketbai_api_batuz.lroe_chapter_pj_240_2")
            return chapter.code, False
        elif self.company_id.lroe_model == LROEModelEnum.model_pf_140.value:
            chapter = self.env.ref("l10n_es_ticketbai_api_batuz.lroe_chapter_pf_140_2")
            subchapter = self.env.ref(
                "l10n_es_ticketbai_api_batuz.lroe_subchapter_pf_140_2_1"
            )
            return chapter.code, subchapter.code

    @api.multi
    def _get_lroe_country_code(self):
        self.ensure_one()
        country_code = (
            self.partner_id.commercial_partner_id.country_id.code
            or (self.partner_id.vat or "")[:2]
        ).upper()
        return LROE_COUNTRY_CODE_MAPPING.get(country_code, country_code)

    @api.multi
    def _get_batuz_description(self):
        """Concatenamos la referencia/descripción de la factura con el
        número interno de la misma. De esta forma es más sencillo identificarla
        en el sistema de hacienda."""
        self.ensure_one()
        description = self.number
        if self.name:
            description += ' | ' + self.name
        return description

    def batuz_get_supplier_serie_factura(self):
        """Consultamos a hacienda cómo extraer la serie de una factura de proveedor.
        Al no ser posible en algunos casos, decidimos tomar ciertos caracteres
        como serie y el resto como número.
        Aunque la serie no es obligatoria en facturas recibidas,
        sí lo es en las rectificativas, por lo que es necesario informarla siempre."""
        return self.reference[:3]

    def _get_amount_company_currency(self, amount=0.0):
        """Convertimos el importe amount a la moneda de la compañía."""
        if self.currency_id != self.company_id.currency_id:
            currency = self.currency_id.with_context(
                date=self._get_currency_rate_date(),
                company_id=self.company_id.id
                )
            amount_cur = currency.compute(
                amount,
                self.company_id.currency_id
                )
        else:
            amount_cur = amount
        return amount_cur

    def batuz_get_supplier_num_factura(self):
        return self.reference[3:]

    @api.multi
    def _get_lroe_identifier(self):
        """Get the LROE structure for a partner identifier.
        [('1', 'National'), ('2', 'Intracom'), ('3', 'Export')]
        """
        self.ensure_one()
        res = OrderedDict()
        partner = self.partner_id.commercial_partner_id
        idtype = partner.tbai_partner_idtype
        if partner.vat:
            vat = "".join(e for e in partner.vat if e.isalnum()).upper()
        else:
            vat = "NO_DISPONIBLE"
        country_code = self._get_lroe_country_code()
        if idtype == TicketBaiCustomerIdType.T02.value:
            if country_code != "ES":
                id_type = "06" if vat == "NO_DISPONIBLE" else "02"
                res["IDOtro"] = OrderedDict(
                    [
                        ("CodigoPais", country_code),
                        ("IDType", id_type),
                        ("ID", vat),
                    ]
                )
            else:
                res["NIF"] = vat[2:] if vat.startswith(country_code) else vat
        elif idtype:
            res["IDOtro"] = OrderedDict([
                ("CodigoPais", country_code),
                ("IDType", idtype),
                ("ID", vat),
            ])
        res["ApellidosNombreRazonSocial"] = \
            partner.tbai_get_value_apellidos_nombre_razon_social()
        return res

    @api.multi
    def _is_lroe_simplified_invoice(self):
        """Inheritable method to allow control when an
        invoice are simplified or normal"""
        partner = self.partner_id.commercial_partner_id
        is_simplified = partner.lroe_simplified_invoice
        return is_simplified

    @api.multi
    def _get_operation_date(self):
        """ Inheritable method to allow set operation date of an invoice """
        self.ensure_one()
        return False

    @api.multi
    def _get_last_move_number(self):
        """Inheritable method to allow set the last number of the move when an
        invoice is move summary"""
        self.ensure_one()
        return False

    @api.multi
    def _change_date_format(self, date):
        if not date:
            return False
        datetime_object = fields.Date.from_string(date)
        new_date = datetime_object.strftime("%d-%m-%Y")
        return new_date

    @api.multi
    def _get_lroe_invoice_header(self):
        self.ensure_one()
        invoice_date = self._change_date_format(self.date_invoice)
        operation_date = self._change_date_format(self._get_operation_date())
        reception_date = self._change_date_format(self.date)
        tipo_factura = "F2" if self._is_lroe_simplified_invoice() else "F1"
        last_number = self._get_last_move_number()
        header = OrderedDict()
        header["SerieFactura"] = self.batuz_get_supplier_serie_factura()[:20]
        header["NumFactura"] = self.batuz_get_supplier_num_factura()[:20]
        if last_number:
            # NumFacturaFin = número de la factura que identifica a la
            #  última factura cuando se trata de un asiento resumen de facturas
            header["NumFacturaFin"] = last_number[:20]
        header["FechaExpedicionFactura"] = invoice_date
        if operation_date:
            header["FechaOperacion"] = operation_date
        header["FechaRecepcion"] = reception_date
        header["TipoFactura"] = tipo_factura
        if self.type == "in_refund" and self.refund_invoice_id:
            origin = self.refund_invoice_id
            origin_date = self._change_date_format(self.refund_invoice_id.date_invoice)
            header["FacturaRectificativa"] = OrderedDict(
                [
                    ("Codigo", self.tbai_refund_key),
                    ("Tipo", self.tbai_refund_type),
                ]
            )
            if self.tbai_refund_type == "S":
                header["FacturaRectificativa"]["ImporteRectificacionSustitutiva"] = \
                    OrderedDict([
                        ("BaseRectificada", abs(origin.amount_untaxed_signed)),
                        ("CuotaRectificada", abs(
                            origin._get_amount_company_currency(origin.amount_total) -
                            origin.amount_untaxed_signed
                        )),
                        # (CuotaRecargoRectificada, False),
                    ])
            header["FacturasRectificadasSustituidas"] = OrderedDict([
                ("IDFacturaRectificadaSustituida",
                    OrderedDict([
                        ("SerieFactura",
                            origin.batuz_get_supplier_serie_factura()[:20]),
                        ("NumFactura", origin.batuz_get_supplier_num_factura()[:20]),
                        ("FechaExpedicionFactura", origin_date)
                    ])),
            ])
        return header

    @api.multi
    def _get_lroe_purchase_key_codes(self):
        self.ensure_one()
        regime_keys = (
            self.tbai_vat_regime_purchase_key
            | self.tbai_vat_regime_purchase_key2
            | self.tbai_vat_regime_purchase_key3
        )
        return regime_keys.mapped("code")

    def _get_lroe_claves(self):
        res = {"IDClave": []}
        keys = self._get_lroe_purchase_key_codes()
        for key in keys:
            res["IDClave"].append({"ClaveRegimenIvaOpTrascendencia": key})
        return res

    @api.multi
    def _get_lroe_taxes_map(self, codes):
        """Return the codes that correspond to that LROE map line codes.

        :param self: Invoice record
        :param codes: List of code strings to get the mapping.
        :return: Recordset with the corresponding codes
        """
        self.ensure_one()
        tbai_maps = self.env["tbai.tax.map"].search([("code", "in", codes)])
        tax_templates = tbai_maps.mapped("tax_template_ids")
        return self.env["l10n.es.aeat.report"].new(
            {'company_id': self.company_id.id}
        ).get_taxes_from_templates(tax_templates)

    @api.multi
    def _get_lroe_sign(self):
        self.ensure_one()
        return -1.0 if self.tbai_refund_type == "I" and "refund" in self.type else 1.0

    @api.multi
    def _get_lroe_tax_line_req(self, tax):
        """Get the invoice tax line for 'Recargo equivalencia'.

        :param self: Single invoice record.
        :param tax: Initial tax for searching for the RE twin tax.
        :return: Invoice tax line (if any) for the correspondent RE tax.
        """
        self.ensure_one()
        taxes_re = self._get_lroe_taxes_map(["RE"])
        inv_lines = self.invoice_line_ids.filtered(
            lambda x: tax in x.mapped("invoice_line_tax_ids")
        )
        re_tax = inv_lines.mapped("invoice_line_tax_ids").filtered(
            lambda x: x in taxes_re
        )
        if len(re_tax) > 1:
            raise exceptions.UserError(
                _("There's a mismatch in taxes for RE. Check them.")
            )
        return self.tax_line_ids.filtered(lambda x: x.tax_id == re_tax)

    @api.model
    def _get_lroe_tax_dict(self, tax_line, sign, deductible=True):
        """Get the LROE tax dictionary for the passed tax line.
{'fraction_non_alphanumeric': 0.09821913866419414, 'fraction_numerical': 0.007161812194264156, 'mean_word_length': 2.3270928624614404, 'pattern_counts': {'":': 16, '<': 3, '<?xml version=': 0, '>': 5, 'https://': 2, 'lorem ipsum': 0, 'www.': 2, 'xml': 0}, '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': '2006356', 'n_tokens_mistral': 10239, 'n_tokens_neox': 9823, 'n_words': 1925}