Добрый день! Вот попалась задачка, и честно говоря, в начале показалась ничего особенного, но попытавшись решить, начал спотыкаться, появились сомнения.
Решил сюда выкинуть, может у кого -то появятся мыслишки по решению. Условие пишу как есть:

Необходимо реализовать универсальную функцию вида: СкорректироватьЗаписи(ИмяРегистра, ИсходныеДвижения, ЦелевыеДвижения).
Где ИмяРегистра - имя регистра накопления (строка);
ИсходныеДвижения - набор исходных движений (тип - Таблица значений)
ЦелевыеДвижения - набор целевых движений (тип - Таблица значений)

Функция должна возвращать набор записей соответствующего регистра (в виде таблицы значений), при записи котрого (в рамках данной задачи запись реализовывать не надо) в системе будут целевые движения. Исходные движения при этом не удаляются.

Например, существует движения регистра "Остатки номенклатуры", Исходные движения (Приходная №2):

Номенк Колво Сум
Kohinor 60 390
Паркер 40 3800

И есть движения регистра "Остатки номенклатуры", Целевые движения (Приходная №1):

Номенк Колво Сум
Kohinor 10 70
Паркер 10 1000

На выходе функция должна вернуть таблицу значений следующего вида:

Номенк Колво Сум
Kohinor -50 -320
Паркер -30 -2800

Функция должна быть универсальной, т.е. на вход могут подаваться записи другого регистра накопления , например, "Продажи",
имеющего другую структуру измерений и ресурсов.
Решение должно содержать примитивный пользовательский интерфейс для проверки, позволяющий задать параметры функции и продемонстрировать результат.
---
Я так в свое время решал:

```1c
    // Создадим итоговую таблицу, в которой найдем разницу
    ИтоговаяТаблица = СоставПлана.Выгрузить();

    // Вычитаем строки исходного плана(добавляем строки с минусом)
    Для Каждого СтрокаТЗ Из ИсходнаяТаблица Цикл
    НоваяСтрока = ИтоговаяТаблица.Добавить();
    ЗаполнитьЗначенияСвойств(НоваяСтрока, СтрокаТЗ);
    НоваяСтрока.Количество = -СтрокаТЗ.Количество;// Тут нужно будет перечислить все ресурсы

    КонецЦикла;

    // Сформируем перечень полей по которым надо свернуть таблицу
    КолонкиГруппировок = "Период, Подразделение, Номенклатура, ХарактеристикаНоменклатуры, ЕдиницаИзмерения, Коэффициент, Цена, СтавкаНДС, Заказ, Контрагент, Договор, Проект";// Тут все Измерения(Все колонки, кроме ресурсов)
    КолонкиСуммирования = "Количество, СуммаНДС, Сумма";// Тут перечисляем все ресурсы

    // Свернем итоговую таблицу, чтобы получить разницу
    ИтоговаяТаблица.Свернуть(КолонкиГруппировок, КолонкиСуммирования);

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

    // Сортировать не обязательно
    ИтоговаяТаблица.Сортировать("Период, Номенклатура, ХарактеристикаНоменклатуры, Количество");
```
---
по запросу: в цикле конкатенацией строк. Это вообще не проблема.
по второму варианту: из метаданных полученные измерения и ресурсы собрать в отдельные массивы. Потом собрать в строку не составит проблем. В чем сложность?

Я привел примеры решений. Я не уговариваю использовать только запрос.
---
постановка задачи, какая-то дибильная.
Но по факту, как я понял,
интерактивно пользователь вбивает целевые движения, т.е. то, что должно после операции остаться.
А значит, делаем срез остатков, и обычной формулой рассчитывааем Целевое-Исходное= ТоЧтоНужно.

А универсальный запрос сделать просто,

(3)

```1c
    "ВЫБРАТЬ *
    |ИЗ
    | РегистрНакопления.ИмяРегистра.Остатки КАК РегистрОстатки"
```

Имя регистра, подменяй только методом СтрЗаменить

Потом выполнить выгрузить, и обходишь колонки и строки таблицы значений, как нужно
---
А при чем тут остатки.... На вход может подаваться любой регистр накопления. Оборотов например.
Здесь смысл привести движения одного документа к движениям другого. Сторнировать так сказать под цифры второго документа.
---
я и написал, что формулировка задачи не совсем понятная. Как я понял, так и написал.

Если речь не о корректировке остатка, а сторнировании движения,
то точно также, не вижу проблем, немного видоизменить мой запрос, только выбрать другую таблицу регистра, все поля, и по регистратору отобрать. Только в параметрах функции у вас регистратора то нет. Откуда она узнает о каком исходном документе речь.

А так задача, сама по себе, проста и незатейлива...
А если параметры движения, это уже движения исходного документа, то и запрос никакой не нужен, просто анализируйте их и все.
---
в том то и дело. Про регистратор ничего в задании не сказано. Считаем его нет. Да и не нужен он в данном случае. Корректировка регистра. Нет данных про период выборки.
---
Скорее всего, да, задача на сравнивание таблиц без запроса. Могли бы Вы привести пример сравнивания на данной задачке без запроса?
---
несколько вариантов.
1. Можно через загрузку обеих ТЗ в запрос. Соединение, группировка по измерениям. Вычитание.
2. Можно без запроса. Инвертировать ресурсы первой таблицы (создать локальную ТЗ). Объединить обе ТЗ в одну результирующую. Результирующую свернуть по измерениям и суммированием ресурсов.
---
Возьмём запрос. Структура каждый раз разная, как собрать запрос, как описать поля? В цикле по измерениям и ресурсам? А потом как дать запросу понять какие поля группировать, а какие складывать? Возьмём второй вариант: чтобы свернуть, нужно опять в цикле описывать строку ,состоящая из полей, по которым будем сворачивать, и строку, с теми полями которые будут сворачиваться.
---
Дело даже не в этом. Исходные данные уже в ОЗУ, доп-данных не требуется. Привлекать СУБД в качестве калькулятора - это... это... у меня нет слов.
---
и в чем проблема? Выборки из таблиц sql не будет, соответственно блокировок тоже.
---
А передача ТЗ в СУБД? А получение результата от СУБД? Особенно если таблицы большие? А накладные расходы на вызовы СУБД? Ради чего? Когда уже все вот оно и все можно легко сделать даже не трогая сетку, не говоря уже о цельном сервере баз данных?
Для меня это такой же нонсенс, как если бы всерьез предлагали для сложения двух чисел дергать сиквел, чтобы это сделал он. Да, работать будет. Можно еще веб-сервис для этого написать. Тоже рабочее решение. И очень гибкое. Чем не вариант?
---
в любом случае будут накладные расходы. ТЗ сама по себе высоконагруженная сущность. Без запроса нужна еще одна ТЗ. Результирующая ТЗ будет удвоена. Если ТЗ большая, то индексировать не помешает.
Что будет менее нагрузочным и быстрым нужно смотреть уже по факту использования. Как правило для sql выделяется больше ресурсов.
---
Передача в СУБД для экономии ОЗУ на обработке результирующей таблицы? Интересная идея :) Только не взлетит. Для сериализации и передачи в СУБД большой таблицы тоже потребуются дополнительные ресурсы.
В общем, раз консенсуса нет - не вижу смысла продолжать эту тему. Я чувствую себя нелепо, пытаясь доказывать аксиоматичные для меня вещи.
---
по запросу: в цикле конкатенацией строк. Это вообще не проблема.
по второму варианту: из метаданных полученные измерения и ресурсы собрать в отдельные массивы. Потом собрать в строку не составит проблем. В чем сложность?

Я привел примеры решений. Я не уговариваю использовать только запрос.
---
ясно, приму как решение. Поясню, задача предлагалась как тестовый пример на собеседовании. Время на решение не более 30 минут с реализацией интерфейса. Просто показалась, что заниматься на собеседовании за 30 минут собиранием строки не лучшей идей. Вот и выложил сюда. Спасибо
---
то что это тестовое задание было сразу понятно.
А вот то что его нужно было решить прямо на собеседовании в коде за 30 мин. Лучше не рассматривать как вариант будущей работы там.
На собеседовании можно описать алгоритм и/или потом решить высланное тестовое задание. Это нормально.
---
А вот то что его нужно было решить прямо на собеседовании в коде за 30 мин. Лучше не рассматривать как вариант будущей работы там.
Ну почему же? Мне наоборот - очень понравилось. 30 мин на такую задачку - это с запасом на мандраж, тупняк и чаю попить.
А возможность посмотреть на живой код с гарантией авторства и тут же задать вопросы по нему - бесценно.
Задачка с одной стороны - простейшая алгоритмистика, с другой - нужно хотя бы знать про метаданные :)
Если отдать эту примитивную задачку "на потом", то так и не узнаешь, что чувак на месте не смог бы решить ее за полчаса самостоятельно (что сразу бы сказало о крайне ограниченной практике программирования).
Задачи на "домашнее задание" - это уже другой класс задач. Где обычно проверяется не только элементарное умение кодить, но и какое-то элементарное понимание учета.
---
да и вообще, если вчитаться...
то параметры функции уже таблицы значений и исходные и целевые..
а значит, нам вообще никакой запрос точно не нужен.
Просто сравниваем 2 таблицы значений,
и создаем набор записей, от нужного регистра по имени, в него и загружаем эту таблицу значений
---
не нужно создавать движения. Нужно вернуть результирующую таблицу значений.
---
нуууу
Функция должна возвращать набор записей соответствующего регистра (в виде таблицы значений)
я х.з. тогда вообще что она вернуть должна ))) набор записей, или таблицу значений ))
---
ну а если так, тогда согласен с твоим (8)
регистр нужен только для определения измерений.
---
Получаем метаданные по "ИмяРегистра". Находим измерения и ресурсы. Далее для уникальных записей по измерениям делаем вычитание ресурсов из второй таблицы первой таблицы.
---
В чем загвоздка-то? Элементарным запросом решается, кмк. Ну, текст генерить придется.
---
Загвоздка как раз и в том, что запрос будет каждый раз разный, т.к. могут подаваться разные регистры. Если решается запросом, то как сделать универсальный запрос?
---
Если таблицы повторяют структуру регистра, то циклом по колонкам определяем типы, если тип числовой - это ресурс, схему запроса, думаю, привлекать не будем, из имен колонок формируем секцию ВЫБРАТЬ, передаем таблицу целевых движений в параметры запроса, на выходе получаем таблицу необходимых движений.
---
В качестве параметров нужно передавать, какие колонки таблицы являются ресурсами, или же в функции придется анализировать, обращаясь к метаданным, что в этих колонках ресурсы - их вычитаем, в этих - измерения - по ним группируем.

Задача нахождения разницы решается "вычитание" из одной таблицы в другую. Языком запросов - это объединение с последующей группировкой по ключевым полям - измерениям.
---
Зачем тут запрос? Каких данных вам не хватает?
---
Проблема определить что таблице ресурс а что измерение (оно тоже может быть числовым или датой)
---
В регистрах накопления ресурсом может быть только число. В принципе, нам нужны только имена ресурсов для решения задачи.
---
а измерение нет, поэтому легко можно спутать измерение с ресурсом (если определяться по типу значений), и начать вычитать то что не надо.
---
Что то это мне напоминает - контроль остатков при отмене проведения.
---
Я так в свое время решал:

```1c
    // Создадим итоговую таблицу, в которой найдем разницу
    ИтоговаяТаблица = СоставПлана.Выгрузить();

    // Вычитаем строки исходного плана(добавляем строки с минусом)
    Для Каждого СтрокаТЗ Из ИсходнаяТаблица Цикл
    НоваяСтрока = ИтоговаяТаблица.Добавить();
    ЗаполнитьЗначенияСвойств(НоваяСтрока, СтрокаТЗ);
    НоваяСтрока.Количество = -СтрокаТЗ.Количество;// Тут нужно будет перечислить все ресурсы

    КонецЦикла;

    // Сформируем перечень полей по которым надо свернуть таблицу
    КолонкиГруппировок = "Период, Подразделение, Номенклатура, ХарактеристикаНоменклатуры, ЕдиницаИзмерения, Коэффициент, Цена, СтавкаНДС, Заказ, Контрагент, Договор, Проект";// Тут все Измерения(Все колонки, кроме ресурсов)
    КолонкиСуммирования = "Количество, СуммаНДС, Сумма";// Тут перечисляем все ресурсы

    // Свернем итоговую таблицу, чтобы получить разницу
    ИтоговаяТаблица.Свернуть(КолонкиГруппировок, КолонкиСуммирования);

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

    // Сортировать не обязательно
    ИтоговаяТаблица.Сортировать("Период, Номенклатура, ХарактеристикаНоменклатуры, Количество");
```
---
+(22) Никаких запросов - все выполняется с помощью объектов 1С. В данном примере все поля, конечно жестко прописаны, но коли у вас есть имя регистра - поля ресурсов по метаданным определить не сложно.
---
Я бы делал так (никаких запросов - меня вообще раздражает, когда шурупы забивают молотком, просто потому что уже научились забивать им гвозди):
- определил по метаданным и имени регистра поля ресурсов и измерений
- слил таблицы в одну (исходные движения - с минусом по ресурсам)
- свернул таблицу по измерениям (суммируя по полям ресурсов)
- удалил строки с нулевыми значениями всех ресурсов
- вернул получившуюся таблицу
ЗЫ. Собственно, в (22) именно так и сделано
---
никаких запросов - меня вообще раздражает, когда шурупы забивают молотком
Не в контексте темы: некоторые шурупы только молотком и нужно забивать. Дюбель-шуруп :)
---
сворачивать тз умеет еще построитель запроса,скд, и анализ данных.
---
Построитель запроса и СКД - это инструмент делегирования исполнения из среды 1с на уровень СУБД. Если нет обращения к реквизитам объектов базы, то в нем нет смысла, обычные циклы работают объяснимо быстрее.
---
нет. Вы ошибаетесь. скд и построитель запроса умеют обрабатывать тз без субд. причем у каждого механизма есть свои преимущества: скд может объединять и сворачивать тз , а ПЗ в некоторых случаях быстрее сворачивает тз чем тз.
---
А где нашли такую задачку? А то я хочу в скором времени начать готовиться к экзамену по платформе.
Скиньте ссылку пожалуйста, если не сложно.