Public release from ruodoo-project: 19.0 - 2026-05-31 21:19:12 UTC
This commit is contained in:
2
dadata_connector/tests/__init__.py
Normal file
2
dadata_connector/tests/__init__.py
Normal file
@ -0,0 +1,2 @@
|
||||
from . import test_res_partner
|
||||
from . import test_wizard
|
||||
99
dadata_connector/tests/common.py
Normal file
99
dadata_connector/tests/common.py
Normal file
@ -0,0 +1,99 @@
|
||||
from odoo.tests.common import TransactionCase
|
||||
|
||||
|
||||
# Minimal DaData API response fixture for a legal entity (ООО)
|
||||
DADATA_LEGAL_RESPONSE = [
|
||||
{
|
||||
"data": {
|
||||
"type": "LEGAL",
|
||||
"inn": "7707083893",
|
||||
"ogrn": "1027700132195",
|
||||
"kpp": "773601001",
|
||||
"okpo": "00032537",
|
||||
"okved": "64.19",
|
||||
"opf": {"code": "12300"},
|
||||
"name": {
|
||||
"short_with_opf": "ПАО Сбербанк",
|
||||
"full_with_opf": "Публичное акционерное общество «Сбербанк России»",
|
||||
},
|
||||
"state": {"status": "ACTIVE"},
|
||||
"address": {
|
||||
"unrestricted_value": "117997, г Москва, ул Вавилова, д 19",
|
||||
"data": {
|
||||
"country_iso_code": "RU",
|
||||
"region_iso_code": "RU-MOW",
|
||||
"city": "Москва",
|
||||
"street_with_type": "ул Вавилова",
|
||||
"house_type_full": "дом",
|
||||
"house": "19",
|
||||
"flat_type_full": None,
|
||||
"flat": None,
|
||||
"postal_code": "117997",
|
||||
},
|
||||
},
|
||||
"documents": {
|
||||
"fts_registration": {
|
||||
"series": "77",
|
||||
"number": "004599035",
|
||||
"issue_date": "2002-08-16",
|
||||
}
|
||||
},
|
||||
"management": {
|
||||
"name": "ГРЕФ ГЕРМАН ОСКАРОВИЧ",
|
||||
"post": "ПРЕЗИДЕНТ, ПРЕДСЕДАТЕЛЬ ПРАВЛЕНИЯ",
|
||||
},
|
||||
}
|
||||
}
|
||||
]
|
||||
|
||||
# Minimal DaData API response fixture for an individual entrepreneur (ИП)
|
||||
DADATA_INDIVIDUAL_RESPONSE = [
|
||||
{
|
||||
"data": {
|
||||
"type": "INDIVIDUAL",
|
||||
"inn": "500100732259",
|
||||
"ogrn": "304500116000157",
|
||||
"kpp": None,
|
||||
"okpo": "0107544",
|
||||
"okved": "47.91",
|
||||
"opf": {"code": "50102"},
|
||||
"fio": {
|
||||
"surname": "ИВАНОВ",
|
||||
"name": "ИВАН",
|
||||
"patronymic": "ИВАНОВИЧ",
|
||||
},
|
||||
"state": {"status": "ACTIVE"},
|
||||
"address": {
|
||||
"unrestricted_value": "141001, Московская обл, г Мытищи",
|
||||
"data": {
|
||||
"country_iso_code": "RU",
|
||||
"region_iso_code": "RU-MOS",
|
||||
"city": "Мытищи",
|
||||
"street_with_type": None,
|
||||
"house_type_full": None,
|
||||
"house": None,
|
||||
"flat_type_full": None,
|
||||
"flat": None,
|
||||
"postal_code": "141001",
|
||||
},
|
||||
},
|
||||
"documents": {"fts_registration": None},
|
||||
"management": None,
|
||||
}
|
||||
}
|
||||
]
|
||||
|
||||
|
||||
class DadataConnectorCommon(TransactionCase):
|
||||
"""Base class for dadata_connector tests."""
|
||||
|
||||
@classmethod
|
||||
def setUpClass(cls):
|
||||
super().setUpClass()
|
||||
cls.partner = cls.env["res.partner"].create({
|
||||
"name": "Test Partner",
|
||||
"is_company": True,
|
||||
})
|
||||
cls.env["ir.config_parameter"].sudo().set_param(
|
||||
"dadata_connector.dadata_token", "test_token_123"
|
||||
)
|
||||
182
dadata_connector/tests/test_res_partner.py
Normal file
182
dadata_connector/tests/test_res_partner.py
Normal file
@ -0,0 +1,182 @@
|
||||
from unittest.mock import MagicMock, patch
|
||||
|
||||
from odoo.exceptions import ValidationError
|
||||
from odoo.tests.common import tagged
|
||||
|
||||
from .common import (
|
||||
DADATA_INDIVIDUAL_RESPONSE,
|
||||
DADATA_LEGAL_RESPONSE,
|
||||
DadataConnectorCommon,
|
||||
)
|
||||
|
||||
|
||||
@tagged("post_install", "-at_install")
|
||||
class TestResPartnerDadata(DadataConnectorCommon):
|
||||
"""Tests for res.partner DaData integration methods."""
|
||||
|
||||
# ------------------------------------------------------------------
|
||||
# get_dadata_token
|
||||
# ------------------------------------------------------------------
|
||||
|
||||
def test_get_dadata_token_returns_token(self):
|
||||
"""Token is returned when the system parameter is set."""
|
||||
token = self.partner.get_dadata_token()
|
||||
self.assertEqual(token, "test_token_123")
|
||||
|
||||
def test_get_dadata_token_raises_when_missing(self):
|
||||
"""ValidationError is raised when token is not configured."""
|
||||
self.env["ir.config_parameter"].sudo().set_param(
|
||||
"dadata_connector.dadata_token", ""
|
||||
)
|
||||
with self.assertRaises(ValidationError):
|
||||
self.partner.get_dadata_token()
|
||||
|
||||
# ------------------------------------------------------------------
|
||||
# _parse_dadata_response — legal entity
|
||||
# ------------------------------------------------------------------
|
||||
|
||||
def test_parse_legal_entity_basic_fields(self):
|
||||
"""Parsing a legal entity response fills basic partner fields."""
|
||||
wizard_data, result = self.partner._parse_dadata_response(DADATA_LEGAL_RESPONSE)
|
||||
|
||||
self.assertEqual(result["vat"], "7707083893")
|
||||
self.assertEqual(result["ogrn"], "1027700132195")
|
||||
self.assertEqual(result["kpp"], "773601001")
|
||||
self.assertEqual(result["okpo"], "00032537")
|
||||
self.assertEqual(result["arceat"], "64.19")
|
||||
self.assertEqual(result["name"], "ПАО Сбербанк")
|
||||
self.assertEqual(result["city"], "Москва")
|
||||
self.assertEqual(result["zip"], "117997")
|
||||
self.assertEqual(result["street"], "ул Вавилова, дом, 19")
|
||||
|
||||
def test_parse_legal_entity_company_form(self):
|
||||
"""OPF code 12300 maps to 'plc' company form."""
|
||||
_, result = self.partner._parse_dadata_response(DADATA_LEGAL_RESPONSE)
|
||||
self.assertEqual(result["company_form"], "plc")
|
||||
|
||||
def test_parse_legal_entity_wizard_data(self):
|
||||
"""Wizard data contains status, organization_type, name and address."""
|
||||
wizard_data, _ = self.partner._parse_dadata_response(DADATA_LEGAL_RESPONSE)
|
||||
|
||||
self.assertEqual(wizard_data["status"], "active")
|
||||
self.assertEqual(wizard_data["organization_type"], "legal")
|
||||
self.assertEqual(wizard_data["name"], "ПАО Сбербанк")
|
||||
self.assertIn("Вавилова", wizard_data["full_address"])
|
||||
|
||||
def test_parse_legal_entity_management(self):
|
||||
"""Management data is extracted when present."""
|
||||
_, result = self.partner._parse_dadata_response(DADATA_LEGAL_RESPONSE)
|
||||
|
||||
self.assertIn("management", result)
|
||||
self.assertEqual(result["management"]["manager_name"], "ГРЕФ ГЕРМАН ОСКАРОВИЧ")
|
||||
self.assertEqual(
|
||||
result["management"]["manager_position"],
|
||||
"ПРЕЗИДЕНТ, ПРЕДСЕДАТЕЛЬ ПРАВЛЕНИЯ",
|
||||
)
|
||||
|
||||
def test_parse_legal_entity_fts_registration(self):
|
||||
"""FTS registration series and number are concatenated correctly."""
|
||||
_, result = self.partner._parse_dadata_response(DADATA_LEGAL_RESPONSE)
|
||||
self.assertEqual(result["sp_register_number"], "77 004599035")
|
||||
self.assertEqual(result["sp_register_date"], "2002-08-16")
|
||||
|
||||
def test_parse_legal_entity_country_and_state(self):
|
||||
"""Country and state are resolved from ISO codes when they exist in DB."""
|
||||
russia = self.env["res.country"].search([("code", "=", "RU")], limit=1)
|
||||
if not russia:
|
||||
self.skipTest("Russia not found in res.country — l10n data not loaded")
|
||||
|
||||
_, result = self.partner._parse_dadata_response(DADATA_LEGAL_RESPONSE)
|
||||
self.assertEqual(result["country_id"][0], russia.id)
|
||||
|
||||
# ------------------------------------------------------------------
|
||||
# _parse_dadata_response — individual entrepreneur
|
||||
# ------------------------------------------------------------------
|
||||
|
||||
def test_parse_individual_name(self):
|
||||
"""Individual entrepreneur name is built from fio parts."""
|
||||
_, result = self.partner._parse_dadata_response(DADATA_INDIVIDUAL_RESPONSE)
|
||||
self.assertEqual(result["name"], "ИВАНОВ ИВАН ИВАНОВИЧ")
|
||||
|
||||
def test_parse_individual_no_kpp(self):
|
||||
"""Individual entrepreneur response does not set kpp."""
|
||||
_, result = self.partner._parse_dadata_response(DADATA_INDIVIDUAL_RESPONSE)
|
||||
self.assertNotIn("kpp", result)
|
||||
|
||||
def test_parse_individual_no_management(self):
|
||||
"""No management key in result when management is absent."""
|
||||
_, result = self.partner._parse_dadata_response(DADATA_INDIVIDUAL_RESPONSE)
|
||||
self.assertNotIn("management", result)
|
||||
|
||||
def test_parse_individual_no_fts_registration(self):
|
||||
"""No sp_register_number when fts_registration is None."""
|
||||
_, result = self.partner._parse_dadata_response(DADATA_INDIVIDUAL_RESPONSE)
|
||||
self.assertNotIn("sp_register_number", result)
|
||||
|
||||
# ------------------------------------------------------------------
|
||||
# get_legal_entity_data
|
||||
# ------------------------------------------------------------------
|
||||
|
||||
@patch("myaddons.dob.dadata_connector.models.res_partner.Dadata")
|
||||
def test_get_legal_entity_data_returns_action(self, MockDadata):
|
||||
"""get_legal_entity_data returns an act_window action when widget=True."""
|
||||
mock_instance = MagicMock()
|
||||
mock_instance.find_by_id.return_value = DADATA_LEGAL_RESPONSE
|
||||
MockDadata.return_value = mock_instance
|
||||
|
||||
action = self.partner.get_legal_entity_data(vat="7707083893", widget=True)
|
||||
|
||||
self.assertEqual(action["type"], "ir.actions.act_window")
|
||||
self.assertEqual(action["res_model"], "res.partner.auto_data.wizard")
|
||||
self.assertEqual(action["target"], "new")
|
||||
|
||||
@patch("myaddons.dob.dadata_connector.models.res_partner.Dadata")
|
||||
def test_get_legal_entity_data_returns_dict_when_no_widget(self, MockDadata):
|
||||
"""get_legal_entity_data returns raw data dict when widget=False."""
|
||||
mock_instance = MagicMock()
|
||||
mock_instance.find_by_id.return_value = DADATA_LEGAL_RESPONSE
|
||||
MockDadata.return_value = mock_instance
|
||||
|
||||
result = self.partner.get_legal_entity_data(vat="7707083893", widget=False)
|
||||
|
||||
self.assertIsInstance(result, dict)
|
||||
self.assertEqual(result["vat"], "7707083893")
|
||||
|
||||
@patch("myaddons.dob.dadata_connector.models.res_partner.Dadata")
|
||||
def test_get_legal_entity_data_raises_when_empty(self, MockDadata):
|
||||
"""ValidationError is raised when DaData returns no results."""
|
||||
mock_instance = MagicMock()
|
||||
mock_instance.find_by_id.return_value = []
|
||||
MockDadata.return_value = mock_instance
|
||||
|
||||
with self.assertRaises(ValidationError):
|
||||
self.partner.get_legal_entity_data(vat="0000000000", widget=False)
|
||||
|
||||
@patch("myaddons.dob.dadata_connector.models.res_partner.Dadata")
|
||||
def test_get_legal_entity_data_raises_on_http_error(self, MockDadata):
|
||||
"""ValidationError is raised on HTTPStatusError from DaData."""
|
||||
from httpx import HTTPStatusError, Request, Response
|
||||
|
||||
mock_instance = MagicMock()
|
||||
mock_instance.find_by_id.side_effect = HTTPStatusError(
|
||||
"401", request=MagicMock(spec=Request), response=MagicMock(spec=Response)
|
||||
)
|
||||
MockDadata.return_value = mock_instance
|
||||
|
||||
with self.assertRaises(ValidationError):
|
||||
self.partner.get_legal_entity_data(vat="7707083893", widget=False)
|
||||
|
||||
# ------------------------------------------------------------------
|
||||
# _get_view
|
||||
# ------------------------------------------------------------------
|
||||
|
||||
def test_get_view_sets_dadata_search_widget_on_vat(self):
|
||||
"""_get_view replaces widget on vat field with dadata_search."""
|
||||
arch, _view = self.env["res.partner"]._get_view(view_type="form")
|
||||
vat_nodes = arch.xpath("//field[@name='vat']")
|
||||
for node in vat_nodes:
|
||||
self.assertEqual(
|
||||
node.get("widget"),
|
||||
"dadata_search",
|
||||
"vat field must use dadata_search widget",
|
||||
)
|
||||
47
dadata_connector/tests/test_wizard.py
Normal file
47
dadata_connector/tests/test_wizard.py
Normal file
@ -0,0 +1,47 @@
|
||||
from odoo.tests.common import tagged
|
||||
|
||||
from .common import DadataConnectorCommon
|
||||
|
||||
|
||||
@tagged("post_install", "-at_install")
|
||||
class TestResPartnerAutoDataWizard(DadataConnectorCommon):
|
||||
"""Tests for res.partner.auto_data.wizard."""
|
||||
|
||||
def _create_wizard(self, **kwargs):
|
||||
vals = {
|
||||
"partner_id": self.partner.id,
|
||||
"name": "ПАО Тест",
|
||||
"status": "active",
|
||||
"organization_type": "legal",
|
||||
"full_address": "117997, г Москва, ул Вавилова, д 19",
|
||||
}
|
||||
vals.update(kwargs)
|
||||
return self.env["res.partner.auto_data.wizard"].create(vals)
|
||||
|
||||
def test_wizard_creation(self):
|
||||
"""Wizard record is created with expected field values."""
|
||||
wizard = self._create_wizard()
|
||||
self.assertEqual(wizard.name, "ПАО Тест")
|
||||
self.assertEqual(wizard.status, "active")
|
||||
self.assertEqual(wizard.organization_type, "legal")
|
||||
self.assertEqual(wizard.partner_id, self.partner)
|
||||
|
||||
def test_button_yes_returns_close_action(self):
|
||||
"""button_yes returns act_window_close with update flag."""
|
||||
wizard = self._create_wizard()
|
||||
result = wizard.button_yes()
|
||||
|
||||
self.assertEqual(result["type"], "ir.actions.act_window_close")
|
||||
self.assertTrue(result["infos"]["update"])
|
||||
|
||||
def test_wizard_status_selection_values(self):
|
||||
"""All expected status values are accepted."""
|
||||
statuses = ["active", "liquidating", "liquidated", "bankrupt", "reorganizing"]
|
||||
for status in statuses:
|
||||
wizard = self._create_wizard(status=status)
|
||||
self.assertEqual(wizard.status, status)
|
||||
|
||||
def test_wizard_organization_type_individual(self):
|
||||
"""Wizard accepts individual organization type."""
|
||||
wizard = self._create_wizard(organization_type="individual")
|
||||
self.assertEqual(wizard.organization_type, "individual")
|
||||
Reference in New Issue
Block a user