Задача: завести довідник УКТЗЕД у базу і прив'язати код до товару так, щоб він читався в
документах, а не лежав окремою моделлю. УКТЗЕД (Українська класифікація товарів
зовнішньоекономічної діяльності) — національний класифікатор товарів, код від 2 до 10 цифр;
для послуг коду немає за визначенням. У KitWorks під цю задачу є три модулі — kw_uktzed,
kw_uktzed_data, l10n_ua_uktz — і з назв здається, що це один продукт. Це не так.
Як влаштований довідник
Розглянемо модель. kw_uktzed і l10n_ua_uktz незалежно одне від одного оголошують ту саму:
class UktzedCode(models.Model):
_name = 'kw.uktzed.code'
_description = 'Uktzed code'
_inherit = ['generic.mixin.parent.names']
_parent_name = 'parent_id'
name = fields.Char(string='Code', )
active = fields.Boolean(default=True, )
parent_id = fields.Many2one(comodel_name='kw.uktzed.code', )
title = fields.Char()
description = fields.Text()
# ...
Окремої моделі під кожен рівень ієрархії немає — це самопосилання через parent_id. Домішка
generic.mixin.parent.names додає перевірку на циклічність і обчислює display_name
обходом батьків знизу вгору («Розділ / Група / Позиція / …»). Код товару — це поле name:
розділи римськими цифрами (I, II, …), далі 2-, 4-, 6-, 8- і 10-значні коди:
<record model="kw.uktzed.code" id="kw_uktzed_code_1">
<field name="name">I</field>
<field name="title">(з 01 по 05) Живі тварини; продукти тваринного походження</field>
</record>
Той самий запис у l10n_ua_uktz — інший формат:
{
"name": "I",
"title": "(з 01 по 05) Живі тварини; продукти тваринного походження"
}
У даних, що постачаються з модулями, 16 560 записів, з них 10 473 — кінцеві 10-значні коди. Решта — вузли дерева (розділи, групи, товарні позиції), на товар не чіпляються.
Два модулі на одну модель
kw_uktzed_data довантажує ці 16 560 записів у kw_uktzed XML-даними
(data/data.xml, ~4,3 МБ, noupdate="1") — вони підтягуються автоматично при встановленні.
l10n_ua_uktz даних при встановленні не завантажує: той самий довідник лежить у форматі
JSON (~4,7 МБ) і чекає, поки в Загальних налаштуваннях, розділ «Uktzed»,
хтось натисне «Upload Uktzed» — візард із трьома джерелами: з модуля, з файлу, за посиланням.
Неправильно — ставити і kw_uktzed_data, і l10n_ua_uktz одразу: жоден не залежить від
іншого, і Odoo об'єднає два незалежно написані визначення однієї моделі.
Правильно — обрати один і саме з ним будувати подальшу реалізацію: обидва вантажать той
самий довідник, різниця лише у способі завантаження.
Важливо! Завантаження в l10n_ua_uktz завжди створює новий запис, без перевірки, чи такий
код уже є:
def upload_json(self, data):
name = data.get('name')
title = data.get('title')
parent_id = False
parent_id_name = data.get('parent_id')
if parent_id_name:
parent = self.sudo().env['kw.uktzed.code'].search([
('name', '=', parent_id_name)], limit=1)
parent_id = parent.id if parent else False
if name or title:
self.sudo().env['kw.uktzed.code'].create(
{'name': name, 'title': title, 'parent_id': parent_id})
Повторний запуск майстра після оновлення класифікатора не оновить довідник, а задублює його.
Вбудованого шляху «оновити класифікатор» немає: XML з noupdate="1" при апгрейді нічого не
перезапише, а json-візард лише ще раз усе створить.
Прив'язка до товару: чого немає в жодному з двох модулів
Ні kw_uktzed, ні l10n_ua_uktz не додають жодного поля на product.template. Довідник
доступний лише через власне меню «UKTZED → Codes» — готова ієрархічна модель без готової
інтеграції з карткою товару.
Важливо! Якщо стоїть тільки kw_uktzed/l10n_ua_uktz — довідник є, а прив'язати код до
товару штатно нічим. Потрібне поле під конкретний проєкт — звичайний Many2one:
uktzed_id = fields.Many2one(
comodel_name='kw.uktzed.code',
string='UKTZED Code',
)
Куди код потрапляє на практиці
Ні kw_uktzed, ні l10n_ua_uktz код нікуди не виводять і ніде не читають. Ну по суті вони
і не повинні — це довідник і спосіб його завантажити; прив'язка до товару й показ коду в
документі (рахунку, накладній, митній декларації) — окрема робота під конкретний проєкт.
Важливо! Це стосується і оновлення самого довідника: коли класифікатор зміниться (з'являються
нові коди, скасовуються старі), штатного способу перезалити вже завантажені дані немає в
жодному з двох модулів — kw_uktzed_data через noupdate="1" пропускає зміни при апгрейді,
а l10n_ua_uktz, як показано вище, лише додасть дублі. Звірка після оновлення класифікатора
— ручна робота.
Масове проставлення кодів наявним товарам
Коли товарів багато й вручну не варіант — це задача під kw_generic_import. Готового
шаблону з колонкою УКТЗЕД немає: kw_generic_import_product імпортує default_code,
barcode, category, ціни, атрибути. Але власне поле-Many2one під код УКТЗЕД на
product.template рушій резолвить конвертером m2o_by_name — точним збігом по полю
name, яке в kw.uktzed.code і є кодом. Досить додати один рядок мапінгу в шаблон
імпорту — решта працює як для будь-якого Many2one. Про сам рушій — окрема стаття:
https://kitworks.systems/blog/programuvannia-2/generic-import-arhitektura-136. Я б перед Run Import
спершу прогнав Dry Run — рядки без збігу видно одразу, і в базу нічого не пишеться.