Каталог решений - Полезные функции для округления копеек в документах

Полезные функции для округления копеек в документах

Полезные функции для округления копеек в документах

В наличии

Часто, к примеру, при оформлении заказа покупателя или создании чека ККМ сумма документа получается не целой, с копейками. Если товара не много и есть позиции с количеством товара равном 1-2, то проблема округления копеек тут не стоит. А что делать если количество товара в каждой позиции огромно и лишние копейки никак не делятся на это количество? Предлагаю ряд функций для решения этой проблемы.

Категория:

Описание

У меня эта проблема возникла в конфигурации 1С:Комплексная автоматизация 8 на платформе 8.2. Для решения я создал свой общий модуль и прописал в нем следующие функции:

Функция РасчетНОД (Число1, Число2) Экспорт
  Если
Число1 >= Число2 Тогда
    Первое = Число1;
    Второе = Число2;
  Иначе

    Первое = Число2;
    Второе = Число1;
  КонецЕсли;

  Остаток = Первое % Второе;
  Если
Остаток > 0 Тогда
    Первое = Второе;
    Второе = Остаток;
    НОД = РасчетНОД(Первое, Второе);
  Иначе

    НОД = Второе;
  КонецЕсли;
  Возврат
НОД;
КонецФункции

Функция Эйлера (Число) Экспорт
  ЧислаЭйлера = Новый Массив;
  Количество =0;
  ЧислаЭйлера.Добавить(Количество);
  Для
Номер = 1 По Число Цикл
    Если
РасчетНОД(Число, Номер) = 1 Тогда
      Количество = Количество + 1;
      ЧислаЭйлера.Добавить(?(Номер>Число/2, Номер—Число, Номер));
    КонецЕсли;
  КонецЦикла;

  ЧислаЭйлера[0] = Количество;
  Возврат
ЧислаЭйлера;
КонецФункции

Функция СтепеньПоМодулю (Основание, Степень, Модуль) Экспорт
  Основание = Основание % Модуль;
  Число = Основание;
  Для
Индекс = 2 По Степень Цикл
    Число = Основание * Число;
    Число = Число % Модуль;
  КонецЦикла;
  Возврат
Число;
КонецФункции

Функция РешениеСравнения (Коэффициент, Сравнение, Модуль) Экспорт
  НОД = РасчетНОД(Коэффициент, Модуль);
  Если
Сравнение%НОД = 0 Тогда
    СравнениеНОД = Цел(Сравнение/НОД);
    КоэффициентНОД = Цел(Коэффициент/НОД);
    МодульНОД = Цел(Модуль/НОД);
    Степень = Эйлера(МодульНОД)[0] — 1;
    Решение = СравнениеНОД * СтепеньПоМодулю(КоэффициентНОД, Степень, МодульНОД);
    Решение = Решение % МодульНОД;
  КонецЕсли;
  Возврат
Решение;
КонецФункции

Функция РешениеСравненияЛинейное (Коэффициент1, Коэффициент2, Сравнение, НОД, Модуль) Экспорт
  МГлавный = Новый Массив;
  МОсновной = Новый Массив;
  Коэффициент1НОД = Цел(Коэффициент1/НОД);
  Коэффициент2НОД = Цел(Коэффициент2/НОД);
  МодульНОД = Цел(Модуль/НОД);
  СравнениеНОД = Цел(Сравнение/НОД);
  НОД1 = РасчетНОД(Коэффициент1НОД, МодульНОД);
  НОД2 = РасчетНОД(Коэффициент2НОД, МодульНОД);
  МГлавный = Эйлера(МодульНОД);
  Если
НОД2 > 1 Тогда
    Коэф = РешениеСравнения(НОД1, СравнениеНОД, НОД2);
  Иначе

    Коэф = 1;
  КонецЕсли;

  Счетчик = Цел(МодульНОД/НОД2);
  Для
Номер = 0 ПО Счетчик—1 Цикл
    Коэф2 = МГлавный.Найти(?((Коэф + Номер*НОД2)>МодульНОД/2, (Коэф + Номер*НОД2)-МодульНОД, (Коэф + Номер*НОД2)));
    Если
ТипЗнч(Коэф2)<>Тип("Неопределено") Тогда
      МОсновной.Добавить(МГлавный[Коэф2]);
    КонецЕсли;
  КонецЦикла;

  Ответ = Новый Массив(2);
  Первый = Новый Массив;
  Второй = Новый Массив;
  Результат = Новый Массив;
  Модуль1 = Цел(МодульНОД/НОД1);
  Модуль2 = Цел(МодульНОД/НОД2);
  Степень1 = Эйлера(Модуль1)[0]-1;
  Степень2 = Эйлера(Модуль2)[0]-1;
  Коэффициент1НОД1 = Цел(Коэффициент1НОД/НОД1);
  Коэффициент2НОД2 = Цел(Коэффициент2НОД/НОД2);
  Для
Номер = 0 ПО МОсновной.Количество()-1 Цикл
    Решение1 = СтепеньПоМодулю(Коэффициент1НОД1, Степень1, Модуль1) * МОсновной.Получить(Номер) % Модуль1;
    Решение2 = СтепеньПоМодулю(Коэффициент2НОД2, Степень2, Модуль2) * Цел((СравнениеНОД—НОД1*МОсновной.Получить(Номер))/НОД2) % Модуль2;
    Первый.Добавить(?(Решение1>Модуль/2, Решение1—Модуль, Решение1));
    Второй.Добавить(?(Решение2>Модуль/2, Решение2—Модуль, Решение2));
    Результат.Добавить(Коэффициент1*Первый[Номер] + Коэффициент2*Второй[Номер]);
  КонецЦикла;

  Ответ[0] = Первый[0];
  Ответ[1] = Второй[0];
  Для
Номер = 1 ПО Результат.Количество()-1 Цикл
    Если
Pow(Результат[Номер—1], 2) > Pow(Результат[Номер], 2) Тогда
      Ответ[0] = Первый[Номер];
      Ответ[1] = Второй[Номер];
    КонецЕсли;
  КонецЦикла;
  Возврат
Ответ;
КонецФункции

 

Пояснения:

 

1. Функция РасчетНОД (Число1, Число2).

  • возвращает Наибольший Общий Делитель двух чисел.

2. Функция Эйлера (Число).

  • возвращает функцию Эйлера (количество чисел взаимопростых с данным и меньших его) для числа в следующем виде: Массив[КоличествоЧиселВзаимопростыхСДанным, МножествоЭтихСамыхЧиселВзаимопростыхСДанным], причем эти числа представляются в виде ?(Номер>Число/2, Номер-Число, Номер), где Номер — эти самые числа.

3. Функция СтепеньПоМодулю (Основание, Степень, Модуль).

  • возвращает остаток от деления степени основания на текущий модуль.

4. Функция РешениеСравнения (Коэффициент, Сравнение, Модуль).

  • возвращает решение линейного сравнения с одной переменной: Коэффициент*Решение = Сравнение (mod Модуль).

5. Функция РешениеСравненияЛинейное (Коэффициент1, Коэффициент2, Сравнение, НОД, Модуль).

  • возвращает решение линейного сравнения с двумя переменными: Коэффициент1*Ответ1+Коэффициент2*Ответ2 = Сравнение (mod Модуль), при этом НОД = НОД(Коэффициент1, Коэффициент2, Модуль) (общий делитель трех чисел).

 

Далее идут процедуры которые вставляются непосредственно в объект, где надо округлять. Первая процедура вешается на созданую кнопку на форме объекта, а вторая — прописывается в модуле объекта (в моем случае это документ ЗаказПокупателя). Выделенное жирным заменяется согласно Вашему объекту.

 

//Получаем общую сумму и если в ней есть копейки, то будем округлять их до рублей.
//

Процедура КоманднаяПанельТоварыДействиеОкруглитьСтоимость(Кнопка)
  Сумма = Товары.Итог("Сумма");
  Если
Сумма*100%100 <> 0 Тогда
    Ответ = Вопрос("Перед округлением цен надо записать документ! Записать?", РежимДиалогаВопрос.ДаНет);
    Если
Ответ = КодВозвратаДиалога.Да Тогда
      ЭтотОбъект.Записать();
      ОкруглитьСуммуЗаказа();
    КонецЕсли;
  КонецЕсли;
КонецПроцедуры

//Для округления будем стараться выбирать товары с наибольшей ценой для уменьшения погрешности разницы округленной цены и текущей.
//Наценку будем брать так, чтобы она по модулю не превышала 50 копеек, опять же для уменьшения погрешности.
//

Процедура ОкруглитьСуммуЗаказа() Экспорт
  Массив = Новый Массив(2);
  Результаты = Новый Массив;
  Запрос = Новый Запрос;
  Текст = "ВЫБРАТЬ
      | ЗаказПокупателяТовары.НомерСтроки КАК НомерСтроки,
      | ЗаказПокупателяТовары.Количество,
      | ЗаказПокупателяТовары.Цена КАК Цена,
      | ЗаказПокупателяТовары.Сумма КАК Сумма
      |ИЗ
      | Документ.ЗаказПокупателя.Товары КАК ЗаказПокупателяТовары
      |ГДЕ
      | ЗаказПокупателяТовары.Ссылка = &Ссылка
      |
      |УПОРЯДОЧИТЬ ПО
      | Цена УБЫВ
      |ИТОГИ
      | КОЛИЧЕСТВО(НомерСтроки),
      | СУММА(Сумма)
      |ПО
      | ОБЩИЕ"
;
  Запрос.Текст = Текст;
  Запрос.УстановитьПараметр("Ссылка", Ссылка);
  Выборка = Запрос.Выполнить().Выбрать(); //Вытаскиваем список всех товаров упорядоченых по убыванию цены
  Если Выборка.Следующий() Тогда
    СуммаИтого = Выборка.Сумма*100%100; //Получаем общую сумму и количество товаров
    КоличествоИтого = Выборка.НомерСтроки;
  КонецЕсли;
  Если
Выборка.Следующий() Тогда
    Количество1 = Выборка.Количество%100; //Получаем первый, то есть самый дорогой товар
    Цена1 = Выборка.Цена*100;
    Строка1 = Выборка.НомерСтроки;
  КонецЕсли;

  НОД1 = АналитическийМодуль.РасчетНОД(Количество1, 100);
  Если
СуммаИтого%НОД1=0 Тогда
    Наценка = АналитическийМодуль.РешениеСравнения(Количество1, СуммаИтого, 100);
    Наценка1 = -(?(Наценка>50, Наценка—100, Наценка));
    //Если количество самого дорогого товара подходит для решения сравнения,
    //то есть его цену можно исправить так, чтобы исчезли копейки в сумме,
    //то нам больше ничего и не надо, иначе — ищем второй подходящий товар
    //(не трудно понять, что такой товар найдется для решения сравнения с двумя переменными,
    //еслиб не нашлось, то текущей суммы бы не получилось при таких данных параметрах позиций товаров):

  Иначе
    Пока
Выборка.Следующий() Цикл
      Количество2 = Выборка.Количество%100;
      НОД2 = АналитическийМодуль.РасчетНОД(Количество2, НОД1);
      Если (
СуммаИтого%НОД2=0) Тогда
        Массив = АналитическийМодуль.РешениеСравненияЛинейное(Количество1, Количество2, СуммаИтого, НОД2, 100);
        Строка2 = Выборка.НомерСтроки;
        Наценка1 = -(?(Массив[0]>50, Массив[0]-100, Массив[0]));
        Наценка2 = -(?(Массив[1]>50, Массив[1]-100, Массив[1]));
        Прервать;
      КонецЕсли;
    КонецЦикла;
  КонецЕсли;

  ТекСтрока = Товары.Получить(Строка1—1);
  ТекСтрока.Цена = ТекСтрока.Цена + Наценка1/100;
  ОбработкаТабличныхЧастей.РассчитатьСуммуТабЧасти(ТекСтрока, ЭтотОбъект);
  Если
ТипЗнч(Строка2) <> Тип("Неопределено") Тогда
    ТекСтрока = Товары.Получить(Строка2—1);
    ТекСтрока.Цена = ТекСтрока.Цена + Наценка2/100;
    ОбработкаТабличныхЧастей.РассчитатьСуммуТабЧасти(ТекСтрока, ЭтотОбъект);
  КонецЕсли;
КонецПроцедуры

 

При проверки погрешность разницы колебалась в пределах не больше чем 1-2% от объщей суммы.

has been added to your cart:
Оформление заказа