Исправление ошибки арифметического переполнения при преобразовании numeric к типу данных numeric при расчете себестоимости




Принцип обмена данными из 1С с сайтом (на MySQL) и выдачи (публикации) этих данных по запросу.
PHP-Скрипт автоматической загрузки данных из файла данных в формате CSV в базу данных сайта работающего на WordPress.

В продолжение моей темы: 1С:Альфа-Авто Автосалон Автосервис: обмен с сайтом.
С помощью данного скрипта можно загружать в автоматическом режиме, по расписанию, данные сервисных книжек (ремонтов авто) из 1С:Альфа-Авто Автосалон Автосервис.
Также можно загружать данные в ручном режиме: для этого делается скрытая страница, где размещается специальная кнопка.
Комментарии размещенные внутри скрипта разъяснят логику и порядок действия.
Комментарии с "/////    echo" использовались для отладки.
Дополнительно создана таблица для журналирования результатов загрузки данных.
Скрипт включает в себя защиту от SQL инъекций (думаю безопасность соблюдена в полной мере).
В кратце:
1. Пишется скрипт, который запускает этот.
2. Создается регламентное задание в WordPress, по которому запускается скрипт из п.1. 
3. Этот скрипт осуществляет проверку на существование файла обмена в папке.
4. Если данные не новые, загрузка не производится.
5. Если данные новые, очищается таблица сервисных книжек.
6. Загружаются новые данные.

Собственно сам скрипт:

<?php // Полная загрузка сервисных книжек, создан 2026-01-05 12:44:55

global $wpdb2;
global $failure;
global $file_hist;

/////  echo '<H2><b>Старт загрузки</b></H2><br>';

$failure=FALSE;
//подключаемся к базе
$wpdb2 = include_once 'connection.php'; ; // подключаемся к MySQL
// если не удалось подключиться, и нужно оборвать PHP с сообщением об этой ошибке
if (!empty($wpdb2->error))
{
/////   echo '<H2><b>Ошибка подключения к БД, завершение.</b></H2><br>';
$failure=TRUE;
wp_die( $wpdb2->error );
}

$m_size_file=0;
$m_mtime_file=0;
$m_comment='';
/////проверка существования файлов выгрузки из 1С
////файл выгрузки сервисных книжек
$file_hist = ABSPATH.'/_1c_alfa_exchange/AA_hist.csv';
if (!file_exists($file_hist))
{
/////   echo '<H2><b>Файл обмена с сервисными книжками не существует.</b></H2><br>';
$m_comment='Файл обмена с сервисными книжками не существует';
$failure=TRUE;
}

/////инициируем таблицу лога
/////если не существует файла то возврат и ничего не делаем
if ($failure){
///включает защиту от SQL инъекций и данные можно передавать как есть, например: $_GET['foo']
/////   echo '<H2><b>Попытка вставить запись в лог таблицу</b></H2><br>';
$insert_fail_zapros=$wpdb2->insert('vin_logs', array('time_stamp'=>time(),'last_mtime_upload'=>$m_mtime_file,'last_size_upload'=>$m_size_file,'comment'=>$m_comment));
wp_die();
/////    echo '<H2><b>Возврат в начало.</b></H2><br>';
return $failure;
}
/////проверка лога загрузки, что бы не загружать тоже самое
$masiv_data_file=stat($file_hist);   ////передаем в массив свойство файла
$m_size_file=$masiv_data_file[7];    ////получаем размер файла
$m_mtime_file=$masiv_data_file[9];   ////получаем дату модификации файла
////создаем запрос на получение последней удачной загрузки
////выбираем по штампу времени создания (редактирования) файла загрузки AA_hist.csv, $m_mtime_file

/////   echo '<H2><b>Размер файла: '.$m_size_file.'</b></H2><br>';
/////   echo '<H2><b>Штамп времени файла: '.$m_mtime_file.'</b></H2><br>';
/////   echo '<H2><b>Формирование запроса на выборку из лога</b></H2><br>';
////препарируем запрос
$text_zaprosa=$wpdb2->prepare("SELECT * FROM `vin_logs` WHERE `last_mtime_upload` = %s", $m_mtime_file);
$results=$wpdb2->get_results($text_zaprosa);

if ($results)
{   foreach ( $results as $r)
{
////если штамп времени и размер файла совпадают, возврат
if (($r->last_mtime_upload==$m_mtime_file) && ($r->last_size_upload==$m_size_file))
{////echo '<H2><b>Возврат в начало, т.к. найдена запись в логе.</b></H2><br>';
$insert_fail_zapros=$wpdb2->insert('vin_logs', array('time_stamp'=>time(),'last_mtime_upload'=>$m_mtime_file,'last_size_upload'=>$m_size_file,'comment'=>'Загрузка отменена, новых данных нет, т.к. найдена запись в логе.'));
wp_die();
return $failure;
}
}
}
////если данные новые, пишем в лог запись о начале загрузки
/////echo '<H2><b>Попытка вставить запись о начале загрузки в лог таблицу</b></H2><br>';
$insert_fail_zapros=$wpdb2->insert('vin_logs', array('time_stamp'=>time(),'last_mtime_upload'=>0, 'last_size_upload'=>$m_size_file, 'comment'=>'Начало загрузки'));

////очищаем таблицу
$clear_tbl_zap=$wpdb2->prepare("TRUNCATE TABLE %s", 'vin_history');
$clear_tbl_zap_repl=str_replace("'","`",$clear_tbl_zap);
$results=$wpdb2->query($clear_tbl_zap_repl);
/////   echo '<H2><b>Очистка таблицы сервисных книжек</b></H2><br>';
if (empty($results))
{
/////   echo '<H2><b>Ошибка очистки таблицы книжек, завершение.</b></H2><br>';
//// если очистка не удалась, возврат
$failure=TRUE;
wp_die();
return $failure;
}

////загружаем данные
$table='vin_history';         // Имя таблицы для импорта
//$file_hist Имя CSV файла, откуда берется информация     // (путь от корня web-сервера)
$delim=';';          // Разделитель полей в CSV файле
$enclosed='"';      // Кавычки для содержимого полей
$escaped='\

22 Comments

  1. v3rter

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

    Reply ↓
  2. alex_4x

    В данном случае помогло, но это не факт, что всегда поможет.

    Проблема может быть не только с длиной целой части, но и длиной дробной части.

    Суть ошибки в том, что во время вычисления — SQL не может преобразовать тип внутри вычисления как правило умножения или деления. Решается не обязательно увеличением разрядности, достаточно ВЫРАЗИТЬ( Значение , ХХ,ХХ) использовать для всех вычисляемых аргументов в том числе внутри скобок. Тогда SQL не занимается самодеятельностью по выбору типа внутри самого вычисления и ошибка не происходит.

    Ну примерно так:

    Было:

     ВЫРАЗИТЬ(СУММА(ЕСТЬNULL(ВтТаблицаРешений.ПостояннаяРазница, 0) * ЕСТЬNULL(ПеремещенияСписания.Количество, 0)) КАК ЧИСЛО(23, 10)))
    

    Будет

    ВЫРАЗИТЬ
    СУММА(ВЫРАЗИТЬ ЕСТЬNULL(ПеремещенияСписания.Количество, 0) КАК ЧИСЛО(23, 10) *
    ВЫРАЗИТЬ ЕСТЬNULL(ПеремещенияСписания.Количество, 0) КАК ЧИСЛО(23, 10) )
    КАК ЧИСЛО(23, 10)
    Reply ↓
  3. v3rter

    При таких расчетах могут накапливаться и ошибки округления дробной части, здесь это нивелируется десятью знаками после запятой ЧИСЛО(…, 10). Попутно может всплыть проблема сравнения итогов с нулём или константой, так как из-за накопления ошибок округления дробной части выглядеть она будет как -0.0001 < x <0,0001

    Reply ↓
  4. a.artemov

    Коллеги прошу помощи.

    Увеличение разрядности не помогло.

    Ошибка осталась. Кто может помочь?

    Ошибка при выполнении обработчика — ‘ОбработкаПроведения’

    по причине:

    {ОбщийМодуль.КорректировкаСтоимостиУчетЗатрат.Модуль(403)}: Ошибка при вызове метода контекста (Выполнить)

    Док.Записать(РежимЗаписиДокумента.Проведение);

    по причине:

    Ошибка выполнения запроса

    по причине:

    Ошибка при выполнении операции над данными:

    Microsoft SQL Server Native Client 11.0: Ошибка арифметического переполнения при преобразовании numeric к типу данных numeric.

    HRESULT=80040E57, SQLSrvr: SQLSTATE=22003, state=8, Severity=10, native=8115, line=1

    Код такой:

    СУММА(ВЫРАЗИТЬ(
    |  ВЫБОР КОГДА УчетЗатрат.ВидДвижения = ЗНАЧЕНИЕ(ВидДвиженияНакопления.Расход) ТОГДА
    |   УчетЗатрат.Стоимость
    |  ИНАЧЕ
    |   0
    |  КОНЕЦ
    | КАК ЧИСЛО(38,10))) КАК Стоимость
    Reply ↓
  5. Al-77

    (5) Какая конфигурация?

    Reply ↓
  6. Al-77

    Какая конфигурация?

    Reply ↓
  7. a.artemov

    Управление производственным предприятием, редакция 1.3 (1.3.89.2)

    Если есть возможность помочь для более оперативного общения контакты на почту прислать? У меня каждый день на счету.

    Reply ↓
  8. rintik

    А если попробовать поменять

    | КАК ЧИСЛО(38,10))) КАК Стоимость

    на

    | КАК ЧИСЛО(32,10))) КАК Стоимость
    Reply ↓
  9. корум

    (9) читаем статью, «не хватает знаков ДО запятой», метод решения:

    Если есть строчки ВЫРАЗИТЬ(<ВыбранноеПоле> КАК ЧИСЛО (23,10)) изменяем их на ВЫРАЗИТЬ(<ВыбранноеПоле> КАК ЧИСЛО (25,10)) и радуемся результату.

    Читаем (9) и понимаем, что уменьшение знаков до запятой не поможет…

    Reply ↓
  10. Al-77

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

    Reply ↓
  11. a.artemov

    (9) Начинали с 25,10 дошли до 38,10 не помогает. ТЬочнее попробовал 40,10 ругнулась что перебор…

    Reply ↓
  12. a.artemov

    Проблема не решена. Помогите кто может.

    Reply ↓
  13. Napalmmm

    Вот и я попал. (37,10) стояло больше двух лет, сейчас не спасло. Решения нет?

    Reply ↓
  14. see888

    Теперь и я) как перевел базу на с 11.1 на 11.3.4… даже не пойму что он там по кругу выполняет,с каждым выполнением отклонение растет. Что это такое вообще? Кто сможет объяснить:

    Дополнительная информация об этапе:

    — Отклонение на текущей итерации: 42 631,8

    — Отклонение на текущей итерации: 24 780

    — Отклонение на текущей итерации: 21 311,8

    — Отклонение на текущей итерации: 17 797,43

    — Отклонение на текущей итерации: 7 800

    — Отклонение на текущей итерации: 20 588,5568513129

    — Отклонение на текущей итерации: 20 588,556851313

    — Отклонение на текущей итерации: 73 530,5601832607

    — Отклонение на текущей итерации: 73 530,5601832608

    — Отклонение на текущей итерации: 262 609,1435116457

    — Отклонение на текущей итерации: 262 609,1435116456

    — Отклонение на текущей итерации: 937 889,7982558771

    — Отклонение на текущей итерации: 937 889,798255877

    — Отклонение на текущей итерации: 3 349 606,4223424179

    — Отклонение на текущей итерации: 3 349 606,422342418

    — Отклонение на текущей итерации: 11 962 880,07979435

    — Отклонение на текущей итерации: 11 962 880,07979435

    — Отклонение на текущей итерации: 42 724 571,71355125

    — Отклонение на текущей итерации: 42 724 571,71355125

    — Отклонение на текущей итерации: 152 587 756,1198258929

    — Отклонение на текущей итерации: 152 587 756,119825893

    — Отклонение на текущей итерации: 544 956 271,8565210464

    — Отклонение на текущей итерации: 544 956 271,8565210464

    — Отклонение на текущей итерации: 1 946 272 399,4875751657

    — Отклонение на текущей итерации: 1 946 272 399,4875751656

    — Отклонение на текущей итерации: 6 950 972 855,3127684486

    — Отклонение на текущей итерации: 6 950 972 855,3127684486

    — Отклонение на текущей итерации: 24 824 903 054,688458745

    — Отклонение на текущей итерации: 24 824 903 054,688458745

    — Отклонение на текущей итерации: 88 660 368 052,4587812321

    — Отклонение на текущей итерации: 88 660 368 052,458781232

    — Отклонение на текущей итерации: 316 644 171 615,9242186857

    Это то что под приложением выдал из состоянии расчета.

    А в отладчике последнее значение было таково :

    267 488 214 754 648 642 684 559 809,9800832

    и это на 85 Итерации из 200 как он показывает.

    Что тут вообще происходит?)

    Reply ↓
  15. Glav

    Такая же проблема. Увеличение разряда не помогает УТ 11.3.4.124

    Reply ↓
  16. jefjef

    Смысл ошибки можно сформулировать проще.

    Запрос не может выразить полученное при исполнении числовое значение, так как в результате получено число больше, чем разрешено условиями запроса.

    Если будет конструкция ВЫРАЗИТЬ(1,0) а запросу попадется число 11, то он не сможет выразить его числом от 0 до 9.

    Так то у вас все написано правильно, но к сути проблемы докопаться сложно.

    Reply ↓
  17. dap

    (3)

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

    Вот строка запроса в обработке базопузомера:

    Подсчитывалось количество записей в регистре сведений

    | СУММА(1) КАК Счетчик

    Вот этот счетчик и выдал ошибку. Количество записей в самом большом регистре было около 62 млн. записей

    и нормально заработала по вашему совету вот так:

    | СУММА(ВЫРАЗИТЬ (1 КАК Число(23,0))) КАК Счетчик

    плюсую

    Reply ↓
  18. svetanik
    Reply ↓
  19. Afandi

    Такая же проблема. Запрос к регистру ВыручкаИСебестоимостьПродаж показал что каждый документ записан 2 раза один раз с правильным количеством товара и суммы. а второй раз с правильной суммой а количество равное нулю. После отмены проведения и повторно ручного проведения запись документа в этом регистре с нулевым количеством исчезает. Это стандартное поведение или ошибка в системе?

    Reply ↓
  20. TelekaevAB
    Reply ↓
  21. kabantus
    Reply ↓
  22. Red_Devil

    (3)тоже самое. Пришлось всем полям ВЫРАЗИТЬ писать. Тогда ошибка ушла

    СУММА(ВЫРАЗИТЬ(
    |  ВЫБОР КОГДА ВЫРАЗИТЬ(УзлыКорректировкиСтоимостиСписания.Количество КАК ЧИСЛО(23, 10)) <> 0 ТОГДА
    |   ВЫРАЗИТЬ(ТаблицаРешений.Стоимость КАК ЧИСЛО(23, 10)) *
    |   (ВЫБОР КОГДА ВЫРАЗИТЬ(ВложенныйЗапрос.Количество КАК ЧИСЛО(23, 10)) = 0 ТОГДА
    |    ВЫРАЗИТЬ(ВложенныйЗапрос.Стоимость КАК ЧИСЛО(23, 10))
    |   ИНАЧЕ
    |    ВЫРАЗИТЬ(ВложенныйЗапрос.Количество КАК ЧИСЛО(23, 10))
    |   КОНЕЦ) /
    |   ВЫРАЗИТЬ(УзлыКорректировкиСтоимостиСписания.Количество КАК ЧИСЛО(23, 10))
    |  ИНАЧЕ
    |   0
    |  КОНЕЦ
    | КАК ЧИСЛО(23,10))) КАК Стоимость

    Показать

    Reply ↓

Leave a Comment

Ваш адрес email не будет опубликован. Обязательные поля помечены *