Skip to content

Commit

Permalink
l10n_br_cte_spec: monkey patch to run tests
Browse files Browse the repository at this point in the history
use to 2 monkey patches to be able to run the XML import tests
against AbstractModel CTe models
rvalyi committed Jan 16, 2025
1 parent 226e8a4 commit 0b23d72
Showing 1 changed file with 135 additions and 3 deletions.
138 changes: 135 additions & 3 deletions l10n_br_cte_spec/tests/test_cte_import.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
# Copyright 2020 Akretion - Raphael Valyi <raphael.valyi@akretion.com>
# License LGPL-3.0 or later (https://www.gnu.org/licenses/lgpl-3.0.en.html).
# flake8: noqa: C901

import re
from datetime import datetime
@@ -8,8 +9,11 @@
import pkg_resources
from nfelib.cte.bindings.v4_0.cte_v4_00 import Tcte

from odoo import api
from odoo import api, fields, models
from odoo.fields import Command
from odoo.models import BaseModel, NewId
from odoo.tests import TransactionCase
from odoo.tools import OrderedSet

from ..models import spec_mixin

@@ -115,7 +119,7 @@ def build_attrs_fake(self, node, create_m2o=False):

@api.model
def match_or_create_m2o_fake(self, comodel, new_value, create_m2o=False):
return comodel.new(new_value).id
return comodel.new(new_value)._ids[0]


# spec_mixin.CteSpecMixin._update_cache = _update_cache
@@ -124,7 +128,135 @@ def match_or_create_m2o_fake(self, comodel, new_value, create_m2o=False):
spec_mixin.CteSpecMixin.match_or_create_m2o_fake = match_or_create_m2o_fake


class CTeImportTest(TransactionCase):
# in version 12, 13 and 14, the code above would properly allow loading NFe XMLs
# as an Odoo AbstractModel structure for minimal testing of these structures.
# However in version Odoo 15 and 16 (at least), the ORM has trouble when
# doing env["some.model"].new(vals) if some.model is an AbstractModel like the
# models in this module. This is strange as new is available for AbstractModel...
# Anyway, only 2 methods are problematic for what we want to test here so
# a workaround is to monkey patch them as done in the next lines.
# Note that we only want test loading the XML data structure here,
# we remove the monkey patch after the tests and even if it's a dirty
# workaround it doesn't matter much because in the more completes tests in l10n_br_nfe
# we the models are made concrete so this problem does not occur anymore.
def fields_convert_to_cache(self, value, record, validate=True):
"""
A monkey patched version of convert_to_cache that works with
new instances of AbstractModel. Look at the lines after
# THE NEXT LINE WAS PATCHED:
and # THE NEXT 4 LINES WERE PATCHED:
to see the change.
"""
# cache format: tuple(ids)
if isinstance(value, BaseModel):
if validate and value._name != self.comodel_name:
raise ValueError("Wrong value for %s: %s" % (self, value))
ids = value._ids

Check warning on line 154 in l10n_br_cte_spec/tests/test_cte_import.py

Codecov / codecov/patch

l10n_br_cte_spec/tests/test_cte_import.py#L153-L154

Added lines #L153 - L154 were not covered by tests
if record and not record.id:
# x2many field value of new record is new records
ids = tuple(it and NewId(it) for it in ids)
return ids

Check warning on line 158 in l10n_br_cte_spec/tests/test_cte_import.py

Codecov / codecov/patch

l10n_br_cte_spec/tests/test_cte_import.py#L158

Added line #L158 was not covered by tests
elif isinstance(value, (list, tuple)):
# value is a list/tuple of commands, dicts or record ids
comodel = record.env[self.comodel_name]
# if record is new, the field's value is new records
# THE NEXT LINE WAS PATCHED:
if record and hasattr(record, "id") and not record.id:
browse = lambda it: comodel.browse([it and NewId(it)])
else:
browse = comodel.browse
# determine the value ids
ids = OrderedSet(record[self.name]._ids if validate else ())
# modify ids with the commands
for command in value:
if isinstance(command, (tuple, list)):
if command[0] == Command.CREATE:
# THE NEXT 4 LINES WERE PATCHED:
if hasattr(comodel.new(command[2], ref=command[1]), "id"):
ids.add(comodel.new(command[2], ref=command[1]).id)

Check warning on line 176 in l10n_br_cte_spec/tests/test_cte_import.py

Codecov / codecov/patch

l10n_br_cte_spec/tests/test_cte_import.py#L176

Added line #L176 was not covered by tests
else:
ids.add(comodel.new(command[2], ref=command[1])._ids[0])
elif command[0] == Command.UPDATE:
line = browse(command[1])

Check warning on line 180 in l10n_br_cte_spec/tests/test_cte_import.py

Codecov / codecov/patch

l10n_br_cte_spec/tests/test_cte_import.py#L180

Added line #L180 was not covered by tests
if validate:
line.update(command[2])

Check warning on line 182 in l10n_br_cte_spec/tests/test_cte_import.py

Codecov / codecov/patch

l10n_br_cte_spec/tests/test_cte_import.py#L182

Added line #L182 was not covered by tests
else:
line._update_cache(command[2], validate=False)
ids.add(line.id)

Check warning on line 185 in l10n_br_cte_spec/tests/test_cte_import.py

Codecov / codecov/patch

l10n_br_cte_spec/tests/test_cte_import.py#L184-L185

Added lines #L184 - L185 were not covered by tests
elif command[0] in (Command.DELETE, Command.UNLINK):
ids.discard(browse(command[1]).id)

Check warning on line 187 in l10n_br_cte_spec/tests/test_cte_import.py

Codecov / codecov/patch

l10n_br_cte_spec/tests/test_cte_import.py#L187

Added line #L187 was not covered by tests
elif command[0] == Command.LINK:
ids.add(browse(command[1]).id)

Check warning on line 189 in l10n_br_cte_spec/tests/test_cte_import.py

Codecov / codecov/patch

l10n_br_cte_spec/tests/test_cte_import.py#L189

Added line #L189 was not covered by tests
elif command[0] == Command.CLEAR:
ids.clear()

Check warning on line 191 in l10n_br_cte_spec/tests/test_cte_import.py

Codecov / codecov/patch

l10n_br_cte_spec/tests/test_cte_import.py#L191

Added line #L191 was not covered by tests
elif command[0] == Command.SET:
ids = OrderedSet(browse(it).id for it in command[2])
elif isinstance(command, dict):
ids.add(comodel.new(command).id)

Check warning on line 195 in l10n_br_cte_spec/tests/test_cte_import.py

Codecov / codecov/patch

l10n_br_cte_spec/tests/test_cte_import.py#L195

Added line #L195 was not covered by tests
else:
ids.add(browse(command).id)

Check warning on line 197 in l10n_br_cte_spec/tests/test_cte_import.py

Codecov / codecov/patch

l10n_br_cte_spec/tests/test_cte_import.py#L197

Added line #L197 was not covered by tests
# return result as a tuple
return tuple(ids)
elif not value:
return ()
raise ValueError("Wrong value for %s: %s" % (self, value))

Check warning on line 202 in l10n_br_cte_spec/tests/test_cte_import.py

Codecov / codecov/patch

l10n_br_cte_spec/tests/test_cte_import.py#L201-L202

Added lines #L201 - L202 were not covered by tests


fields_convert_to_cache._original_method = fields._RelationalMulti.convert_to_cache
fields._RelationalMulti.convert_to_cache = fields_convert_to_cache


def models_update_cache(self, values, validate=True):
"""
A monkey patched version of _update_cache that works with
new instances of AbstractModel. Look at the lines after
# THE NEXT LINE WAS PATCHED:
to see the change.
"""
self.ensure_one()
cache = self.env.cache
fields = self._fields
try:
field_values = [(fields[name], value) for name, value in values.items()]
except KeyError as e:
raise ValueError("Invalid field %r on model %r" % (e.args[0], self._name))

Check warning on line 222 in l10n_br_cte_spec/tests/test_cte_import.py

Codecov / codecov/patch

l10n_br_cte_spec/tests/test_cte_import.py#L221-L222

Added lines #L221 - L222 were not covered by tests
# convert monetary fields after other columns for correct value rounding
for field, value in sorted(field_values, key=lambda item: item[0].write_sequence):
cache.set(self, field, field.convert_to_cache(value, self, validate))
# set inverse fields on new records in the comodel
if field.relational:
# THE NEXT LINE WAS PATCHED:
inv_recs = self[field.name].filtered(
lambda r: hasattr(r, "id") and not r.id
)
if not inv_recs:
continue
for invf in self.pool.field_inverses[field]:
# DLE P98: `test_40_new_fields`
# /home/dle/src/odoo/master-nochange-fp/odoo/addons/test_new_api/tests/test_new_fields.py
# Be careful to not break `test_onchange_taxes_1`, `test_onchange_taxes_2`, `test_onchange_taxes_3`
# If you attempt to find a better solution
for inv_rec in inv_recs:
if not cache.contains(inv_rec, invf):
val = invf.convert_to_cache(self, inv_rec, validate=False)
cache.set(inv_rec, invf, val)

Check warning on line 242 in l10n_br_cte_spec/tests/test_cte_import.py

Codecov / codecov/patch

l10n_br_cte_spec/tests/test_cte_import.py#L241-L242

Added lines #L241 - L242 were not covered by tests
else:
invf._update(inv_rec, self)

Check warning on line 244 in l10n_br_cte_spec/tests/test_cte_import.py

Codecov / codecov/patch

l10n_br_cte_spec/tests/test_cte_import.py#L244

Added line #L244 was not covered by tests


models_update_cache._original_method = models.BaseModel._update_cache
models.BaseModel._update_cache = models_update_cache


class NFeImportTest(TransactionCase):
@classmethod
def tearDownClass(cls):
fields._RelationalMulti.convert_to_cache = (
fields_convert_to_cache._original_method
)
models.BaseModel._update_cache = models_update_cache._original_method
super().tearDownClass()

def test_import_cte(self):
res_items = (
"cte",

0 comments on commit 0b23d72

Please sign in to comment.