Компания продает кабель.

Кабель продается в бобинах по 28 и 67 метров. Не режутся.

На складе лежит какое-то произвольное количество этих бобин обоих видов.

Клиент может попросить любую длину.

Нужно одним запросом подобрать оптимальное количество бобин одной и второй длин с учетом наличия на складе.
---
логика:
1. получаем Остаток28 и Остаток67
2. берем целую часть от ТребуетсяХ/67 и сравниваем с остатком67 берем минимум из двух чисел - это количество 67бабин
3 (ТребуетсяХ - количество67бабин * 67)/28 округление вверх и сравниваем с остатком28 - берем минимум из двух чисел это количество 28 бабин

делать эту логику в запросе... ну такое себе.

```1c
    "ВЫБРАТЬ
    | ТоварыНаСкладахОстатки.Товар КАК Товар
    | Поместить втОстатокБабина28
    |ИЗ
    | РегистрНакопления.ТоварыНаСкладах.Остатки(, , , Товар = &Бабина28) КАК ТоварыНаСкладахОстатки
    | ;
    |ВЫБРАТЬ
    | ТоварыНаСкладахОстатки.Товар КАК Товар
    | Поместить втОстатокБабина67
    |ИЗ
    | РегистрНакопления.ТоварыНаСкладах.Остатки(, , , Товар = &Бабина67) КАК ТоварыНаСкладахОстатки
    | ;
    |ВЫБРАТЬ
    | Выбор
    | Когда ЦЕЛ(&ТребуетсяХ/67) > втОстатокБабина67.Товар Тогда втОстатокБабина67.Товар
    | Иначе ЦЕЛ(&ТребуетсяХ/67)
    | Конец Как Количество67бабин
    | Поместить втКоличество67бабин
    |ИЗ
    | втОстатокБабина67 КАК втОстатокБабина67
    | ;
    |ВЫБРАТЬ
    | Выбор
    | Когда ЦЕЛ((&ТребуетсяХ - втКоличество67бабин.Количество67бабин*67)/28)+1 > втОстатокБабина28.Товар Тогда втОстатокБабина28.Товар
    | Иначе ЦЕЛ((&ТребуетсяХ - втКоличество67бабин.Количество67бабин*67)/28)+1
    | Конец Как Количество28бабин
    | Поместить втКоличество28бабин
    |ИЗ
    | втКоличество67бабин как втКоличество67бабин,
    | втОстатокБабина28 КАК втОстатокБабина28,
    | ;
    |ВЫБРАТЬ
    | втКоличество67бабин.втКоличество67бабин,
    | втКоличество28бабин.втКоличество28бабин
    |ИЗ
    | втКоличество67бабин как втКоличество67бабин,
    | втКоличество28бабин КАК втКоличество28бабин"
```

PS. небольшая не точность в Количество28бабин - так как нет функции окрулить вверх я использовал взять цел и +1 - это не всегда равно окрулению вверх, если у нас число кратно 28.
---
ровно надо? или есть + - ?
---
если нужно например 29 метров то 2 бобины по 28
---
если 68 метров то 1 бобина на 67 и 1 на 28
---
если 68 метров то 1 бобина на 67 и 1 на 28

будет
67+28 = 95

хотя по условию:
 Нужно одним запросом подобрать оптимальное количество бобин одной и второй длин с учетом наличия на складе
28+28+28 = 84 (при наличии)

оптимальным, как я пониманию минимальной общей длиной, будет 28*3.

Уточните, что значит "оптимальное количество бобин".
По количеству бобин, по минимальной общей длине, ...?
---
Предлагаю задачу разложить на запрос и обработку запроса, т.е. сначала делаем запрос остатков, а уже потом обрабатываем логикой
---
нужно запросом одним получить нужное количество бобин
---
```1c
    Запрос = Новый Запрос;
    Запрос.Текст =
    "ВЫБРАТЬ
    | ТЗ.Количество28 КАК Количество28,
    | ТЗ.Количество67 КАК Количество67
    |ПОМЕСТИТЬ ОстаткиНаСкладе
    |ИЗ
    | &ТЗ КАК ТЗ";

    Запрос.УстановитьПараметр("ТЗ",ТЗ);
```
---
могу написать логику, целиком запрос писать лень)))
---
ок, я перепишу под запрос
---
1. Делаем запрос на остатки
2. Устанавливаем параметр - НеобходимоеКоличество
3. Логика:
СколькоНужно28 = НеобходимоеКоличество / 28;
СколькоНужно67 = НеобходимоеКоличество / 67;
Если (СколькоНужно67 < 1) И (СколькоНужно28 < 1) Тогда
Ответ = Количество28 * 1;
Иначе Если (СколькоНужно67 < 1) И (СколькоНужно28 > 1) Тогда
Ответ = Количество67 * 1;

И методами сравнения далее определяем, честно сказать, не знаю как в запросе можно обрабатывать числа т.к. в основном нам будут интересны числа после запятой.
---
может быть перебор всех вариантов бобин с расчетом остатка
и выбор варианта с минимальным остатком

готовим две таблицы с бобинами 1 и 2, помещаем во времТаб
бобина, Количество (при формировании этих таблиц как раз и ограничиваем их остатками)
б1, 0
б1, 1
б1, 2
б1, 3
б1, 4 и тд

и
б2, 0
б2, 1

б2, 2
б2, 3
б2, 4 и тд

соединяем эти две таблицы через запятую, получаем всевозможные варианты сочетаний бодин
и рассчитываем общую длину в каждой строке
затем отбираем строчки где общая длинна чуть больше требуемой,
(так как операции умножения и сложения самые простые, результат будет почти мгновенный)
---
логика:
1. получаем Остаток28 и Остаток67
2. берем целую часть от ТребуетсяХ/67 и сравниваем с остатком67 берем минимум из двух чисел - это количество 67бабин
3 (ТребуетсяХ - количество67бабин * 67)/28 округление вверх и сравниваем с остатком28 - берем минимум из двух чисел это количество 28 бабин

делать эту логику в запросе... ну такое себе.

```1c
    "ВЫБРАТЬ
    | ТоварыНаСкладахОстатки.Товар КАК Товар
    | Поместить втОстатокБабина28
    |ИЗ
    | РегистрНакопления.ТоварыНаСкладах.Остатки(, , , Товар = &Бабина28) КАК ТоварыНаСкладахОстатки
    | ;
    |ВЫБРАТЬ
    | ТоварыНаСкладахОстатки.Товар КАК Товар
    | Поместить втОстатокБабина67
    |ИЗ
    | РегистрНакопления.ТоварыНаСкладах.Остатки(, , , Товар = &Бабина67) КАК ТоварыНаСкладахОстатки
    | ;
    |ВЫБРАТЬ
    | Выбор
    | Когда ЦЕЛ(&ТребуетсяХ/67) > втОстатокБабина67.Товар Тогда втОстатокБабина67.Товар
    | Иначе ЦЕЛ(&ТребуетсяХ/67)
    | Конец Как Количество67бабин
    | Поместить втКоличество67бабин
    |ИЗ
    | втОстатокБабина67 КАК втОстатокБабина67
    | ;
    |ВЫБРАТЬ
    | Выбор
    | Когда ЦЕЛ((&ТребуетсяХ - втКоличество67бабин.Количество67бабин*67)/28)+1 > втОстатокБабина28.Товар Тогда втОстатокБабина28.Товар
    | Иначе ЦЕЛ((&ТребуетсяХ - втКоличество67бабин.Количество67бабин*67)/28)+1
    | Конец Как Количество28бабин
    | Поместить втКоличество28бабин
    |ИЗ
    | втКоличество67бабин как втКоличество67бабин,
    | втОстатокБабина28 КАК втОстатокБабина28,
    | ;
    |ВЫБРАТЬ
    | втКоличество67бабин.втКоличество67бабин,
    | втКоличество28бабин.втКоличество28бабин
    |ИЗ
    | втКоличество67бабин как втКоличество67бабин,
    | втКоличество28бабин КАК втКоличество28бабин"
```

PS. небольшая не точность в Количество28бабин - так как нет функции окрулить вверх я использовал взять цел и +1 - это не всегда равно окрулению вверх, если у нас число кратно 28.
---
и сравниваем с остатком28 - берем минимум
Мин(Цел(НеобходимоеКоличество / 67),ОстатокНаСкладе67) ???
---
это в коде такое возможно.
тока если речь про остаток28, то так: МИН(Цел(НеобходимоеКоличество/28), остатокНаСкладе28)

в запросе так низя.

смысл в том,чтобы не взять больше чем остаток на складе..

PS. благодарю за $m
---
Если нужно 132м то не правильно считает 1 по 67 и 3 по 28
А нужно 2 по 67
---
т.е. критерий правильно/ не правильно - это минимум выражения количество67*67+количество28*28 ?
сейчас подумаю какой алгоритм составить, для такого результата.
---
нужно оптимальное количество бобин, тоесть если 132 м надо то 132/67 будет 1,97
Значит 2 бобины по 67 будет оптимально чем 1 по 67 и 3 по 28
---
алгоритм:
1. подготавливаем таблицу нумерации от 0 до 99.
2. считаем все возможные варианты количества 67 бабин
от 0 до максимум Кол67 = Мин( ЦЕЛ(&НеобходимоеКоличество / 67) + 1, Остаток67)

3. к каждому варианту количества 67 бабин считаем количество 28 бабин по формуле
Кол28=Мин(ОкруглВверх((ТребуетсяХ - Кол67)/28), Остаток28)
4. находим минимум кол67*67+кол28*28

PS. ограничение метода в количестве предварительной таблицы в 67*100=6700 метров т.к. у нас сотня вариантов заложена нумератором. Можно заложить больше, но в любом случае будет некое ограничение.

ВЫБРАТЬ
 0 КАК Номер
ПОМЕСТИТЬ втПятерка

ОБЪЕДИНИТЬ

ВЫБРАТЬ
 1

ОБЪЕДИНИТЬ

ВЫБРАТЬ
 2

ОБЪЕДИНИТЬ

ВЫБРАТЬ
 3

ОБЪЕДИНИТЬ

ВЫБРАТЬ
 4
;

////////////////////////////////////////////////////////////­////////////////////
ВЫБРАТЬ
 АВТОНОМЕРЗАПИСИ() КАК Номер,
 Пятерка1.Номер КАК НомерП
ПОМЕСТИТЬ втСотня
ИЗ
 втПятерка КАК Пятерка1,
 втПятерка КАК Пятерка2,
 втПятерка КАК Пятерка3
;

////////////////////////////////////////////////////////////­////////////////////
ВЫБРАТЬ
 ТЗ.Количество28 КАК Кол28,
 ТЗ.Количество67 КАК Кол67
ПОМЕСТИТЬ втОстатки
ИЗ
 &ТЗ КАК ТЗ
;

////////////////////////////////////////////////////////////­////////////////////
ВЫБРАТЬ
 ВЫБОР
  КОГДА ЦЕЛ(&НеобходимоеКоличество / 67) + 1 >= втОстатки.Кол67
   ТОГДА втОстатки.Кол67
  ИНАЧЕ ЦЕЛ(&НеобходимоеКоличество / 67) + 1
 КОНЕЦ КАК МаксКол67
ПОМЕСТИТЬ втОстатки67
ИЗ
 втОстатки КАК втОстатки
;

////////////////////////////////////////////////////////////­////////////////////
ВЫБРАТЬ
 втСотня.Номер - 1 КАК Кол67,
 ВЫБОР
  КОГДА (&НеобходимоеКоличество - (втСотня.Номер + 1) * 67) / 28 > Остатки.Кол28
   ТОГДА Остатки.Кол28
  ИНАЧЕ (ВЫРАЗИТЬ((&НеобходимоеКоличество - (втСотня.Номер + 1) * 67) / 28 - 0.5 КАК ЧИСЛО(15, 0))) + 1
 КОНЕЦ КАК Кол28
ПОМЕСТИТЬ втВсеВариантыБабин
ИЗ
 втСотня КАК втСотня,
 втОстатки67 КАК втОстатки67,
 втОстатки КАК Остатки
ГДЕ
 втСотня.Номер - 1 >= втОстатки67.МаксКол67
;

////////////////////////////////////////////////////////////­////////////////////
ВЫБРАТЬ ПЕРВЫЕ 5
 ВсеВариантыБабин.Кол67 КАК Кол67,
 ВсеВариантыБабин.Кол28 КАК Кол28,
 ВсеВариантыБабин.Кол67 * 67 + ВсеВариантыБабин.Кол28 * 28 КАК Сортировка
ИЗ
 втВсеВариантыБабин КАК ВсеВариантыБабин

УПОРЯДОЧИТЬ ПО
 Сортировка
---
При 132 выдает -91 и 38
---
со знаками накосячил.
поправил условия прикрепил обработку
---
внесу свой гов.. запрос, 35 и 280 это значение в метрах, которое будет передаваться в качестве параметра. Не продумал до конца логику, вдруг немного поможет)

```1c
    ВЫБРАТЬ
    ВЫБОР
    КОГДА 35 > 28
    ТОГДА
    ВЫБОР КОГДА ЦЕЛ(35 / 28) = 35 ТОГДА ЦЕЛ(35 / 28)
    КОГДА ЦЕЛ(35 / 28) > 28ТОГДА ЦЕЛ(35 / 28) + 1
    ИНАЧЕ 0
    КОНЕЦ
    КОГДА 35 = 28
    ТОГДА
    1
    ИНАЧЕ
    0
    КОНЕЦ КАК Количество28

    ,ВЫБОР
    КОГДА 280 >= 67
    ТОГДА
    ЦЕЛ(280 / 67)
    ИНАЧЕ
    0
    КОНЕЦ КАК Количество67
```
---
если сами не осилите такой запрос, пишите
(очень оригинальненькая и интересная задачка)
---
вот решение для произвольных чисел.

Соответственно в вашем варианте ячейки это конкретные бабины. Сколько ячеек столько и бабин

http://fixin.com.ru/articles/alg_makesumm/article.htm
---
нет там запроса
---
не думал что это принципиально
---
согласен, но не нам же решать
таковы условия задачи
такие дела
---
```1c
    Количество67 = Цел(НеобходимоеКоличество / 67);
    Количество28 = ОкрБольш((НеобходимоеКоличество-Количество67*67)/28);

    Если ОстатокНаСкладе28 - Количество28 < 0 Тогда
    Сообщить("Не хватает 28 м. бабин в количестве: " + Строка(Количество28 - ОстатокНаСкладе28) + " шт.")
    КонецЕсли;

    Если ОстатокНаСкладе67 - Количество67 < 0 Тогда
    Сообщить("Не хватает 67 м. бабин в количестве: " + Строка(Количество67 - ОстатокНаСкладе67) + " шт.")
    КонецЕсли;

    Функция ОкрБольш(Ч, Разрядность=0) Экспорт
    Возврат Окр(Ч+Pow(10, Разрядность)/2, Разрядность, РежимОкругления.Окр15как10);
    КонецФункции
```
---
```1c
    ВЫБРАТЬ
    ТЗ.Количество28 КАК Количество28,
    ТЗ.Количество67 КАК Количество67
    ПОМЕСТИТЬ ОстаткиНаСкладе
    ИЗ
    &ТЗ КАК ТЗ
    ;

    ////////////////////////////////////////////////////////////­////////////////////
    ВЫБРАТЬ
    ВЫБОР
    КОГДА ЦЕЛ(&НеобходимоеКоличество / 67) > ОстаткиНаСкладе.Количество67
    ТОГДА ОстаткиНаСкладе.Количество67
    ИНАЧЕ ЦЕЛ(&НеобходимоеКоличество / 67)
    КОНЕЦ КАК Количество67,
    ВЫБОР
    КОГДА (&НеобходимоеКоличество - ЦЕЛ(&НеобходимоеКоличество / 67) * 67) / 28 > ОстаткиНаСкладе.Количество28
    ТОГДА ОстаткиНаСкладе.Количество28
    ИНАЧЕ (ВЫРАЗИТЬ((&НеобходимоеКоличество - ЦЕЛ(&НеобходимоеКоличество / 67) * 67) / 28 - 0.5 КАК ЧИСЛО(15, 0))) + 1
    КОНЕЦ КАК Количество28
    ИЗ
    ОстаткиНаСкладе КАК ОстаткиНаСкладе
```
---
кому интересно, попробуйте такой вариант

```1c
    //таб с номерами > мак остатка (для примера взято 10)
    Выбрать 0 как Номер поместить втНомера
    объединить все Выбрать 1
    объединить все Выбрать 2
    объединить все Выбрать 3
    объединить все Выбрать 4
    объединить все Выбрать 5
    объединить все Выбрать 6
    объединить все Выбрать 7
    объединить все Выбрать 8
    объединить все Выбрать 9
    объединить все Выбрать 10
    ;
    //эмуляция запроса остатков по бобинам
    Выбрать "Б28" как товар, 9 как Количество поместить втОстатки
    объединить все Выбрать "Б67", 8
    ;
    //две таблицы с товарами для расчета
    Выбрать
    втОстатки.Товар,
    втНомера.Номер как Количество,
    28 как длина
    поместить втТаб1
    из
    втОстатки
    левое соединение втНомера
    по втОстатки.Количество >= втНомера.Номер
    где
    втОстатки.Товар = "Б28"
    ;
    Выбрать
    втОстатки.Товар,
    втНомера.Номер как Количество,
    67 как длина
    поместить втТаб2
    из
    втОстатки
    левое соединение втНомера
    по втОстатки.Количество >= втНомера.Номер
    где
    втОстатки.Товар = "Б67"
    ;
    Выбрать первые 1 //3
    т1.Товар,
    т1.Количество,
    т2.Товар,
    т2.Количество,
    т1.Количество * т1.Длина + т2.Количество * т2.Длина как Длина
    из
    втТаб1 т1, втТаб2 т2
    где
    т1.Количество * т1.Длина + т2.Количество * т2.Длина >= &НужнаяДлина
    Упорядочить по
    5
```
---
Скучно....
Усложнил задачу из спортивного интереса.
Бобины могут быть любой длины, а не только 67 и 28. У каждой бобины есть место хранения и дата производства.
Необходимо подобрать либо заданное количество метража, а если подобрать не удаётся то ближайшее большее. При этом необходимо стараться подбирать бобины с максимальной длиной в первую очередь и среди прочих равных брать с более ранней датой производства. Если среди двух одинаковых бобин дата производства одинаковая, то первую попавшуюся. Не важно какую из этих двух.
Если метража на складе не достаточно, то в результате должны быть все имеющиеся.
Результат: метраж бобины через запятую, места хранения через запятую, общий метраж (sum_kol).
Для понимания алгоритма вывел ещё и служебную не обязательную колонку (sum_npp).

https://onecompiler.com/postgresql/42m5hvdy6

Запрос на PsgSQL. В начале задается количество в
vt_const as (SELECT 1000 as a)
Вместо 1000 можно рисовать любые положительные циферки. Это тот метраж, который запросил клиент.
---
Проверял кто результат в запросе {26}?
Для 129 и 132 будут результаты 129 и 133 (на скринах).

Такой же по сути запрос отработал и на SQLite и на MS SQL.
Только в 1С, к огромному сожалению, нет рекурсивных запросов и обычной функции нумерации (только подобие, реализованное странным образом).
---
вариант в {10} для значения 84. Там вообще резать ничего не нужно, но и 67 брать не нужно.