Public release from ruodoo-project: 19.0 - 2026-07-26 21:17:35 UTC
This commit is contained in:
165
dadata_connector/TECHNICAL.md
Normal file
165
dadata_connector/TECHNICAL.md
Normal file
@ -0,0 +1,165 @@
|
||||
# DaData Connector
|
||||
**name**: dadata_connector
|
||||
**version**: 19.0.2025.12.03
|
||||
**author**: MK.Lab
|
||||
**depends**: `base`, `web`, `contacts`, `account`, `l10n_ru_doc`
|
||||
**external_dependencies**: `dadata==21.10.1`
|
||||
|
||||
## Архитектура
|
||||
|
||||
Модуль состоит из трёх частей: серверная логика запроса к DaData и разбор ответа, визард подтверждения найденных данных, и OWL-компонент (виджет) который добавляет кнопку поиска к полям Tax ID (ИНН) и ОГРН прямо в форме.
|
||||
|
||||
```
|
||||
dadata_connector
|
||||
├── res.partner (расширение) — запрос к DaData, разбор ответа
|
||||
├── res.partner.auto_data.wizard — диалог подтверждения данных
|
||||
├── res.config.settings (расширение) — хранение токена DaData
|
||||
└── SearchField (OWL-компонент) — виджет с кнопкой поиска в форме
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Модели
|
||||
|
||||
### `res.partner` — Расширение карточки контрагента
|
||||
|
||||
**Файл:** `models/res_partner.py`
|
||||
|
||||
Добавляет логику поиска и обработки данных из DaData. Новых полей в базу не добавляет — только методы и переопределение виджета.
|
||||
|
||||
#### Переопределение виджета (`_get_view`)
|
||||
|
||||
При загрузке формы контрагента метод `_get_view` программно устанавливает виджет `dadata_search` для поля `vat` (ИНН). Это сделано через код, а не через XML, чтобы установить приоритет над стандартным виджетом `partner_autocomplete` от Odoo. Он подключается к полю первым, и XML-переопределение его не вытесняет.
|
||||
|
||||
Для поля `ogrn` виджет устанавливается через XML (`views/res_partner_views.xml`).
|
||||
|
||||
#### Метод `get_legal_entity_data(vat, widget=True)`
|
||||
|
||||
Точка входа вызывается из JS-компонента при нажатии кнопки поиска. Выполняет три шага:
|
||||
|
||||
1. Получает токен через `get_dadata_token()`
|
||||
2. Отправляет запрос в DaData: `dadata.find_by_id("party", vat, branch_type="MAIN")` — ищет только головные организации, без филиалов
|
||||
3. Разбирает ответ через `_parse_dadata_response()` и открывает визард подтверждения
|
||||
|
||||
Параметр `widget=True` означает режим работы через интерфейс (возвращает действие открытия визарда). При `widget=False` возвращает словарь с данными напрямую, что используется в тестах.
|
||||
|
||||
#### Метод `get_dadata_token()`
|
||||
|
||||
Читает токен из системных параметров (`dadata_connector.dadata_token`). Если токен не задан, возникает `ValidationError` с подсказкой, где его найти в настройках.
|
||||
|
||||
#### Метод `_parse_dadata_response(data)`
|
||||
|
||||
Разбирает JSON-ответ от DaData и возвращает два словаря:
|
||||
|
||||
- **`wizard_data`** — данные для показа в диалоге подтверждения: статус организации, тип (юрлицо / ИП), название, полный адрес строкой
|
||||
- **`new_data`** — данные для записи в карточку контрагента: все поля ниже
|
||||
|
||||
**Маппинг полей DaData → Odoo:**
|
||||
|
||||
| Поле Odoo | Поле DaData | Примечание |
|
||||
|---|---|---|
|
||||
| `vat` | `inn` | ИНН |
|
||||
| `ogrn` | `ogrn` | ОГРН |
|
||||
| `kpp` | `kpp` | Только для юрлиц |
|
||||
| `okpo` | `okpo` | ОКПО |
|
||||
| `arceat` | `okved` | Основной ОКВЭД |
|
||||
| `company_form` | `opf.code` → таблица `okopf` | Код ОКОПФ переводится в тип: `sp`, `plc`, `jsc` и т.д. |
|
||||
| `sp_register_number` | `documents.fts_registration` | Серия и номер через пробел |
|
||||
| `sp_register_date` | `documents.fts_registration.issue_date` | Дата регистрации в налоговой |
|
||||
| `name` | `name.short_with_opf` (юрлицо) или `fio` (ИП) | |
|
||||
| `country_id` | `address.data.country_iso_code` | Поиск по коду страны |
|
||||
| `state_id` | `address.data.region_iso_code` | Поиск по коду региона |
|
||||
| `city` | `address.data.city` | |
|
||||
| `street` | Собирается из нескольких полей | `street_with_type`, `house_type_full`, `house`, `flat_type_full`, `flat` через запятую |
|
||||
| `zip` | `address.data.postal_code` | |
|
||||
| `management` | `management.name` и `management.post` | Не поле, а вложенный словарь для создания контакта |
|
||||
|
||||
**Таблица ОКОПФ** (словарь `okopf` в начале файла): переводит цифровые коды организационно-правовых форм в технические коды Odoo. Например, `50102` → `sp` (индивидуальный предприниматель), `12300` → `plc` (ООО).
|
||||
|
||||
---
|
||||
|
||||
### `res.partner.auto_data.wizard` — Диалог подтверждения
|
||||
|
||||
**Файл:** `wizard/res_partner_auto_data_wizard.py`
|
||||
|
||||
Временная модель (`TransientModel`), работает только пока открыт диалог. Показывает пользователю найденные данные перед тем, как они будут применены к карточке.
|
||||
|
||||
| Поле | Тип | Описание |
|
||||
|---|---|---|
|
||||
| `partner_id` | Many2one → `res.partner` | Контрагент, к которому применяются данные |
|
||||
| `status` | Selection | Статус: active / liquidating / liquidated / bankrupt / reorganizing |
|
||||
| `organization_type` | Selection | Тип: legal (юрлицо) / individual (ИП) |
|
||||
| `name` | Char | Название организации |
|
||||
| `full_address` | Text | Полный юридический адрес строкой |
|
||||
|
||||
Форма открывается в режиме только для чтения (`create="0" delete="0" edit="0"`), редактировать данные в диалоге нельзя.
|
||||
|
||||
**Метод `button_yes()`** — при нажатии «Да» закрывает диалог и передаёт во фронтенд сигнал `{"update": True}`. JS-компонент перехватывает этот сигнал и применяет данные к карточке.
|
||||
|
||||
---
|
||||
|
||||
### `res.config.settings` — Расширение настроек
|
||||
|
||||
**Файл:** `models/res_config_settings.py`
|
||||
|
||||
Добавляет одно поле в раздел «Интеграции» в настройках Odoo. Значение хранится в системных параметрах.
|
||||
|
||||
| Поле | Параметр | Описание |
|
||||
|---|---|---|
|
||||
| `dadata_token` | `dadata_connector.dadata_token` | API-токен сервиса DaData |
|
||||
|
||||
---
|
||||
|
||||
## OWL-компонент: виджет `dadata_search`
|
||||
|
||||
**Файл:** `static/src/views/fields/search/search_field.js`
|
||||
|
||||
Кастомный виджет для полей типа `char`. Визуально выглядит как стандартное текстовое поле с иконкой поиска справа. Зарегистрирован в реестре виджетов Odoo как `dadata_search`.
|
||||
|
||||
#### Логика метода `search()`
|
||||
|
||||
Вызывается при нажатии иконки поиска. Последовательность:
|
||||
|
||||
1. Вызывает серверный метод `res.partner.get_legal_entity_data()` через ORM с текущим значением поля
|
||||
2. Получает в ответ действие открытия визарда подтверждения
|
||||
3. Открывает визард и ждёт его закрытия
|
||||
4. При закрытии проверяет флаг `closeInfo.update`:
|
||||
- Если `True` — применяет данные к карточке и сохраняет
|
||||
- Если `False` (нажали «Нет») — ничего не делает
|
||||
|
||||
#### Применение данных к карточке
|
||||
|
||||
Перед обновлением карточки фильтрует только те поля, которые реально существуют в модели (`record.fields`). Many2one-поля (например, `country_id`, `state_id`) конвертируются в формат `[id, ""]` — так требует OWL.
|
||||
|
||||
После обновления полей принудительно устанавливает `company_type: "company"` — контрагент всегда сохраняется как организация.
|
||||
|
||||
#### Создание контакта-руководителя
|
||||
|
||||
Если в ответе DaData есть данные об управляющем (`management`), компонент проверяет нет ли уже контакта с таким именем и должностью среди дочерних записей контрагента. Если нет — создаёт новый контакт через `res.partner.create()` с привязкой `parent_id` к текущему контрагенту.
|
||||
|
||||
---
|
||||
|
||||
## Представления
|
||||
|
||||
**Файл:** `views/res_partner_views.xml`
|
||||
|
||||
Два расширения формы контрагента:
|
||||
|
||||
- Расширение `l10n_ru_doc.view_partner_ru_form` — добавляет виджет `dadata_search` к полю `ogrn`
|
||||
- Расширение `base.view_partner_form` — добавляет виджет `dadata_search` к полю `vat`. Приоритет `2`, но виджет также устанавливается программно через `_get_view` для гарантированного перебития `partner_autocomplete`
|
||||
|
||||
**Файл:** `views/res_config_settings_view.xml`
|
||||
|
||||
Добавляет поле токена в настройки.
|
||||
|
||||
---
|
||||
|
||||
## Тесты
|
||||
|
||||
**Файл:** `tests/`
|
||||
|
||||
| Файл | Что покрывает |
|
||||
|---|---|
|
||||
| `test_res_partner.py` | Запрос к DaData, разбор ответа, маппинг полей |
|
||||
| `test_wizard.py` | Создание визарда, отображение полей, кнопка подтверждения |
|
||||
| `common.py` | Общие фикстуры: клиент DaData, тестовый контрагент |
|
||||
Reference in New Issue
Block a user