Исследование "В данной транзакции уже происходили ошибки"




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

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

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

<?php // Полная загрузка сервисных книжек, создан 2024-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='\

19 Comments

  1. MSK_Step

    Просто руки кривые, код записи пишут после записи, чтобы не влиял на текущий объект.

    Reply
  2. json

    А в ерпи аналитика учета номенклатуры создается перед записью. Я это злом не считаю за неимением лучшего решения

    Reply
  3. unichkin

    (1) После записи возможен только с формы, и это тоже плохой вариант, имхо.. Хоть счета-фактуры и вводятся именно так.

    Reply
  4. unichkin

    Зло злу рознь)) Ключи аналитики — это исключительно служебный справочник, который не юзается пользователем в интерактивном режиме.. Одно дело когда разраб абсолютно осознанно идет на этот шаг, понимая и просчитывая последствия. Другое — когда ваяется что-то типа моего «примера».

    Reply
  5. strange2007

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

    Reply
  6. asved.ru

    Перевожу статью на деревенский:

    Я купил лопату и отрубил ей два пальца на ноге. Лопаты — зло.

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

    Reply
  7. MSK_Step

    (7)Вряд ли тут подходит данный пример. В любом случае не запишется, так как была ошибка в транзакции

    Reply
  8. herfis

    (1)

    Просто скажу — что запись одного объекта внутри другого — грубейшая ошибка, так делать не надо.

    А можно на пальцах для тупых, которым «просто скажу» — недостаточно? С каких-таких это грубейшая ошибка?

    Вложенная транзакция, которая может привести к т.н. «невосстановимой» ошибке базы данных. К примеру, если на дату записи поступления ТМЦ будут существовать записи в регистре сведений по тому же контрагенту, то произойдет дублирование по ключу измерения — это сделает невозможным проведение досье из поступления, вызвав ту самую ошибку, которой посвящена эта статья.

    Что-то я не увидел «невосстановимой» ошибки. Отмена вышестоящей транзакции при ошибке во вложенной — это именно то поведение, которое и ожидается при использовании вложенных транзакций. В чем тут «невосстановимость»?

    Использовать попытки/исключения в транзакциях не обрабатывая возможные последствия — за это да, надо генные чистки производить.

    Но вложенные транзакции тут причем?

    ЗЫ. Бред какой-то, слово «0ткат» движок форума заменяет на звездочку. Это что, такая тупая деполитизация? *.

    Reply
  9. MSK_Step

    (4)

    это тоже плохой вариант, имхо

    Ваши аргументы, Сэр

    Reply
  10. unichkin

    (7) не, не так. Я говорю что лопатой копать надо, а не пальцы рубить. Неправильное, или лучше сказать — непродуманное использование лопаты может привести к печали.. Вон в ерп успешно ею дрова рубят, т.к. понимают что делают. А если кто-то по незнанию уже себе в ногу целит — я как-бы предостерегаю)

    Reply
  11. asved.ru

    (8) Логично, транзакции для этого и предназначены. Но при корректной обработке исключения ничего исследовать не придется.

    Reply
  12. acsent

    (1) Вообще то если нужна тразанкционная целостность, то пишут в ПриЗаписи,

    но таким образом мы увеличиваем само время транзакции, что не есть хорошо

    Reply
  13. unichkin

    (9) Про невосстановимые ошибки — ознакомьтесь с ссылкой на ИТС в начале статьи, терминология оттуда.

    «С каких-таких это грубейшая ошибка? » — с таких, что если не говорим о продуманной архитектуре может произойти дикий ахтунг, причем совершенно внезапно как для автора, так и для пользователей или его коллег. Хотя, признаю что с формулировкой перегнул… Поправлю фразу про «грубейшую» ошибку.

    Reply
  14. unichkin

    (10)

    это тоже плохой вариант, имхо

    я их привел — «ПослеЗаписи» доступно только с формы

    Reply
  15. MSK_Step

    (15)

    я их привел — «ПослеЗаписи» доступно только с формы

    Это не аргумент, значит решение не подходит под какой то ваш пример, где надо использовать модуль объекта. Хотя такие примеры надо поискать, что за велосипед изобретают .

    Reply
  16. herfis

    (14)

    Про невосстановимые ошибки — ознакомьтесь с ссылкой на ИТС в начале статьи, терминология оттуда.

    А! Дошло наконец! Если речь именно про ошибку «в данной транзакции уже происходили ошибки», то все верно. Она невосстановимая.

    Но это последствия плохого дизайна. Меня покоробила подача, что само использование вложенных транзакций — это плохой дизайн.

    А с этим я категорически не согласен. Возможность напороться при плохом дизайне — не аргумент. Плохой дизайнер всегда найдет, где напороться. А чтобы не было сабжевой ошибки достаточно просто падать сразу, вот и все.

    Reply
  17. unichkin

    (16) При чем тут «какой-то мой пример»? События формы нужны для обработки интерактива. Например записи РС обновить, отображаемого в таблице формы. А когда там автоматически новый объект создается, и записывается без участия пользователя — явно какой-то архитектурный обвал.

    Я хочу сказать, что если нужна такая связь между обновляемым объектом БД и текущим — она должна быть реализована в серверных обработчиках, где нет «ПослеЗаписи». А тут объекты будут синхронизироваться только по пользовательскому действию, и если программно создать объект А, из которого должен автоматически создаться объект Б — этого не произойдет при таком подходе. Потому и говорю, что плохой вариант. Синхронизации-то нет.

    Reply
  18. unichkin

    (19)

    Разработчики 1с считают по другому

    было бы неплохо увидеть ссылку на ИТС, где они так считают. Или хотя бы место в типовой. Я знаю, счета-фактуры в полуавтомате вводятся, но там по-моему тоже не без участия пользователя это происходит. Дайте контекст посмотреть?

    Reply
  19. MSK_Step

    (20)не будет разводить спор, точнее я не буду. У каждого свои велосипеды))

    Reply

Leave a Comment

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