---
name: 1c-queries
description: Выполняет запросы к базе данных 1С:Предприятие через HTTP-сервис. Используй этот навык когда нужно получить данные из 1С, выполнить анализ данных в базе 1С, написать или проверить запрос на языке 1С.
---

# Выполнение запросов к базе 1С:Предприятие

## Обзор

Этот навык позволяет выполнять запросы к базе данных 1С:Предприятие через HTTP-сервис. Используй его для анализа данных и получения информации из 1С.

## Скрипты для выполнения запросов

Скрипты читают `.env` автоматически и отправляют запрос в 1С.

**Windows (PowerShell):**

```powershell
.\.claude\skills\1c-queries\scripts\query-1c.ps1 -Query 'ВЫБРАТЬ Кассы.Код, Кассы.Наименование ИЗ Справочник.Кассы КАК Кассы ГДЕ Кассы.Наименование = "Основная касса"'
```

**Linux (bash):**

```bash
./.claude/skills/1c-queries/scripts/query-1c.sh 'ВЫБРАТЬ Кассы.Код, Кассы.Наименование ИЗ Справочник.Кассы КАК Кассы ГДЕ Кассы.Наименование = "Основная касса"'
```

## Формат ответа

- Ответ в формате TSV (Tab-Separated Values)
- Первая строка — заголовки колонок
- Следующие строки — данные
- При ошибке: HTTP 400 с описанием ошибки

---

## КРИТИЧЕСКИЕ ОГРАНИЧЕНИЯ

### 1. НЕЛЬЗЯ использовать параметры через "&"

❌ **НЕПРАВИЛЬНО:**
```
ГДЕ Товар = &Товар
```

✅ **ПРАВИЛЬНО:**
```
ГДЕ Товар.Наименование = "iPhone 17 Pro Max"
```

### 2. ОБЯЗАТЕЛЬНО использовать ПЕРВЫЕ N

Всегда ограничивай количество записей:

```sql
ВЫБРАТЬ ПЕРВЫЕ 100
    Документы.Номер,
    Документы.Дата
ИЗ
    Документ.РеализацияТоваровУслуг КАК Документы
```

### 3. Сравнение ссылочных полей — только через примитивные реквизиты

❌ **НЕПРАВИЛЬНО** (сравнение ссылок напрямую):
```
ГДЕ Документ.Контрагент = Справочник.Контрагенты.Ссылка
```

✅ **ПРАВИЛЬНО** (сравнение через Код, Наименование и т.п.):
```
ГДЕ Документ.Контрагент.Наименование = "ООО Ромашка"
ГДЕ Документ.Контрагент.Код = "000001"
```

### 4. Работа с датами — функция ДАТАВРЕМЯ

```sql
ГДЕ Документы.Дата >= ДАТАВРЕМЯ(2026, 1, 1) 
    И Документы.Дата < ДАТАВРЕМЯ(2026, 2, 1)
```

**Формат:** `ДАТАВРЕМЯ(Год, Месяц, День[, Час, Минута, Секунда])`

### 5. Строковые значения — двойные кавычки

```sql
ГДЕ Номенклатура.Наименование = "iPhone 17 Pro Max, 512 Гб"
ГДЕ Контрагент.ИНН = "7707083893"
```

---

## Синтаксис запросов 1С

### Базовая структура

```sql
ВЫБРАТЬ [РАЗЛИЧНЫЕ] [ПЕРВЫЕ N]
    <Поля выборки>
ИЗ
    <Источники данных>
[ГДЕ
    <Условия отбора>]
[СГРУППИРОВАТЬ ПО
    <Поля группировки>]
[УПОРЯДОЧИТЬ ПО
    <Поля сортировки>]
```

### Основные источники данных

| Тип объекта | Синтаксис |
|-------------|-----------|
| Справочник | `Справочник.ИмяСправочника` |
| Документ | `Документ.ИмяДокумента` |
| Табличная часть документа | `Документ.ИмяДокумента.ИмяТабличнойЧасти` |
| Регистр накопления (остатки) | `РегистрНакопления.ИмяРегистра.Остатки()` |
| Регистр накопления (обороты) | `РегистрНакопления.ИмяРегистра.Обороты()` |
| Регистр сведений | `РегистрСведений.ИмяРегистра` |
| Регистр сведений (срез последних) | `РегистрСведений.ИмяРегистра.СрезПоследних()` |

### Суффиксы ресурсов регистра накопления (ОстаткиИОбороты)

В виртуальной таблице `РегистрНакопления.<ИмяРегистра>.ОстаткиИОбороты()` поля ресурсов формируются как `<ИмяРесурса><Суффикс>`:

- `НачальныйОстаток` — начальный остаток (например: `КоличествоНачальныйОстаток`)
- `Приход` — сумма движений типа Приход (`КоличествоПриход`)
- `Расход` — сумма движений типа Расход (`КоличествоРасход`)
- `Оборот` — оборот за период (`Приход - Расход`) (`КоличествоОборот`)
- `КонечныйОстаток` — конечный остаток (`КоличествоКонечныйОстаток`)

### Суффиксы ресурсов регистра накопления (Остатки)

В виртуальной таблице `РегистрНакопления.<ИмяРегистра>.Остатки()` поля ресурсов формируются как `<ИмяРесурса>Остаток` (например: `КоличествоОстаток`).

### Суффиксы ресурсов регистра накопления (Обороты)

В виртуальной таблице `РегистрНакопления.<ИмяРегистра>.Обороты()` поля ресурсов формируются как `<ИмяРесурса><Суффикс>`:

- `Оборот` — оборот ресурса (например: `КоличествоОборот`)
- `Приход`, `Расход` — существуют только для регистра накопления остатков (`КоличествоПриход`, `КоличествоРасход`)

### Псевдонимы

Обязательно используй псевдонимы для читаемости:

```sql
ВЫБРАТЬ ПЕРВЫЕ 10
    Товары.Код КАК КодТовара,
    Товары.Наименование КАК НаименованиеТовара
ИЗ
    Справочник.Номенклатура КАК Товары
```

---

## Работа с NULL

При соединениях таблиц поля могут содержать NULL. Используй функцию `ЕСТЬNULL()`:

```sql
ВЫБРАТЬ ПЕРВЫЕ 100
    Товары.Наименование,
    ЕСТЬNULL(Остатки.КоличествоОстаток, 0) КАК Остаток
ИЗ
    Справочник.Номенклатура КАК Товары
    ЛЕВОЕ СОЕДИНЕНИЕ РегистрНакопления.ТоварыНаСкладах.Остатки() КАК Остатки
    ПО Остатки.Номенклатура = Товары.Ссылка
```

---

## Соединения таблиц

### ЛЕВОЕ СОЕДИНЕНИЕ (самое частое)

```sql
ВЫБРАТЬ ПЕРВЫЕ 50
    Товары.Наименование,
    Остатки.КоличествоОстаток
ИЗ
    Справочник.Номенклатура КАК Товары
    ЛЕВОЕ СОЕДИНЕНИЕ РегистрНакопления.ТоварыНаСкладах.Остатки() КАК Остатки
    ПО Остатки.Номенклатура = Товары.Ссылка
```

### ВНУТРЕННЕЕ СОЕДИНЕНИЕ

Возвращает только совпадающие записи из обеих таблиц.

---

## Агрегатные функции

| Функция | Описание |
|---------|----------|
| `СУММА()` | Сумма значений |
| `КОЛИЧЕСТВО()` | Количество записей |
| `МАКСИМУМ()` | Максимальное значение |
| `МИНИМУМ()` | Минимальное значение |
| `СРЕДНЕЕ()` | Среднее значение |

```sql
ВЫБРАТЬ ПЕРВЫЕ 20
    Продажи.Номенклатура.Наименование КАК Товар,
    СУММА(Продажи.Количество) КАК ВсегоПродано,
    СУММА(Продажи.Сумма) КАК СуммаПродаж
ИЗ
    РегистрНакопления.Продажи.Обороты() КАК Продажи
СГРУППИРОВАТЬ ПО
    Продажи.Номенклатура
```

---

## Условный оператор ВЫБОР

```sql
ВЫБРАТЬ ПЕРВЫЕ 50
    Товары.Наименование,
    ВЫБОР
        КОГДА Товары.ЭтоГруппа = ИСТИНА ТОГДА "Группа"
        ИНАЧЕ "Элемент"
    КОНЕЦ КАК ТипЗаписи
ИЗ
    Справочник.Номенклатура КАК Товары
```

---

## Предопределённые значения

Используй конструкцию `Значение()`:

```sql
ГДЕ ВидОперации = Значение(Перечисление.ВидыОпераций.Продажа)
ГДЕ Склад = Значение(Справочник.Склады.ОсновнойСклад)
```

---

## Иерархические справочники

### Получить элементы группы (включая вложенные)

```sql
ВЫБРАТЬ ПЕРВЫЕ 100
    Товары.Наименование
ИЗ
    Справочник.Номенклатура КАК Товары
ГДЕ
    Товары.Ссылка В ИЕРАРХИИ (
        ВЫБРАТЬ Ссылка ИЗ Справочник.Номенклатура 
        ГДЕ Наименование = "Бытовая техника"
    )
```

**ВАЖНО:** Конструкция `В ИЕРАРХИИ` с пустой ссылкой работает очень медленно!

### Только непосредственные подчинённые

```sql
ГДЕ Товары.Родитель.Наименование = "Бытовая техника"
```

### Только элементы (не группы)

```sql
ГДЕ Товары.ЭтоГруппа = ЛОЖЬ
```

---

## Виртуальные таблицы регистров

### Остатки регистра накопления

```sql
ВЫБРАТЬ ПЕРВЫЕ 50
    Остатки.Номенклатура.Наименование КАК Товар,
    Остатки.Склад.Наименование КАК Склад,
    Остатки.КоличествоОстаток КАК Количество
ИЗ
    РегистрНакопления.ТоварыНаСкладах.Остатки() КАК Остатки
```

### Остатки на дату

```sql
РегистрНакопления.ТоварыНаСкладах.Остатки(ДАТАВРЕМЯ(2026, 1, 1)) КАК Остатки
```

### Обороты за период

```sql
РегистрНакопления.Продажи.Обороты(
    ДАТАВРЕМЯ(2026, 1, 1),
    ДАТАВРЕМЯ(2026, 1, 31)
) КАК ОборотыПродаж
```

### Срез последних (регистр сведений)

```sql
ВЫБРАТЬ ПЕРВЫЕ 20
    Цены.Номенклатура.Наименование КАК Товар,
    Цены.Цена
ИЗ
    РегистрСведений.ЦеныНоменклатуры.СрезПоследних() КАК Цены
```

---

## Фильтрация в виртуальных таблицах

Условия лучше передавать в параметры виртуальной таблицы, а не в ГДЕ:

❌ **Неэффективно** (сначала получаются ВСЕ остатки, потом фильтрация):
```sql
ИЗ РегистрНакопления.ТоварыНаСкладах.Остатки() КАК Остатки
ГДЕ Остатки.Склад.Наименование = "Основной склад"
```

✅ **Эффективно** (фильтрация при расчёте):
```sql
ИЗ РегистрНакопления.ТоварыНаСкладах.Остатки(
    ,
    Склад.Наименование = "Основной склад"
) КАК Остатки
```

---

## Функции для работы с датами

| Функция | Описание | Пример |
|---------|----------|--------|
| `ГОД()` | Год из даты | `ГОД(Дата)` |
| `КВАРТАЛ()` | Квартал | `КВАРТАЛ(Дата)` |
| `МЕСЯЦ()` | Месяц | `МЕСЯЦ(Дата)` |
| `ДЕНЬ()` | День месяца | `ДЕНЬ(Дата)` |
| `НАЧАЛОПЕРИОДА()` | Начало периода | `НАЧАЛОПЕРИОДА(Дата, МЕСЯЦ)` |
| `КОНЕЦПЕРИОДА()` | Конец периода | `КОНЕЦПЕРИОДА(Дата, МЕСЯЦ)` |
| `ДОБАВИТЬКДАТЕ()` | Добавить к дате | `ДОБАВИТЬКДАТЕ(Дата, МЕСЯЦ, 1)` |
| `РАЗНОСТЬДАТ()` | Разность дат | `РАЗНОСТЬДАТ(Дата1, Дата2, ДЕНЬ)` |

---

## Функции для работы со строками

| Функция | Описание |
|---------|----------|
| `ПОДСТРОКА(Строка, Начало, Длина)` | Часть строки |
| `ДЛИНА(Строка)` | Длина строки |

**Оператор ПОДОБНО:**
```sql
ГДЕ Наименование ПОДОБНО "%телефон%"
ГДЕ Код ПОДОБНО "001___"
```
- `%` — любое количество символов
- `_` — один символ

---

## Приведение типов (ВЫРАЗИТЬ)

Используй для явного указания типа:

```sql
ВЫРАЗИТЬ(Сумма КАК ЧИСЛО(15, 2))
ВЫРАЗИТЬ(Наименование КАК СТРОКА(100))
ВЫРАЗИТЬ(Регистратор КАК Документ.РеализацияТоваровУслуг)
```

---

## Проверка типа (ССЫЛКА)

```sql
ВЫБРАТЬ ПЕРВЫЕ 20
    Движения.Регистратор
ИЗ
    РегистрНакопления.ТоварыНаСкладах КАК Движения
ГДЕ
    Движения.Регистратор ССЫЛКА Документ.РеализацияТоваровУслуг
```

---

## Типичные ошибки и их решения

### Ошибка: "Параметр не найден"
**Причина:** Использование `&ИмяПараметра`
**Решение:** Указывать значения явно

### Ошибка при сравнении ссылок
**Причина:** Прямое сравнение ссылочных полей
**Решение:** Сравнивать через `.Код`, `.Наименование`, `.ИНН` и т.п.

### Слишком много данных / таймаут
**Причина:** Нет ограничения `ПЕРВЫЕ N`
**Решение:** Всегда использовать `ПЕРВЫЕ N`, начинать с малых значений

### NULL в вычислениях
**Причина:** Левое соединение без обработки NULL
**Решение:** Использовать `ЕСТЬNULL(Поле, ЗначениеПоУмолчанию)`

---

## Примеры запросов

### Остатки товаров на складах

```sql
ВЫБРАТЬ ПЕРВЫЕ 100
    Остатки.Номенклатура.Наименование КАК Товар,
    Остатки.Склад.Наименование КАК Склад,
    Остатки.КоличествоОстаток КАК Количество
ИЗ
    РегистрНакопления.ТоварыНаСкладах.Остатки() КАК Остатки
ГДЕ
    Остатки.КоличествоОстаток > 0
УПОРЯДОЧИТЬ ПО
    Товар
```

### Продажи за период

```sql
ВЫБРАТЬ ПЕРВЫЕ 50
    Продажи.Номенклатура.Наименование КАК Товар,
    СУММА(Продажи.Количество) КАК КоличествоПродано,
    СУММА(Продажи.Сумма) КАК СуммаПродаж
ИЗ
    РегистрНакопления.Продажи.Обороты(
        ДАТАВРЕМЯ(2026, 1, 1),
        ДАТАВРЕМЯ(2026, 1, 31)
    ) КАК Продажи
СГРУППИРОВАТЬ ПО
    Продажи.Номенклатура
УПОРЯДОЧИТЬ ПО
    СуммаПродаж УБЫВ
```

### Документы за период

```sql
ВЫБРАТЬ ПЕРВЫЕ 50
    Док.Номер,
    Док.Дата,
    Док.Контрагент.Наименование КАК Контрагент,
    Док.СуммаДокумента
ИЗ
    Документ.РеализацияТоваровУслуг КАК Док
ГДЕ
    Док.Дата >= ДАТАВРЕМЯ(2026, 1, 1)
    И Док.Дата < ДАТАВРЕМЯ(2026, 2, 1)
    И Док.Проведен = ИСТИНА
УПОРЯДОЧИТЬ ПО
    Док.Дата УБЫВ
```

### Актуальные цены

```sql
ВЫБРАТЬ ПЕРВЫЕ 100
    Цены.Номенклатура.Наименование КАК Товар,
    Цены.Номенклатура.Код КАК КодТовара,
    Цены.ТипЦен.Наименование КАК ТипЦены,
    Цены.Цена
ИЗ
    РегистрСведений.ЦеныНоменклатуры.СрезПоследних() КАК Цены
УПОРЯДОЧИТЬ ПО
    Товар
```

---

## Выполнение запроса вручную

При необходимости используй curl или аналогичный инструмент.

Значения бери из файла `.env`, который лежит рядом со `SKILL.md`:

- `ONEC_QUERY_URL` — URL HTTP-сервиса (например: `http://20.10.10.101:8314/hs/queries/query`)
- `ONEC_QUERY_LOGIN` — логин для Basic Auth
- `ONEC_QUERY_PASSWORD` — пароль для Basic Auth

```bash
curl -X POST "$ONEC_QUERY_URL" \
    -H "Authorization: Basic $(printf '%s:%s' "$ONEC_QUERY_LOGIN" "$ONEC_QUERY_PASSWORD" | base64)" \
    -H "Content-Type: application/json" \
    -d '{"query": "ВЫБРАТЬ ПЕРВЫЕ 10 Товары.Код, Товары.Наименование ИЗ Справочник.Номенклатура КАК Товары"}'
```

**Важно:** В JSON переносы строк заменяй на пробелы или используй `\n`.
