---
name: 1c-trader-showcase
description: How to implement a showcase-filling method (random items for sale) in a 1С trader form
source: auto-skill
extracted_at: '2026-05-31T19:32:50.428Z'
---

# Заполнение витрины торговца в 1С (ФормаТорговца, ТестированиеЗнаний)

Целевой файл: `DataProcessors\ТестированиеЗнаний\Forms\ФормаТорговца\Ext\Form\Module.bsl`

## Контекст

Форма `ФормаТорговца` открывается с параметрами `Золото`, `Модификаторы` (список значений) и `КартыИгрока` (массив структур `{Карта, ИдентификаторКарты}`). На форме есть две таблицы-витрины:
- `МодификаторыНаВитрине` — модификаторы на продажу
- `КартыНаВитрине` — карты на продажу

Обе таблицы — `v8:ValueTable` с колонками `(Ссылка, Цена)`.

## Шаг 1. Изучить структуру реквизитов (Form.xml)

Открыть `Forms\ФормаТорговца\Ext\Form.xml` и найти секцию `<Attributes>`:

```xml
<Attribute name="КартыНаВитрине" id="6">
  <Type><v8:Type>v8:ValueTable</v8:Type></Type>
  <Columns>
    <Column name="Карта" id="1">
      <Type><v8:Type>cfg:CatalogRef.Карты</v8:Type></Type>
    </Column>
    <Column name="Цена" id="2">
      <Type><v8:Type>xs:decimal</v8:Type>…</Type>
    </Column>
  </Columns>
</Attribute>
```

Это даёт точные типы колонок (например, `CatalogRef.Карты`, а не `CatalogRef.Модификаторы`).

## Шаг 2. Проверить наличие ManagerModule у справочника

У справочника `Модификаторы` есть `ManagerModule.bsl` с экспортной функцией `ВернутьМодификатор()`.

У справочника `Карты` управляющего модуля нет — логику выборки нужно писать прямо в форме.

Проверка:
- `Catalogs\ИмяСправочника\Ext\ManagerModule.bsl` — если нет, значит метод надо реализовать inline.

## Шаг 3. Реализовать метод Заполнить…НаВитрине()

### Паттерн для справочника без ManagerModule

```bsl
// Выполняет заполнение таблицы карт к покупке.
//
&НаСервере
Процедура ЗаполнитьКартыНаВитрине()
    Гсч = Новый ГенераторСлучайныхЧисел;
    КартыНаВитрине.Очистить();
    // Получаем все элементы, не помеченные на удаление
    Запрос = Новый Запрос;
    Запрос.Текст =
    "ВЫБРАТЬ РАЗРЕШЕННЫЕ
    |	Карты.Ссылка КАК Ссылка
    |ИЗ
    |	Справочник.Карты КАК Карты
    |ГДЕ
    |	НЕ Карты.ПометкаУдаления
    |	И НЕ Карты.ЭтоГруппа";
    РезультатЗапроса = Запрос.Выполнить();
    Выборка = РезультатЗапроса.Выбрать();
    МассивВсехЭлементов = Новый Массив;
    Пока Выборка.Следующий() Цикл
        МассивВсехЭлементов.Добавить(Выборка.Ссылка);
    КонецЦикла;
    // Выбираем N случайных уникальных элементов
    КоличествоДобавленных = 0;
    МаксимумКПокупке = 3;
    Пока КоличествоДобавленных < МаксимумКПокупке И МассивВсехЭлементов.Количество() > 0 Цикл
        СлучайныйИндекс = Гсч.СлучайноеЧисло(0, МассивВсехЭлементов.Количество() - 1);
        НовыйЭлемент = МассивВсехЭлементов[СлучайныйИндекс];
        МассивВсехЭлементов.Удалить(СлучайныйИндекс);
        НоваяСтрока = ТаблицаНаВитрине.Добавить();
        НоваяСтрока.Ссылка = НовыйЭлемент;
        НоваяСтрока.Цена = Гсч.СлучайноеЧисло(50, 100);  // целевой диапазон
        КоличествоДобавленных = КоличествоДобавленных + 1;
    КонецЦикла;
КонецПроцедуры
```

### Паттерн для справочника с ManagerModule (как в Модификаторы)

```bsl
&НаСервере
Процедура ЗаполнитьМодификаторыНаВитрине()
    Гсч = Новый ГенераторСлучайныхЧисел;
    МодификаторыНаВитрине.Очистить();
    КоличествоДобавленных = 0;
    МаксимумКПокупке = 3;
    Пока КоличествоДобавленных < МаксимумКПокупке Цикл
        НовыйМодификатор = Справочники.Модификаторы.ВернутьМодификатор(
            Истина, Гсч, МодификаторыИгрока.ВыгрузитьЗначения());
        Если ЗначениеЗаполнено(НовыйМодификатор) Тогда
            // Проверяем дубликаты в витрине
            СтруктураПоиска = Новый Структура("Модификатор", НовыйМодификатор);
            НайденныеСтроки = МодификаторыНаВитрине.НайтиСтроки(СтруктураПоиска);
            Если НайденныеСтроки.Количество() = 0 Тогда
                НоваяСтрока = МодификаторыНаВитрине.Добавить();
                НоваяСтрока.Модификатор = НовыйМодификатор;
                НоваяСтрока.Цена = Гсч.СлучайноеЧисло(15, 30) * 10;  // 150-300
                КоличествоДобавленных = КоличествоДобавленных + 1;
            КонецЕсли;
        Иначе
            Прервать;  // нет подходящих элементов
        КонецЕсли;
    КонецЦикла;
КонецПроцедуры
```

## Шаг 4. Добавить вызов в ПриСозданииНаСервере()

```bsl
Процедура ПриСозданииНаСервере(Отказ, СтандартнаяОбработка)
    // ... получение параметров ...
    ЗаполнитьМодификаторыНаВитрине();
    ЗаполнитьКартыНаВитрине();  // новая строка
КонецПроцедуры
```

## Шаг 5. Реализовать покупку товара с витрины

Команда покупки — клиентская процедура, привязанная к кнопке в форме.

### Паттерн для команды покупки

```bsl
&НаКлиенте
Процедура КупитьТовар(Команда)
    ТекДанные = Элементы.ИмяТаблицыНаВитрине.ТекущиеДанные;
    Если ТекДанные <> Неопределено Тогда
        ТекТовар      = ТекДанные.Ссылка;  // имя колонки со ссылкой
        ТекЦена       = ТекДанные.Цена;
        Если Золото < ТекЦена Тогда
            Сообщить("Недостаточно золота для покупки");
        Иначе
            // Уменьшаем золото на величину цены
            Золото = Золото - ТекЦена;
            // Добавляем товар в список купленных
            КупленныеТовары.Добавить(ТекТовар);
            // Удаляем товар из витрины
            СтруктураПоиска = Новый Структура;
            СтруктураПоиска.Вставить("Ссылка", ТекТовар);  // имя колонки
            НайденныеСтроки = ИмяТаблицыНаВитрине.НайтиСтроки(СтруктураПоиска);
            Для Каждого ТекНайденныеСтроки Из НайденныеСтроки Цикл
                ИмяТаблицыНаВитрине.Удалить(ТекНайденныеСтроки);
            КонецЦикла;
        КонецЕсли;
    Иначе
        Сообщить("Требуется выбрать товар");
    КонецЕсли;
КонецПроцедуры
```

### Подтверждение покупок (закрытие формы)

В процедуре `ПодтвердитьПокупки()` передать купленные товары в структуре закрытия:

```bsl
&НаКлиенте
Процедура ПодтвердитьПокупки(Команда)
    СтруктураЗакрытия = Новый Структура;
    СтруктураЗакрытия.Вставить("Золото", Золото);
    СтруктураЗакрытия.Вставить("КупленныеМодификаторы", КупленныеМодификаторы);
    СтруктураЗакрытия.Вставить("КупленныеКарты", КупленныеКарты);  // добавить при необходимости
    Закрыть(СтруктураЗакрытия);
КонецПроцедуры
```

## Шаг 6. Обработать покупки после закрытия формы

В процедуре `ИмяФормы_ПослеЗакрытия()` (в основной форме) проверить наличие купленных товаров в структуре и добавить их в соответствующие таблицы:

```bsl
Процедура ИмяФормы_ПослеЗакрытия(ВыбранноеЗначение, ДополнительныеПараметры) Экспорт
    Если ТипЗнч(ВыбранноеЗначение) = Тип("Структура") Тогда
        // Обработка золота
        Если ВыбранноеЗначение.Свойство("Золото") Тогда
            Золото = ВыбранноеЗначение.Золото;
            УстановитьПараметрИгрока(ВидПараметраЗолото, Золото);
        КонецЕсли;
        // Обработка купленных модификаторов
        Если ВыбранноеЗначение.Свойство("КупленныеМодификаторы") Тогда
            СписокКупленных = ВыбранноеЗначение.КупленныеМодификаторы;
            Для Каждого ТекСписок Из СписокКупленных Цикл
                ТекМодификатор = ТекСписок.Значение;
                // Проверка дубликатов (если применимо)
                СтруктураПоиска = Новый Структура("Модификатор", ТекМодификатор);
                НайденныеСтроки = ТаблицаМодификаторов.НайтиСтроки(СтруктураПоиска);
                Если НайденныеСтроки.Количество() = 0 Тогда
                    НоваяСтрока = ТаблицаМодификаторов.Добавить();
                    НоваяСтрока.Модификатор = ТекМодификатор;
                    // ... прочие поля
                КонецЕсли;
            КонецЦикла;
        КонецЕсли;
        // Обработка купленных карт (добавляются в колоду с новым идентификатором)
        Если ВыбранноеЗначение.Свойство("КупленныеКарты") Тогда
            СписокКупленныхКарт = ВыбранноеЗначение.КупленныеКарты;
            Для Каждого ТекСписок Из СписокКупленныхКарт Цикл
                ТекКарта = ТекСписок.Значение;
                НоваяСтрока = КолодаИгрока.Добавить();
                НоваяСтрока.Карта = ТекКарта;
                НоваяСтрока.ИдентификаторКарты = Строка(Новый УникальныйИдентификатор());
            КонецЦикла;
        КонецЕсли;
    КонецЕсли;
КонецПроцедуры
```

## Ключевые отличия: справочник с ManagerModule vs без

| Аспект | Модификаторы (с модулем) | Карты (без модуля) |
|---|---|---|
| Метод получения | Вызов `Справочники.Модификаторы.ВернутьМодификатор()` | Inline-запрос |
| Фильтрация | По булевому признаку + исключение списка | Только `ПометкаУдаления` + `ЭтоГруппа` |
| Дубликаты | Проверка через `НайтиСтроки()` перед добавлением | Исключаются удалением из массива |
| Цена | `Гсч.СлучайноеЧисло(15, 30) * 10` (диапазон 150-300) | `Гсч.СлучайноеЧисло(50, 100)` |

## Удаление карт у торговца (продажа за золото)

Паттерн: игрок может удалить карту из своей коллекции за фиксированную плату. Удаление происходит через выбор из списка (`ПоказатьВыборИзСписка`), выбранная карта помечается для удаления и исключается из повторного выбора.

### Шаг 1. Создать функцию стоимости

Вынести фиксированную стоимость удаления в функцию `&НаКлиентеНаСервереБезКонтекста`:

```bsl
&НаКлиентеНаСервереБезКонтекста
Функция СтоимостьУдаленияКарты()
	Возврат 50;
КонецФункции
```

### Шаг 2. Реализовать команду УдалитьКарту (выбор из списка)

```bsl
&НаКлиенте
Процедура УдалитьКарту(Команда)
	Если Золото < СтоимостьУдаленияКарты() Тогда
		Сообщить("Недостаточно золота для удаления карты");
		Возврат;
	КонецЕсли;
	СписокКарт = Новый СписокЗначений;
	Для Каждого ТекКарта Из КартыИгрока Цикл
		// значение = идентификатор, представление = ссылка на карту
		СписокКарт.Добавить(ТекКарта.ИдентификаторКарты, ТекКарта.Карта);
	КонецЦикла;
	ОписаниеОповещения = Новый ОписаниеОповещения("ПослеВыбораКартыДляУдаления", ЭтаФорма);
	ПоказатьВыборИзСписка(ОписаниеОповещения, СписокКарт, , "Выберите карту для удаления");
КонецПроцедуры
```

**Ключевые моменты:**
- Проверка золота выполняется **до** открытия списка — если не хватает, диалог не показывается
- `СписокЗначений` формируется из массива структур: `Добавить(Значение, Представление)` — где значение это `ИдентификаторКарты` (строка), представление — `Карта` (ссылка на справочник)
- Тип параметра `КартыИгрока` — `v8:Array` (передаётся из основной формы)

### Шаг 3. Обработать выбор (списание золота + удаление из списка)

```bsl
&НаКлиенте
Процедура ПослеВыбораКартыДляУдаления(Результат, ДополнительныеПараметры) Экспорт
	Если Результат <> Неопределено Тогда
		Золото = Золото - СтоимостьУдаленияКарты();
		КартыКУдалению.Добавить(Результат);
		// Удаляем карту из списка, чтобы исключить повторный выбор
		ИндексУдаления = -1;
		Для Индекс = 0 По КартыИгрока.ВГраница() Цикл
			Если КартыИгрока[Индекс].ИдентификаторКарты = Результат Тогда
				ИндексУдаления = Индекс;
				Прервать;
			КонецЕсли;
		КонецЦикла;
		Если ИндексУдаления >= 0 Тогда
			КартыИгрока.Удалить(ИндексУдаления);
		КонецЕсли;
	Иначе
		// Пользователь отменил выбор.
	КонецЕсли;
КонецПроцедуры
```

**Почему это работает:**
- `КартыИгрока` — реквизит формы (массив структур), модифицируется на клиенте
- После удаления карты из массива, при повторном вызове `УдалитьКарту()` она не появится в списке
- `КартыКУдалению` — `СписокЗначений` (реквизит формы), куда складываются идентификаторы удалённых карт

### Шаг 4. Передать удалённые карты в структуре закрытия

В процедуре `ПодтвердитьПокупки()` добавить ключ `УдаленныеКарты`:

```bsl
&НаКлиенте
Процедура ПодтвердитьПокупки(Команда)
	СтруктураЗакрытия = Новый Структура;
	СтруктураЗакрытия.Вставить("Золото", Золото);
	СтруктураЗакрытия.Вставить("КупленныеМодификаторы", КупленныеМодификаторы);
	СтруктураЗакрытия.Вставить("КупленныеКарты", КупленныеКарты);
	СтруктураЗакрытия.Вставить("УдаленныеКарты", КартыКУдалению);  // <-- добавить
	Закрыть(СтруктураЗакрытия);
КонецПроцедуры
```

### Шаг 5. Обработать удалённые карты после закрытия формы

В вызывающей форме (в обработчике `ИмяФормы_ПослеЗакрытия`) добавить обработку `УдаленныеКарты`:

```bsl
// Обработка удалённых карт
Если ВыбранноеЗначение.Свойство("УдаленныеКарты") Тогда
	СписокУдаленныхКарт = ВыбранноеЗначение.УдаленныеКарты;
	Для Каждого ТекУдаленнаяКарта Из СписокУдаленныхКарт Цикл
		ИдентификаторУдаляемойКарты = ТекУдаленнаяКарта.Значение;
		// Поиск и удаление из всех таблиц, где могут находиться карты
		СтруктураПоиска = Новый Структура("ИдентификаторКарты", ИдентификаторУдаляемойКарты);
		НайденныеСтроки = СтопкаДобора.НайтиСтроки(СтруктураПоиска);
		Для Каждого ТекНайденныеСтроки Из НайденныеСтроки Цикл
			СтопкаДобора.Удалить(ТекНайденныеСтроки);
		КонецЦикла;
		// Повторить для РукаИгрока, СтопкаСброса и т.д.
	КонецЦикла;
КонецЕсли;
```

**Поиск по всем стопкам:** карта может находиться в любой из трёх стопок (`СтопкаДобора`, `РукаИгрока`, `СтопкаСброса`). Поиск выполняется в каждой таблице по `ИдентификаторКарты` с удалением найденных строк.

### Полная схема потока удаления карты

```
[Основная форма]
   ↓ Передаёт КартыИгрока (массив структур) как параметр
[ФормаТорговца]
   ↓ Игрок нажимает УдалитьКарту
   ↓ Проверка золота ≥ СтоимостьУдаленияКарты()
   ↓ ПоказатьВыборИзСписка() → игрок выбирает карту
   ↓ ПослеВыбораКартыДляУдаления():
       • Списывает золото
       • Добавляет в КартыКУдалению
       • Удаляет из КартыИгрока (чтобы не выбрать повторно)
   ↓ Игрок нажимает ПодтвердитьПокупки
   ↓ В структуру закрытия добавляется УдаленныеКарты
[Основная форма → ОткрытьТорговца_ПослеЗакрытия]
   ↓ Проходит по СписокУдаленныхКарт
   ↓ Для каждого ИдентификаторКарты:
       • Ищет в СтопкаДобора, РукаИгрока, СтопкаСброса
       • Удаляет найденные строки
```
