---
name: 1c-epf
description: >
  Работа с 1С: внешними обработками и отчётами (.epf / .erf) и конфигурацией
  «НоваяКонфа» пользователя (Бурдуков С.Ю). Брать ВСЕГДА, когда задача касается:
  распаковки/сборки .epf/.erf через v8unpack; доработки модулей .bsl; правки
  ФОРМ обработки (добавить/починить кнопку, команду, реквизит формы; сделать
  таблицу редактируемой/только-просмотр; картинка на кнопке; «Связанные
  документы»/общая команда); сборки обработки с нуля из шаблона; круглого
  round-trip diff перед сдачей. Триггеры: «обработка 1С», «.epf», «.erf»,
  «доработай обработку», «распакуй/собери обработку», «добавь кнопку/реквизит/
  команду в форму», «таблица только просмотр / сделай редактируемой»,
  «связанные документы», «v8unpack», «НоваяКонфа», «ОбработкиЭкспорт», имена
  вроде «аСверкаРеестровыхКомпаний», «КомиссияКлиентов», «ОтчетПоДаннымЗаказа».
  НЕ для прошивок ЭБУ (.bin → скилл ecu-firmware) и не для PDF.
---

# Работа с внешними обработками/отчётами 1С (.epf / .erf)

Конфигурация и обработки пользователя (Бурдуков С.Ю). Конфигурация — обычная
бухгалтерия-подобная «НоваяКонфа». Формат хранения распакованной обработки —
**проприетарный вложенно-массивный JSON v8unpack**, НЕ читаемый XML EDT.

## 0. Перед началом — где файлы и доступ

- **Внешние обработки**: `~/Downloads/ОбработкиЭкспорт/` (Finder показывает
  «Загрузки» — это локализованное имя `Downloads`, реального пути `~/Загрузки`
  НЕТ). Если задача про 1С — сначала смотри туда.
- **Конфигурация** (XML-выгрузка): `~/Downloads/ОбработкиЭкспорт/НоваяКонфа/`.
  Тут лежат общие команды (`CommonCommands/`), документы (`Documents/`),
  справочники и т.д. — используется как **источник GUID'ов типов и образцов**.
- Если `.epf`/`.erf` пришёл из другого места (например `~/Разработки/`) —
  работай оттуда же, **не переноси** без явной просьбы.
- **НИКОГДА не открывай 1С визуально для проверки** (правило пользователя,
  навсегда): GUI 1С:Предприятия на этом MacBook рендерится криво и для контроля
  бесполезен. Headless-проверка — только **batch-агентом** (§6, сборка + `log`
  Конфигуратора); интерактивный UI смотрит сам пользователь.
- **macOS TCC-грабли**: эта сессия Claude — вложенный бинарь
  `com.anthropic.claude-code`, а не `/Applications/Claude.app`
  (`com.anthropic.claudefordesktop`). Full Disk Access выдают **по bundle ID**,
  поэтому галка на `Claude.app` НЕ покрывает доступ к `~/Downloads`. Симптом:
  `ls ~/Downloads/... → Operation not permitted`. Лечение: System Settings →
  Privacy & Security → Full Disk Access → `+` → `Cmd+Shift+G` →
  `~/Library/Application Support/Claude/claude-code/<версия>/claude.app` →
  включить → Cmd+Q и перезапустить. (Подробно: `reference/rules_paths_markers.md`.)

## 1. Маркеры правок — ОБЯЗАТЕЛЬНО в .bsl (кроме greenfield)

⚠️ **Исключение — обработки, разрабатываемые «с нуля»** (greenfield, напр.
аСверкаРеестровыхКомпаний, СверкаРеестровыхКомпанийВ1С): **ровно ОДИН** личный
комментарий-маркер — в самой первой строке **модуля объекта** (`Ext/ObjectModule.bsl`),
и БОЛЬШЕ НИГДЕ (правило юзера, твёрдо). Ни по процедурам, ни по строкам, ни в модулях
форм маркеры НЕ ставим — код чистый. Если юзер сказал «разрабатываем с нуля» /
«комментарии только один раз сверху» — markers off везде, кроме одной шапки ObjectModule;
при чистке снести все рассыпанные `//+ //- Бурдуков DEVACC`, оставив один сверху.
Маркеры по правилам ниже — ТОЛЬКО для правок **существующего чужого прода** (история изменений).

Любая правка модуля 1С (конфигурация ИЛИ обработка) обрамляется личным
комментарием. Дата — текущая, `DEVACC-<номер>` — если пользователь назвал задачу.

- Открывающий: `//+ Бурдуков С.Ю <ДД.ММ.ГГГГ> DEVACC-<номер>`
- Закрывающий: `//- Бурдуков С.Ю <ДД.ММ.ГГГГ> DEVACC-<номер>`
- **1 строка** добавлена → только `//+` (без закрывающего).
- **Целая процедура/функция** добавлена → только `//+` сверху (без закрывающего).
- **Блок из нескольких строк** изменён → оба маркера: `//+` до, `//-` после.
- Чужие маркеры НЕ трогать, не объединять, не переносить — это история правок.
- **Старый рабочий/нерабочий код при правке НЕ удаляем — закомментируем** (правило
  юзера, твёрдо): заменяемые строки оставляем как `//<старая строка>` внутри блока
  маркеров `//+ … //-`, новый код пишем рядом. Удаление допустимо только если юзер
  явно попросил. (Кейс DEVACC-738: фильтр `аЕстьПоступление` → `ВЫБОР КОГДА … В
  (подзапрос ПоступлениеТоваровУслуг) … КОНЕЦ`, старая строка закомментирована.)
- **Правки ФОРМ** (`Form.elem.json`/`Form.json`) — бинарная структура, текстовых
  комментариев там нет → маркеры в форму не ставятся (только в .bsl-модули).

## 2. Канонический workflow (.epf/.erf)

Всё через `tools/epf.sh` (обёртка над v8unpack; ставит его при отсутствии).

```bash
SK=~/.claude/skills/1c-epf
# 1) распаковать в исходники
$SK/tools/epf.sh unpack <файл.epf> /tmp/work/orig
# 2) РАБОЧАЯ КОПИЯ (правь её, orig храни как эталон для diff)
cp -r /tmp/work/orig /tmp/work/edit
#    ... правки: .bsl-модули (с маркерами) и/или формы (через tools/form_edit.py) ...
# 3) собрать + ОБЯЗАТЕЛЬНЫЙ round-trip (pack → re-unpack → diff)
$SK/tools/epf.sh roundtrip /tmp/work/edit /tmp/work/out.epf
# 4) сверить, что изменилось ровно то, что хотел:
diff -rq /tmp/work/orig /tmp/work/edit
```

`roundtrip` пересобирает, тут же распаковывает обратно и делает `diff -rq` —
различия должны соответствовать только сознательным правкам. **Без чистого
round-trip обработку не сдавать.** Размер итогового .epf может чуть отличаться
от исходного (другой deflate) — это нормально, важна сверка ИСХОДНИКОВ.

Перед перезаписью оригинала — **бэкап**: `cp -p file.epf file.epf.bak-pre-<задача>-<дата>`.
Самопроверка по существу: собери/проверь исходники **batch-агентом** (§6, порт 8788) —
Конфигуратор либо собирает `.epf`, либо возвращает `log` с ошибкой (ground truth
для синтаксиса/структуры). **Визуально 1С НЕ открывать** — GUI 1С:Предприятия
на этом MacBook рендерится криво и для проверки бесполезен (правило пользователя,
навсегда). Интерактивное поведение UI (кнопка реально видна/нажимается, таблица
редактируется в открытой форме) проверяет ТОЛЬКО пользователь — сформулируй ему,
что именно глянуть. Что не смог проверить сам — скажи явно.

## 3. Правка ФОРМ — безопасный способ

`Form.elem.json` — **валидный JSON**, и v8unpack принимает его переформатированным
Python'ом без потерь (проверено). Поэтому **правь формы программно через Python
`json.load`/`json.dump(..., ensure_ascii=False, indent=2)`**, а НЕ текстовой
правкой — это убирает риск разбалансировки скобок в 4000+ строках вложенных
массивов. Инструменты:

```bash
# изучить форму: реквизиты, дерево, таблицы, кнопки и их привязки
$SK/tools/form_inspect.py <Form.elem.json>
$SK/tools/form_inspect.py <Form.elem.json> --buttons      # кнопки + контейнер + привязка команды
$SK/tools/form_inspect.py <Form.elem.json> --readonly <ТаблицаИмя> --siblings <др.elem.json...>

# добавить реквизит формы, скопировав ТИП из рабочего образца
$SK/tools/form_edit.py add-attribute --form F.json --name ТекДанныеДляДокСтруктуры \
    --id 5 --template-form REF.json --template-attr ТекДанныеДляДокСтруктуры

# выставить «Только просмотр» таблицы (value: 1=только просмотр, 0=редактируема)
$SK/tools/form_edit.py set-table-readonly --form F.json --table СписокСчетовПоКонтрагенту --value 0

# произвольный точечный флаг в raw таблицы (когда индекс известен/найден)
$SK/tools/form_edit.py set-flag --form F.json --table T --index 36 --value 0
```

Подробности формата (props/tree/data/commands, кодировка кнопок, общие команды,
поиск флага readonly триангуляцией) — **`reference/elem_json_format.md`**. Читай
его перед нетривиальной правкой формы.

## 3b. Правка ОБЪЕКТА — типы колонок ТЧ, кнопки, имя/синоним

Когда переделываешь обработку под новую задачу (другие колонки/типы/кнопки),
правь объектные метаданные через **`tools/obj_edit.py`** и команды/кнопки через
**`tools/form_cmd.py`**. Формат и грабли — **`reference/object_metadata.md`**
(читай перед правкой). Выверено на кейсе `УстановкаЮрЛицаИДоговораГостиницы`.

```bash
# увидеть реквизиты ТЧ (имя, binding-uuid, тип) и TypeId типов из конфигурации
$SK/tools/obj_edit.py attrs <ExternalDataProcessor.json>
$SK/tools/obj_edit.py typeid НоваяКонфа Catalog.Организации          # → TypeId
# ПЕРЕТИПИЗИРОВАТЬ колонку ПО МЕСТУ (binding-uuid сохраняется → колонка формы следует сама)
$SK/tools/obj_edit.py retype <obj.json> --uuid <binding-uuid> --name Метка --syn Метка --type bool
$SK/tools/obj_edit.py retype <obj.json> --uuid <binding-uuid> --name СчетРасчетов --syn "Счет расчетов" --type ref:<TypeId>
# имя обработки во ВСЁМ файле (+header!) и синоним (name/name2 — производные, не пакуются)
$SK/tools/obj_edit.py rename <obj.json> --name УстановкаX --syn "Установка X"

# команды/кнопки формы
$SK/tools/form_cmd.py list <Form.elem.json>
$SK/tools/form_cmd.py set-title <elem.json> --cmd ПоказатьСчета --title "Поставить юр лицо и договор гостиницы"
$SK/tools/form_cmd.py add-cmd  <elem.json> --name ПоставитьМетку --title "Поставить метку"   # +процедура в модуль!
$SK/tools/form_cmd.py rebind   <elem.json> --table T --btn <uuid-сортировки> --to-cmd 7 --use-cmd-title
```

**Ключевая идея:** число типизированных колонок = число `'37'`-колонок шаблона;
НЕ добавляй колонки/реквизиты вслепую (состав ТЧ в объектном файле размазан) —
**перетипизируй существующие по месту**. Имя/синоним живут в `header` (поля
name/name2 — производные, пакер их игнорит). Рецепт «переделать под новую задачу»
целиком — в `reference/object_metadata.md`.

## 4. Частые задачи (рецепты)

- **Кнопка «Связанные документы» (общая команда) не появляется** → почти всегда
  её параметр команды `["1",["<id>"]]` ссылается на реквизит формы, которого
  НЕТ. Добавь реквизит с этим id (тип = тип документа строки, бери из образца),
  и убедись, что модуль при активации строки пишет туда текущий документ
  (`ТекДанныеДляДокСтруктуры = ТекСтрока.<КолонкаСсылки>`). Картинку кнопка берёт
  от общей команды (`CommonPicture.СтруктураПодчиненности`). Рабочий образец:
  `ОтчетПоДаннымЗаказа.epf`. Кейс целиком: `reference/elem_json_format.md` §«Кнопка».
- **Таблицы по умолчанию РЕДАКТИРУЕМЫЕ + корректные типы** (устойчивое правило юзера,
  [[onec-epf-fields-editable]]): любую таблицу/колонку оставляем без «Только просмотр» и с
  правильным типом (ref/Число+квалификаторы/Дата, не строка-заглушка), пока юзер ЯВНО не
  попросит ReadOnly. Снять: `set-table-readonly --value 0` (флаг таблицы `[36]` перекрывает
  поля) **И** `set-fields-readonly --value 0` (поле-уровневый флаг `[14]` колонки `'37'`).
  Индекс табличного флага — триангуляцией (`--readonly`); если родственной одноимённой
  таблицы нет, проверь оба флага вручную.
- **«Связанные документы» открывают чужой/«хаотичный» документ** → обработчик активации
  должен ВСЕГДА писать документ ТЕКУЩЕЙ строки в реквизит (`ТекДанныеДляДокСтруктуры =
  ТекущиеДанные.<КолонкаДок>`), без условий по имени колонки. Лучше `ПриАктивизацииСтроки`
  (не `ПриАктивизацииЯчейки` с фильтром по ячейке — он обновляет реквизит лишь на части
  колонок → стале/чужой документ). Кейс аСверкаРеестровыхКомпаний (список счетов).
- **Найти, как сделано «как в других обработках»** → grep по `НоваяКонфа/` и
  распакованным `*_src`/`*_unpacked` соседним обработкам; общие команды лежат в
  `НоваяКонфа/CommonCommands/<Имя>.xml` (там Picture, CommandParameterType, uuid).

## 4b. Перед стартом сборки новой greenfield-обработки — задай вопросы

Не начинай писать XML, пока не закрыты (явным ответом пользователя или однозначно
следуют из задачи) следующие пункты — каждый из них хоть раз уже стоил лишней
пересборки (кейс `РеализацияУслугиПоступлениеБайко`, `[[onec-epf-build-checklist]]`
в Obsidian `Decisions.md` D20):

1. Точный список колонок таблицы и типы — НЕ добавлять «на всякий случай» колонки из
   обработки-образца, которых нет в запросе пользователя.
2. Точные поля связи между документами — сверить с актуальными `Documents/*.xml` в
   `НоваяКонфа/`, не полагаться на память/образец без проверки.
3. Тип соединения при сверке нескольких документов: INNER (только полные пары) или
   LEFT/FULL (показать все, включая недостающую сторону)?
4. Гранулярность строки таблицы (один документ / одна строка заказа / другое).
5. Отборы формы — нужны ли значения **по умолчанию** (код организации и т.п.)? Спросить
   сразу, не ждать правки постфактум.
6. Номер `DEVACC-<N>` — если не назван, маркер пишется без номера, но стоит уточнить.
7. Какие стандартные команды таблицы должны быть ЗАКРЕПЛЕНЫ на видимой панели (не только
   в «Ещё») — например сортировка. См. грабли ниже (§5, «Кнопки стандартных команд»).
8. Маркер greenfield копировать ТОЛЬКО в `Ext/ObjectModule.bsl`, даже если у обработки-образца
   он (по старой практике) стоит и в модуле формы тоже — маркер новой обработки решается
   заново по правилу §1, не копируется построчно.
9. `ПараметрыРегистрации.Наименование` в `СведенияОВнешнейОбработке()` — заполнить тем же
   многословным человекочитаемым текстом, что и XML `<Synonym>`/«Представление» команды, а
   НЕ техническим именем объекта (`РеализацияУслугиПоступлениеБайко`) — это отдельное поле,
   оно и попадает в список «Дополнительные отчёты и обработки», которое видит пользователь.

## 5. Собрать обработку «с нуля»

**Предпочтительно — XML-выгрузка + сборка платформой** (особенно если нужен свой
набор/число колонок ТЧ, отличное от готовых шаблонов): авторишь человекочитаемый
XML (`Имя.xml` + `Имя/Forms/Форма/Ext/Form.xml` + `Module.bsl` + `Ext/ObjectModule.bsl`)
и собираешь настоящей платформой через `onec-build-agent` (§6,
`POST /build?uid=1005954706`). Платформа = ground truth. Полный рецепт, формат каждого
файла, типы, цвет кнопки (BGR-round-trip), грабли — **`reference/xml_build_from_scratch.md`**.
Готовый генератор-болванка — **`examples/gen_xml_epf_from_scratch.py`** (копируй, правь
UUIDs/реквизиты/ТЧ/форму/модуль). Образец структуры в `ОбработкиЭкспорт/` —
`аСверкаРеестровыхКомпаний` (его `.xml` + каталог = эталон формата). Выверено на
`ИсправлениеСчетаРасчетовАгентскихУслуг` (7 колонок, фильтры, 5 команд, зелёная кнопка).

**Альтернатива — от v8unpack-шаблона** (когда колонок хватает и правок мало): взять
ближайшую обработку из `ОбработкиЭкспорт/`, скопировать .epf, распаковать, переделать
имя/синоним (`obj_edit rename`, header!), объектный модуль, формы, перетипизировать
колонки по месту (`obj_edit retype`). Затем `roundtrip`. Детали — `reference/elem_json_format.md`
§«С нуля от шаблона» + `reference/object_metadata.md`.

⚠ Перед опорой на код старых обработок — **сверяй имена полей с актуальной
`НоваяКонфа/Documents/*.xml`**: поля переименовываются (напр. агентское
`СчетУчетаРасчетовСАгентом` → ныне `СчетРасчетов`, тип `ChartOfAccountsRef.Хозрасчетный`).
Связь Поступление↔Реализация в этих обработках — через `ЗаказПокупателя`
(`Поступление.Сделка`/`Услуги.ДокументЗаказ` ↔ `СчётНаОплату.ЗаказПокупателя` ↔
`Реализация.Услуги.СчётНаОплатуПокупателю`), НЕ через `Основание`.

## 6. Сборка и валидация настоящей платформой (onec-build-agent)

На этом MacBook стоит 1С 8.3.27 с лицензией и работает HTTP-агент
(`launchd com.cebers.onec-build-agent`, порт 8788, Bearer из
`~/Services/onec-build-agent/.token`) поверх batch-Конфигуратора:

```bash
TOKEN=$(cat ~/Services/onec-build-agent/.token)
# zip XML-выгрузки («выгрузить в файлы»: Имя.xml + каталог Имя/) → готовый .epf/.erf
curl -s -X POST -H "Authorization: Bearer $TOKEN" \
  --data-binary @src.zip -o out.epf "http://127.0.0.1:8788/build?uid=1005954706"
# .cf/.cfe → zip XML-дескрипторов объектов (индекс метаданных)
curl -s -X POST -H "Authorization: Bearer $TOKEN" \
  --data-binary @conf.cf -o dump.zip "http://127.0.0.1:8788/dump-config?ext=cf"
# готовый .epf/.erf → zip полной XML-выгрузки (для правки + обратной сборки через /build,
# обходит ненадёжную самопересборку v8unpack, DEVACC-776)
curl -s -X POST -H "Authorization: Bearer $TOKEN" \
  --data-binary @in.epf -o dump.zip "http://127.0.0.1:8788/dump-epf?ext=epf&uid=1005954706"
```

Грабли: zip собирать ТОЛЬКО питоном (CLI `zip` кладёт кириллицу в cp437);
422 = платформа отвергла исходники, в JSON-ответе поле `log` Конфигуратора —
это ground truth для правок. Обработки со ссылками на типы конфигурации
(CatalogRef.*) собираются только в базе с конфигурацией юзера
(`ib_user/.ready`); пустая `ib` — для самодостаточных отчётов/обработок.
Прямой batch без агента: `1cv8 DESIGNER /F <ib> /DisableStartupDialogs
/LoadExternalDataProcessorOrReportFromFiles <root.xml> <out.epf> /Out <log>`
(лаунчер детачится — ждать `pgrep -f` по уникальному tmp-пути).

🛑 **`uid=1005954706` ОБЯЗАТЕЛЕН на `/build` И на `/dump-epf` — на ОБОИХ, всегда,
без исключений** (DEVACC-757, 27.07.2026, `СписокРеализаций.epf`). Без `uid`
`/dump-epf` использует пустую безссылочную базу — **типы Ref (CatalogRef.*,
DocumentRef.*) молча деградируют в `xs:string`** (или Number), причём:
- round-trip `epf.sh` — ЧИСТЫЙ (v8unpack сравнивает то же самое, что сам испортил);
- `/dump-epf` возвращает 200, а не ошибку — порча НЕ ловится HTTP-статусом;
- итоговый `.epf` из `/build` (даже с правильным `uid`!) собирается БЕЗ ошибок —
  порча уже запечена во ВХОДНОМ XML, `/build` её просто честно воспроизводит.
Симптом у пользователя: «отбор/поле сломалось» (превратилось в текст вместо
выбора из справочника/документа) — БЕЗ единой ошибки на всём пути проверки.
**Профилактика:** каждый `/dump-epf` без явного `uid` — красный флаг, отменить
и перезапустить с `uid`. **Лечение, если уже пропустил:** сравнить КАЖДЫЙ
объектный реквизит (`obj_edit.py attrs` или ручной обход `'27'`-узлов по uuid)
и КАЖДЫЙ реквизит формы (`props`) — Pattern-тип орига vs текущего; расхождение
чинится точечной заменой Pattern-узла из чистого бэкапа (uuid одинаковый —
привязка не рвётся). Делать это ПОСЛЕ любого `/dump-epf`→`/build` цикла, не
только один раз в конце — сравнивай КАЖДЫЙ финальный артефакт с pristine-эталоном
(оригинальным бэкапом), а не только с предыдущим шагом (иначе порча тихо
наследуется дальше по цепочке правок).

## Самопроверка скилла (после применения)
Прошёл ли round-trip чисто? Изменилось ровно задуманное (semantic diff)? Маркеры
в .bsl по правилам? Сделан бэкап перед перезаписью? Прогнал batch-агент (§6) —
сборка/лог чистые? **НЕ пытался открыть визуальный GUI 1С** (на macOS не работает)?
Сформулировал пользователю, что проверить интерактивно? Если что-то «нет» —
задача не закрыта.
