NODEDC_1C/IN/CHEK_Validation_Run_Account...

18 KiB
Raw Blame History

Да, теперь уже видно не просто то, что “онтология просажена”, а почему именно она просажена и как именно её чинить.

Главный вывод после догрузки: у вас ломается не “граф вообще”, а семаническая нормализация ссылок. Текущий mapping-слой построен слишком грубой эвристикой: все ссылки превращаются в один relation reference, а target_entity определяется почти только по подстроке в имени поля. Из-за этого система формально строит links, но содержательно не понимает, кто есть кто. Отсюда и 1016 Unknown-relations из 2618, при semantic coverage всего 61.19%, хотя link coverage 100%.

Где именно источник поломки

Сейчас правило такое:

  • если поле оканчивается на _Key, ref, или выглядит как GUID — оно становится link;
  • relation всегда один: reference;
  • target_entity угадывается по имени поля;
  • если не угадали по нескольким словам вроде контраг, договор, счет, организ, документ, то ставится Unknown.

Это и есть корневая ошибка архитектуры. Для бухгалтерского контура такой подход слишком бедный, потому что:

  • Recorder — это не просто “reference”, а источник движения / документ-регистратор;
  • Ref в журнале — это не просто “reference”, а journal entry points to concrete document;
  • Поставщик_Key и Покупатель_Key — не generic refs, а роли контрагентов;
  • Ответственный_Key — не generic ref, а actor / responsible person;
  • Валюта_Key — это вообще не Document, а отдельная сущность валюты;
  • Склад_Key, ПодразделениеДт_Key, ФизЛицо_Key, СтатьяДвиженияДенежныхСредств_Key требуют собственных классов, а не падения в Unknown.

Что ломается на реальных примерах

1) Recorder в регистрах НДС

В НДС-регистрах Recorder приходит вместе с Recorder_Type, например:

  • Recorder_Type = StandardODATA.Document_ПоступлениеТоваровУслуг
  • либо Recorder_Type = StandardODATA.Document_СчетФактураПолученный
  • либо Recorder_Type = StandardODATA.Document_РеализацияТоваровУслуг. Но в links это всё равно уходит как target_entity = Unknown.

Это критично, потому что для бухгалтерской аналитики регистр без корректного регистратора — почти полуслепая запись. Вы теряете причинную цепочку: движение регистра → документ-регистратор → контрагент/договор/товар/сумма.

2) СчетФактура ошибочно уезжает в Account

В НДС-регистрах поле СчетФактура по смыслу ссылается на документ, а не на бухгалтерский счёт. Но из-за эвристики “если в имени есть счетAccount” оно типизируется как Account. Это уже не просто unknown, а ложноположительное сопоставление.

То есть у вас часть связей не просто потеряна, а неверно искажена.

3) Ref в журналах документов

В DocumentJournal_БанковскиеВыписки есть:

  • Ref
  • Ref_Type = StandardODATA.Document_СписаниеСРасчетногоСчета или ...ПоступлениеНаРасчетныйСчет. Но link по Ref уходит в Unknown, хотя это должен быть прямой указатель на конкретный документ. Именно поэтому журналы дают много unknown и source_id=unknown.

4) Валюта маппится неверно

В СписаниеСРасчетногоСчета поле ВалютаДокумента_Key в одном из образцов уходит в Document, просто потому что в имени есть слово документа, хотя это ссылка на валюту. Аналогичная проблема в журналах по Валюта_Key, где поле уходит в Unknown.

5) Ролевые контрагенты не разведены

В регистрах есть Поставщик_Key, Покупатель_Key, ДоговорКонтрагента_Key. Сейчас:

  • Поставщик_Key и Покупатель_Key часто падают в Unknown,
  • ДоговорКонтрагента_Key обычно распознаётся как Contract, но без явной роли в relation.

В результате граф знает, что “что-то связано с контрагентом/договором”, но не знает:

  • это supplier или buyer,
  • это основной контрагент документа или договор расчётов,
  • это связь документа, журнала или регистра.

6) Ответственный_Key, ФизЛицо_Key, Склад_Key, СтатьяДвиженияДенежныхСредств_Key

Эти поля в топе проблемных, потому что для них вообще нет соответствующих сущностей в базовой canonical-модели. В текущем ядре у вас classes: Organization, Counterparty, Contract, Account, Subconto, Document, Posting, RegisterMovement, Period, плюс CanonicalEntity. Но нет нормальных классов для people/employee, warehouse, currency, cashflow article, department, item/product. Поэтому всё это закономерно валится в Unknown.

Значит ли это, что надо “подкручивать только relation rules”?

Нет. Тут нужно чинить сразу три слоя:

A. Расширять canonical classes

Минимально надо добавить:

  • EmployeeOrUser / ResponsiblePerson
  • Currency
  • Warehouse
  • CashflowArticle
  • Department
  • Individual
  • Item / Nomenclature
  • BankAccount
  • TaxRegisterRecord или более общий RegisterRecord
  • InvoiceDocument / FacturaDocument как подтип документа, если хотите потом делать объяснения по НДС аккуратнее.

Без этого вы можете переписать relation rules хоть десять раз, но часть полей всё равно некуда будет положить.

B. Уходить от одного relation reference

Нужен не один reference, а словарь осмысленных relations. Минимальный стартовый набор я бы делал такой:

Документы / журналы

  • journal_refers_to_document
  • document_belongs_to_organization
  • document_has_counterparty
  • document_has_contract
  • document_has_currency
  • document_has_warehouse
  • document_has_responsible
  • document_has_cashflow_article
  • document_has_bank_account

Регистры

  • register_recorded_by_document
  • register_relates_to_supplier
  • register_relates_to_buyer
  • register_relates_to_invoice
  • register_relates_to_vat_account
  • register_relates_to_contract
  • register_relates_to_organization

Финансовые документы

  • payment_relates_to_counterparty
  • payment_relates_to_bank_account
  • payment_relates_to_cashflow_article
  • payment_relates_to_individual
  • payment_relates_to_department

Строки табличных частей

  • document_line_has_item
  • document_line_has_account
  • document_line_has_vat_account
  • document_line_has_expense_account
  • document_line_has_income_account

C. Менять сам принцип типизации

Не по имени поля alone, а по комбинации:

  1. source_entity
  2. source_field
  3. *_Type рядом
  4. наличие navigationLinkUrl
  5. контекст набора полей вокруг записи.

Именно это даст переносимость между разными 1С-контурами, а не ручную подгонку под июнь 2020, чего у вас как раз требует ТЗ.

Как я бы правил правила маппинга

Правило 1. *_Type имеет приоритет над эвристикой имени

Если есть:

  • Recorder_Type
  • Ref_Type
  • СчетФактура_Type
  • ДокументОплаты_Type
  • и т.п., то target_entity нужно определять не по имени поля, а по значению type. Например:
  • StandardODATA.Document_*Document
  • StandardODATA.Catalog_КонтрагентыCounterparty
  • StandardODATA.Catalog_СкладыWarehouse
  • StandardODATA.Catalog_ВалютыCurrency
  • StandardODATA.Catalog_ФизическиеЛицаIndividual
  • StandardODATA.Catalog_СтатьиДвиженияДенежныхСредствCashflowArticle. Это автоматически лечит большую часть Recorder, Ref и typed-полей.

Правило 2. Для конкретных полей — словарь приоритетных semantic mappings

Нужен явный field dictionary, например:

  • Recorder → relation register_recorded_by_document, target Document
  • Ref в DocumentJournal_* → relation journal_refers_to_document, target Document
  • Поставщик_Key → relation *_relates_to_supplier, target Counterparty
  • Покупатель_Key → relation *_relates_to_buyer, target Counterparty
  • Ответственный_Key → relation *_has_responsible, target ResponsiblePerson
  • Валюта_Key / ВалютаДокумента_Key → relation *_has_currency, target Currency
  • Склад_Key → relation *_has_warehouse, target Warehouse
  • СтатьяДвиженияДенежныхСредств_Key → relation *_has_cashflow_article, target CashflowArticle
  • ФизЛицо_Key → relation *_relates_to_individual, target Individual
  • БанковскийСчет_Key / СчетОрганизации_Key → target BankAccount.

Правило 3. СчетФактура — специальный case

Поле СчетФактура нельзя по общему правилу отправлять в Account. Если рядом есть СчетФактура_Type = StandardODATA.Document_*, то это relation к документу:

  • register_relates_to_invoice
  • target Document.

Правило 4. Нулевые GUID не создавать как обычные бизнес-связи

00000000-0000-0000-0000-000000000000 сейчас плодит мусорные links. Их лучше:

  • либо не писать в canonical links вообще,
  • либо писать как null_reference / empty_reference технического типа, отдельно от семанических relations.

Это сразу уменьшит шум в графе и не будет создавать фальшивых “связей с нулевым контрагентом”.

Правило 5. source_id для register records нельзя оставлять unknown

Для регистров, где нет Ref_Key, нужно собирать составной ключ, например:

  • source_entity + Recorder + Recorder_Type + LineNumber + Period или аналог. Иначе у вас 358 записей с source_id=unknown, и это разрушает стабильное переиспользование сущности при повторных загрузках.

Приоритеты ремонта по очереди

Приоритет 1 — Recorder, Ref, СчетФактура, Поставщик_Key, Покупатель_Key

Это даст самый большой выигрыш по semantic coverage, потому что именно эти поля сейчас массово ломают регистры, журналы и ключевые документы.

Приоритет 2 — Ответственный_Key, Валюта_Key, ВалютаДокумента_Key

Это быстро снижает долю Unknown в журналах и платежных документах, плюс улучшает explainability по “кто оформлял / в какой валюте”.

Приоритет 3 — СтатьяДвиженияДенежныхСредств_Key, ФизЛицо_Key, Склад_Key, ПодразделениеДт_Key

Это уже следующий слой качества, особенно для операционных и зарплатных/кассовых сценариев.

Что это даст benchmarkу

Если вы сделаете только этот ontology repair, без переписывания всего остального, я ожидаю:

  • существенное падение unknown_relations;
  • рост semantic coverage заметно выше текущих 61%;
  • лучшую связность сценариев document ↔ register ↔ journal;
  • меньше ошибочных ответов по cross-entity вопросам;
  • меньше ложной уверенности в store-only ответах, потому что router будет видеть более богатую и корректную семантику.

Особенно выиграют вопросы вида:

  • связать документ и проводки,
  • связать контрагента, договор и документ,
  • объяснить происхождение записи НДС,
  • пройти от банковской выписки к первичному документу.

Практический план, что делать прямо сейчас

Я бы формализовал следующий этап так:

Шаг 1. Переписать mapper architecture

  • ввести entity_type_resolver
  • ввести relation_resolver
  • ввести null_guid_filter
  • ввести composite_source_id_builder для register records.

Шаг 2. Сделать словарь field semantics Отдельная таблица:

  • source_entity_pattern
  • field_name
  • optional_type_field
  • target_class
  • relation_name
  • allow_zero_guid
  • priority. Это даст переносимость и управляемость.

Шаг 3. Расширить canonical classes Хотя бы теми классами, которые сейчас массово падают в Unknown.

Шаг 4. Переингестить июнь 2020 После изменения rules — заново посадить slice и пересчитать:

  • unknown_relations,
  • semantic_coverage_pct,
  • relation_types_total,
  • top problematic fields.

Шаг 5. Только потом — дочищать router Потому что router сейчас частично страдает вторично: он опирается на недотипизированный граф. Сначала надо дать ему нормальную семаническую подложку.

Мой жёсткий вывод

Сейчас у вас не “плохая онтология”, а слишком примитивный canonical mapper для домена уровня 1С-бухгалтерии. Он годится как стартовый универсальный мост, но не как слой, который должен объяснять бухгалтерскую причинность. Проблема не в snapshotе июня 2020 — он как раз полезен и достаточно показателен. Проблема в том, что mapping-слой ещё не различает:

  • тип ссылки,
  • бизнес-роль ссылки,
  • typed reference,
  • пустую ссылку,
  • и специальные 1С-паттерны вроде Recorder/Ref/СчетФактура.

Следующим сообщением я могу собрать тебе уже прямо готовое ТЗ на ремонт ontology/mapping-слоя: с таблицей новых canonical classes, таблицей relation types и таблицей правил для Recorder, Ref, Поставщик, Покупатель, Ответственный, Валюта, Склад, СтатьяДДС.