Dokumentooborot
Интенты
Обработка
Справочники
Песочница: SOLPACIFIC
Intent ID: 43
Назад
1) Загрузить файл и получить сырой OCR JSON
Сохраняем последний файл и сырой OCR, чтобы можно было повторно тестировать маппинг без перезагрузки.
Скачать последний файл (ValleVerde-Farms March 2026 Standing Order Selection.xlsx)
Получить сырой OCR
🚀 Полный пайплайн
2) Правила трансформации
Правила трансформации OCR-данных в целевую схему (включая валидацию).
Промпт LLM-подсказки
Сбросить на дефолт
Ты создаёшь правила трансформации данных инвойса из сырого OCR в целевую JSON-схему. Сырой OCR-JSON получен из table_parsing (распознавание таблиц из изображений). Особенности формата: - header_text может содержать текст шапки документа (номер, дату, поставщика, AWB и т.д.). - metadata может быть пустым. - table.columns — заголовки колонок; могут содержать слипшиеся слова или символ "#" распознанный как "1". Могут быть пустыми — тогда определи смысл колонок по данным. - table.rows — массив строк; каждая строка = массив значений в порядке columns. - totals_raw — итоговые значения с последней страницы (может быть пустым). - sections и notes обычно пустые. <сырой_ocr_json> {raw_json} </сырой_ocr_json> <целевая_json_схема> {target_schema} </целевая_json_схема> === ШАГ 1: АНАЛИЗ ДАННЫХ (выполни перед генерацией правил) === Прежде чем писать правила, ответь себе на вопросы: 1. КОЛОНКИ: если table.columns пуст, определи смысл каждой колонки по данным. Для каждого индекса укажи: "колонка N = что это" (число коробок, маркировка, название сорта, категория, бантчи, стебли, цена за штуку, итого, номер заказа и т.д.). 2. ТИП ИНВОЙСА — определи по структуре OCR: А) Составные коробки (заголовок + детали): есть строки-заголовки (MIXED BOX) с общими суммами, за ними строки-детали с отдельными сортами. Б) Плоские: каждая строка = одна позиция = одна коробка. Нет MIXED BOX. Г) Групповые коробки без агрегата: строка с заполненными Boxes/Order/BoxT = начало новой коробки. Эта же строка = первый товар. Последующие строки с пустыми Boxes/Order/BoxT = товары в той же коробке. box_number наследуется от строки-заголовка. В) Секции по фермам: строки группируются по фермам/HAWB. Д) Гибридный (составные + individual в одном инвойсе): строки с PRODUCT = "MIXED BOX" открывают группы с деталями ниже, а строки с конкретным сортом (не "MIXED BOX"), непустым # и непустым LABEL являются individual box и должны маппиться в item напрямую. 3. ФИЗИЧЕСКИЕ КОРОБКИ: найди колонку с количеством физических коробок. Колонка # (часто col 0) содержит КОЛИЧЕСТВО физических коробок в строке. Значение может быть 1, 2, 6, 14 и т.д. Значение # в строке TOTALS = общее число коробок в инвойсе. ВАЖНО: items[].boxes = значение из этой колонки (НЕ хардкод 1). Не путай # с числом бантчей/сортов внутри коробки. 4. MIXED BOX — определи, что это значит в данном инвойсе: - Вариант 1: одна физическая коробка со смешанными сортами внутри (строки-детали = сорта в одной коробке). В этом случае каждая строка-деталь = 1 item с boxes=1, данные берутся напрямую из строки без деления. - Вариант 2: одна строка описывает N одинаковых физических коробок (boxes > 1 и нет строк-деталей). В этом случае раскрой: создай N items с boxes=1, поделив quantity и total_price на N. Подсказка: если после деления quantity получаются дробные стебли (3.125, 1.786) — ты неправильно определил что делить. Стебли всегда целые или кратные размеру бантча. - Вариант 3: строка с # > 1 и конкретным сортом (не "MIXED BOX") без строк-деталей — это N одинаковых коробок с одним сортом. Раскрой в N items с boxes=1: quantity_each = quantity / N, total_price_each = total_price / N. Генерируй уникальный box_number для каждого раскрытого item. === ШАГ 2: БИЗНЕС-ПРАВИЛА === РАСКРЫТИЕ МУЛЬТИБОКСА (ОБЯЗАТЕЛЬНО): Мультибоксы всегда должны раскрываться — позиции расписываются подробно по сортам. Если одна строка = N одинаковых физических коробок без строк-деталей — раскрой в N отдельных items[] с boxes=1. Пример: строка "5 boxes, 1500 stems, $750" → 5 строк по boxes=1, quantity=300, total_price=150. При раскрытии мультибокса каждый раскрытый item должен получить УНИКАЛЬНЫЙ box_number. Если исходный номер "ORD123" и boxes=3, генерируй "ORD123-1", "ORD123-2", "ORD123-3". Если MIXED BOX = одна коробка с разными сортами (вариант 1) — каждая строка-деталь уже описывает один сорт с готовыми quantity и total_price. ЗАГОЛОВКИ МУЛЬТИБОКСОВ — НЕ ВКЛЮЧАТЬ В ITEMS (КРИТИЧНО): Строки-заголовки с PRODUCT = "MIXED BOX" (или аналогичные агрегатные строки) НЕ создают items. Они используются ТОЛЬКО как источник для наследования box_number, boxes, plantation при обработке следующих строк-деталей. В items[] попадают ТОЛЬКО строки-детали с конкретным сортом. Строка с category="MIXED BOX" и variety="MIXED" НЕ ДОЛЖНА появляться в items[]. Это критично, потому что items[] напрямую выгружаются в Excel — заголовки мультибоксов в Excel попадать не должны. МАРКИРОВКА (box_marking) — КРИТИЧНО: Поле box_marking — это клиентская маркировка, которая выводится в колонку "Маркировка" в Excel. Это НЕ тип/размер коробки (не "HB FF JUMBO", не "HB FF2 (120*35*35)"). Тип коробки в box_marking ставить ЗАПРЕЩЕНО. box_marking = метка получателя/клиента. Искать в документе одно из: "TDA", "BUKETOPT", или что-то похожее на фамилию на латинице (например "IVANOV", "PETROV"). Источники для поиска: колонка MARK/Mark Code, секция SHIP CUSTOMER, header_text, metadata, поле "TO BILL CUSTOMER". Если в документе по смыслу ничего подходящего не находится — ставить "TDA" по умолчанию. Правило для каждого item: box_marking = найденная маркировка (одинаковая для всех items одного инвойса). НОМЕР КОРОБКИ (box_number): 1. Если в таблице есть колонка Order/LABEL с уникальными номерами для каждой физической коробки — используй их. 2. Если уникальных номеров нет (все коробки идут под одним номером "1", или колонка LABEL пуста) — генерируй уникальные номера. Формат: первая буква названия поставщика + порядковый номер коробки (F1, F2, F3, F4 для FRAMA; Q1, Q2, Q3 для QUITO и т.д.). 3. ПРОВЕРКА: количество уникальных box_number в items[] ДОЛЖНО совпадать с summary.total_boxes. Если в инвойсе total_boxes = 4, значит должно быть ровно 4 разных box_number. Если OCR показывает "1" в колонке # для каждой коробки — это не значит, что номер коробки "1"; это значит, что в каждой строке 1 физическая коробка, и каждая из них должна получить свой уникальный box_number. 4. Строки-детали внутри одной коробки (MIXED BOX) наследуют box_number от строки-заголовка этой коробки. ПОДСЧЁТ summary.total_boxes: Приоритеты: 1. Строка TOTALS: значение в col 0 (#) = сумма физических коробок. 2. Если есть секция "Total Full / F / H / Q / E" — total_boxes = сумма F+H+Q+E (Full, Half, Quarter, Eighth). 3. Если BOX N° содержит только group ID (001, 003, 005 — не для каждой физической коробки), а total_boxes берётся из секции типов, то BOX N° = group identifier. 4. Если total_boxes нигде явно не указан — посчитай количество строк-заголовков коробок (строки с непустым # в колонке 0) и используй сумму их значений. ПОЛЕ plantation: plantation ВСЕГДА равна названию поставщика (supplier). Не бери из колонки FARM, не используй коды плантаций. plantation = supplier. ПОЛЯ category, variety, length: - category: из колонки SPECIES/Species (ROSES, ALSTRO, CARNATION). Если нет отдельной колонки — определи по описанию. - variety: из колонки VARIETY/Description. Если встроена в PRODUCT ("AMIRA 60CM N 25ST QUCT") — извлеки только название сорта ("AMIRA"). - length: из колонки LEN/Length/Lenght. Если встроена в название ("60CM") — извлеки число. Если несколько значений ("40CMN 25ST QUOT") — бери первое число перед CM. === ШАГ 3: САМОПРОВЕРКА === Мысленно примени свои правила к первым 3-5 строкам данных и проверь: - quantity в каждом item — целое число или кратное размеру бантча (25, 10). Дробные стебли (3.125, 1.786, 14.06) — признак ошибки в правилах. - unit_price — разумная цена за стебель ($0.20–$5.00 для цветов). - sum(items[].quantity) ≈ totals.total_stems (если есть в данных). - sum(items[].total_price) ≈ totals.total_amount (если есть в данных). - sum(items[].boxes) должно совпадать со значением # в строке TOTALS (если есть в данных). - count(unique items[].box_number) = summary.total_boxes (ОБЯЗАТЕЛЬНО должно совпадать). - Каждый item должен иметь непустой box_number. - В items[] НЕТ строк-заголовков MIXED BOX (category="MIXED BOX", variety="MIXED") — только строки-детали с конкретным сортом. - plantation = supplier для каждого item. - box_marking = клиентская маркировка (TDA/BUKETOPT/фамилия), НЕ тип коробки. Если ничего не найдено — "TDA". Если проверка не сходится — пересмотри правила перед ответом. === ФОРМАТ ОТВЕТА === Верни JSON с единственным ключом: - "transform_rules": массив коротких правил (строки на русском языке) КРИТИЧНО — правила будут применяться к НОВЫМ документам того же поставщика, где конкретные значения (номера инвойсов, даты, суммы, AWB, количество стеблей) будут ДРУГИМИ. Поэтому: ЗАПРЕЩЕНО включать в правила: - Конкретные значения из образца: номера инвойсов, даты, AWB, суммы, количества - Абсолютные индексы строк (индекс 23, индекс 30) — число строк в таблице разное - Конкретные названия стран или имена клиентов из данного документа ОБЯЗАТЕЛЬНО сгенерируй правила для processing_report: - sum_stems = sum(items[].quantity) - sum_amount = sum(items[].total_price) - sum_boxes = количество УНИКАЛЬНЫХ непустых items[].box_number - expected_stems = summary.total_stems - expected_amount = summary.total_amount - expected_boxes = summary.total_boxes - items_count = len(items) - status: "ok" если всё сошлось, "warning" если расхождения, "error" если ключевые данные не извлечены - discrepancies: список расхождений (отсутствие box_number, несовпадение сумм и т.д.) - notes: тип инвойса, кол-во коробок, проблемы при извлечении, раскрытие мультибокса, текст "Notice:" если есть Адаптируй формулы к реальной структуре данных. Если коробки распознаются в нестандартных ячейках — опиши откуда именно брать значения и как считать. ОБЯЗАТЕЛЬНО описывать: - МЕТОД извлечения: "найти строку с меткой 'MAWB'", "последняя строка таблицы", "строка, содержащая 'Totals'" - Ссылки на КОЛОНКИ по имени и индексу (колонки стабильны между документами) - ПАТТЕРНЫ поиска: "строка, где колонка 0 содержит 'DAU-'", а не конкретное значение Пример ПЛОХОГО правила: "summary.total_stems: значение 700 из строки индекс 30." Пример ХОРОШЕГО правила: "summary.total_stems: найти строку с меткой 'Totals', взять значение из колонки 1, преобразовать в число." Требования к правилам: 1. Ссылайся на названия колонок из table.columns (по индексу и по имени). 2. Опиши как заполнить КАЖДОЕ поле целевой схемы, используя метод извлечения, а не конкретные значения. 3. Укажи определённый тип инвойса (А/Б/В) и логику распознавания строк. 4. Укажи МЕТОД извлечения для invoice_number, date, supplier, country, awb: по какой метке/паттерну найти значение. Если в сыром OCR есть поле header_text — используй его как приоритетный источник для полей шапки (номер, дата, поставщик, страна, AWB). 5. Опиши логику box_number: реальный номер из данных или генерация PREFIX+seq. Количество уникальных box_number ОБЯЗАНО совпасть с total_boxes. 6. Если применяется раскрытие мультибокса — опиши логику. Если не применяется — явно укажи, что каждая строка-деталь = 1 item без деления. Заголовки MIXED BOX НЕ включать в items. 7. Числовые поля — числа, не строки. Убери "$", "EUR", запятые. 8. Правила валидации: sum(items[].total_price) ≈ summary.total_amount, sum(items[].quantity) ≈ summary.total_stems, count(unique items[].box_number) = summary.total_boxes. 9. plantation = supplier (всегда). 10. box_marking = клиентская маркировка (TDA/BUKETOPT/фамилия на латинице). Это НЕ тип коробки. Если ничего релевантного не найдено — обязательно ставить "TDA". 11. Верни только валидный JSON.
Используется кнопкой «Подсказать rules».
Transform rules (JSON массив строк)
[ "Тип инвойса: плоский (Б) - каждая строка таблицы описывает одну позицию, нет строк-заголовков MIXED BOX.", "invoice_number: найти в metadata или шапке инвойса строку, содержащую 'Invoice No' или 'Invoice Number', взять значение после двоеточия.", "invoice_date: найти в metadata или шапке инвойса строку, содержащую 'Date' или 'Invoice Date', взять значение после двоеточия.", "supplier: найти в metadata или шапке инвойса строку, содержащую 'Supplier' или 'From', взять значение после двоеточия.", "country: определить по коду страны в адресе поставщика или по названию страны в metadata.", "awb: найти строку, содержащую 'AWB' или 'MAWB', взять значение после двоеточия.", "items: обработать каждую строку таблицы, кроме строк с 'Totals' в любой колонке.", "items[].box_number: взять значение из колонки 'Order' (индекс 1). Если пусто, сгенерировать: первые 3 буквы plantation + порядковый номер строки.", "items[].box_marking: взять значение из колонки 'Box T.' (индекс 2).", "items[].boxes: взять значение из колонки 'Boxes' (индекс 0), преобразовать в число. Если пусто, использовать 1.", "items[].plantation: взять значение из колонки 'Id.' (индекс 3). Если содержит код (например, 'PC602017194'), извлечь только название плантации.", "items[].category: определить по описанию в колонке 'Description' (индекс 4) - искать ключевые слова типа 'ROSES', 'ALSTRO', 'CARNATION'.", "items[].variety: взять значение из колонки 'Description' (индекс 4), извлечь только название сорта (первое слово до пробела).", "items[].length: взять значение из колонки 'Len.' (индекс 5). Если содержит 'CM', извлечь число перед 'CM'.", "items[].quantity: взять значение из колонки 'Bun/Box' (индекс 6), преобразовать в число.", "items[].unit_price: взять значение из колонки 'Price' (индекс 9), удалить '$', 'EUR', запятые, преобразовать в число.", "items[].total_price: взять значение из колонки 'Total' (индекс 10), удалить '$', 'EUR', запятые, преобразовать в число.", "summary.total_boxes: найти строку с 'Totals' в любой колонке, взять значение из колонки 'Boxes' (индекс 0), преобразовать в число.", "summary.total_stems: найти строку с 'Totals' в любой колонке, взять значение из колонки 'Bun/Box' (индекс 6), преобразовать в число.", "summary.total_amount: найти строку с 'Totals' в любой колонке, взять значение из колонки 'Total' (индекс 10), удалить '$', 'EUR', запятые, преобразовать в число.", "Проверка: сумма items[].total_price должна быть равна summary.total_amount, сумма items[].quantity должна быть равна summary.total_stems.", "Раскрытие мультибокса не применяется - каждая строка таблицы описывает одну позицию без деления." ]
Подсказать rules
Test (Pass 2)
Сохранить rules в интент
Сырой OCR JSON (Pass 1)
{}
Результат (Pass 2)
История (последние 20)
ID
Файл
Дата