Рахунки-фактури та квитанції мають один вхід. Як уникнути змішування двох структур?

Один інтерфейс отримує як рахунки-фактури, так і квитанції, і легко писати великі об’єкти за допомогою «всі поля необов’язкові». Такий предмет може вмістити що завгодно, але складно сказати, що саме він отримав. Було б зрозуміліше зберегти два окремих контракти та вибрати один із них із явним полем типу.

Рахунки-фактури та квитанції часто поміщаються в один інтерфейс для обробки, але якщо для їх отримання використовується лише широкий об’єкт із «усі поля необов’язкові», він може вмістити будь-який вхід, але не може визначити, який документ отримано. Ця стаття призначена для розробників і бізнес-персоналу, яким потрібно розробити інтерфейс отримання документів або підтримувати правила перевірки схеми JSON. Прочитавши це, ви дізнаєтесь, як зберігати два незалежні контракти, вибрати один із явним полем типу та обробляти конфлікти розгалужень і невідомі типи.

У прикладах у цій статті використовуються синтетичні зразки для демонстрації методів обробки. Код було перевірено з прикладами в ізольованому середовищі, але це не є гарантією всіх реальних файлів або продуктивності продукту. Перед обробкою важливих документів збережіть оригінали та перегляньте їх відповідно до меж і пунктів прийняття в тексті.

Як працювати зі спільним входом рахунків-фактур і квитанцій? Висновок одним реченням

Коли ви отримуєте два документи з одного порталу, краще зберігати два окремі контракти та використовувати обов’язкове поле kind, щоб вибрати один із них, а не використовувати широкий об’єкт, який може містити все.

ваша ситуаціяРекомендовані практикипричина
Цей же інтерфейс потрібен для отримання рахунків-фактур і квитанційЗберігайте два окремих договори, вибрані за допомогою поля необхідного типуВеликий об’єкт із необов’язковими полями може вмістити що завгодно, але важко пояснити, що отримано.
Організуйте гілки за допомогою oneOfКожна гілка використовує const для фіксації видуoneOf вимагає точно однієї гілки. Перекривання полів гілок спричинить помилку правового введення.
Введіть відсутній тип або конфлікт полівПовернути чітке повідомлення про помилку, у якому показано конфліктні поля та джерелаЛише показ "немає збігу" не допоможе виявити проблему
Класифікатор не може розрізняти типи документівЗберігати невідомий статус і вводити незалежну чергу для класифікаціїВибір гілки навмання призведе лише до фальшивого «успіху»
Перевірку структури пройденоЦе не означає, що класифікація правильна, а також не означає, що документи є автентичними.Достовірність класифікації, перевірка структури та автентичність бізнесу є трьома різними рівнями

Чому ви не можете використовувати широкий об'єкт?

Ризик зробити всі поля необов'язковими

Коли інтерфейс одночасно отримує і рахунки, і чеки, легко написати великий об'єкт із «всі поля доступні». Такий об'єкт дійсно може приймати різні вхідні дані, але вартість становить:

  • Структурно неможливо визначити, який тип документа отримується.
  • Необхідні поля та опції для обох документів переплутані, а правила перевірки стають дедалі більш розмитими.
  • Коли виникають помилки, важко точно пояснити, де вхід не відповідає очікуванням.

Більш зрозумілий підхід — зберегти два окремі контракти і вибрати один із чітким типом поля.

OneOf — це не просто один із них

Перекриття гілок може спричиняти дійсні збої введення

oneOf вимагає, щоб вхід відповідав рівно одній гілці. Якщо обидві гілки пройдуть одночасно, загальна валідація також провалиться.

Якщо гілка містить лише кілька необов'язкових полів, вони, ймовірно, будуть суттєво перекриватися. На цьому етапі легітимний вхід фактично не проходить перевірку.

Використовуйте тип і конст, щоб усунути перекриття

Використавши необхідне поле kind плюс const для фіксації значень, ви можете встановити чіткі межі між гілками та заздалегідь усунути такі накладання.

Для детальнішої інформації про правила комбінації дивіться Правила комбінування схем JSON.

Виконуваний протокол відхилення

from jsonschema import Draft202012Validator

def branch(kind, identity):
    return {
        "type": "object", "additionalProperties": False,
        "required": ["kind", identity],
        "properties": {"kind": {"const": kind},
                       identity: {"type": "string", "minLength": 1}}
    }

schema = {"oneOf": [branch("invoice", "invoice_number"),
                    branch("receipt", "receipt_number")]}
v = Draft202012Validator(schema)
valid = lambda x: not list(v.iter_errors(x))
assert valid({"kind": "invoice", "invoice_number": "I-1"})
assert valid({"kind": "receipt", "receipt_number": "R-1"})
assert not valid({"kind": "invoice", "receipt_number": "R-1"})
assert not valid({"kind": "receipt", "receipt_number": ""})
assert not valid({"kind": "invoice", "invoice_number": "I-1",
                  "receipt_number": "R-1"})
assert not valid({"kind": "unknown"})

Це спрощений приклад запису, а не повна польова модель реального рахунку. Код демонструє вибір гілок і відхилення надлишкових полів; податкові ставки, заголовки та суми потрібно визначати окремо.

Якщо бізнес дозволяє додаткові поля, простори імен для цих полів мають бути чітко визначені, а не тихо знімати всі обмеження.

Не просто показуйте повідомлення про помилки «невідповідність»

Відображається лише «невідповідність», і ні користувачі, ні технічний персонал не можуть знати, де саме проблема. У таблиці нижче наведено кілька поширених ознак введення та відповідні методи зворотного зв'язку:

Вхідні функціїРеальні сумнівиПропонуйте зворотний зв'язок
Вид дефіцитуНе можу обрати угодуБудь ласка, вкажіть тип документа
Конфлікти типів і числових полівОбидві структури змішаніВідображати суперечливі поля та їх джерела
Пронумеровані порожні рядкиКлюч існує, але не має значенняОглядаючись назад на пронумеровану область
Невідомий типЧинна угода цього не охоплюєУвійдіть у незалежну чергу для класифікації

Невпевнені типи теж потребують виходу

Якщо класифікатор не може відрізнити рахунки-фактури та чеки, статус невідомості слід зберігати, а не випадково обирати відділення для «успіху».

Тут є три різні рівні, які потрібно розглядати окремо:

  • Довіра категорії: Наскільки впевнена система оцінює типи документів.
  • Перевірка структури: Введіть, чи виконано обраний контракт.
  • Бізнес-автентичність: Чи є самі документи справжніми та дійсними.

Структурна перевірка не може бути зворотно доведена як правильна, так само як і не може довести справжність документа.

Тип документа також слід зберігати під час процесу перекладу

Одна й та сама сфера може вимагати іншу термінологію в різних документах. При використанні термінології OmniDoc та двомовних робочих процесів рецензування надання типів документів зі списками доставки допомагає рецензентам зрозуміти контекст кількості та обсягу.

Не дозволяйте широкому JSON-об'єкту стерти ці відмінності.

📊 Порівняння ядра

МетодЧи можна чітко визначити тип документа?Прояви, коли гілки накладаються.Обробка невідомих типів
Широкі об'єкти з усіма полями опціональнимиВажко пояснити, що саме було отриманоНе застосовується; між полями немає меж гілокЛегко проковтнути широкими цілями і важко помітити
Два незалежні контракти + обов'язковий видЧітко обирайте за допомогою полі типуВикористовуйте const для фіксації значень і зменшення перекриттяЗалиште статус невідомого і зайдіть у чергу для сортування

🎯 Який метод тобі підходить?

Якщо потрібно отримувати і рахунки, і чеки одночасно

Залиште два окремих контракти і встановіть kind як обов'язкову сферу.

Якщо ваші поля гілок сильно перекриваються

Виправте kind з const у кожній гілці, щоб уникнути одночасного співпадіння легітимних вхідних даних.

Якщо ваша система дозволяє додаткові поля

Спочатку уточніть простір імен додаткового поля, а потім виріште, як послабити обмеження.

Якщо ваш класифікатор недостатньо впевнений

Зберігайте невідомий статус і надсилайте документи в чергу для сортування; не вибирайте примусово гілки.

❓ Поширені запитання (FAQ)

1. Чому б просто не використати об'єкт, де всі поля є необов'язковими?

Такий партнер може підробити будь-що, але важко зрозуміти, що саме він отримує.

2. Чому oneOf спричиняє помилку дійсного запису?

oneOf вимагає точного співпадіння гілки. Якщо обидві гілки проходять одночасно, загальна перевірка провалиться. Ця ситуація, ймовірно, виникає, коли поля гілок сильно перекриваються.

3. На що слід звертати увагу, дозволяючи додаткові поля?

Простір назв для додаткового поля має бути чітко визначений, а не тихо усувати всі обмеження.

4. Що, якщо класифікатор не може визначити тип документа?

Залиште статус невідомого і введіть незалежну чергу для класифікації; не обирайте випадково гілку для успішного відгалуження.

5. Чи підтверджує проходження структурної перевірки автентичність документів?

Ні. Конфіденційність класифікації, структурна перевірка та автентичність бізнесу — це три окремі рівні; структурна перевірка не може довести автентичність документів.

📝 Резюме

При розміщенні рахунків і квитанцій в одному записі зберігайте два окремі контракти та обирайте їх за допомогою потрібного поля kind, щоб тип внесеного документа був чітко видимий. Перевірка гілок, зворотний зв'язок на помилки та експорт невідомих типів також слід проектувати разом.

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

Оновлено 2026-11-15