Как прибавить разное количество банковских/рабочих дней в запросе на каждую дату возникновения долга




Принцип обмена данными из 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='\

15 Comments

  1. chmv

    Спасибо

    Reply
  2. karpik666

    А что за конфигурацию вы используете, где есть Допустимое число дней задолженности?

    Reply
  3. cj512

    (2) karpik666, это типовой реквизит справочника «Договоры контрагентов» в УПП, нами же был добавлен реквизит «Контроль оплаты в рабочих днях» (тип булево).

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

    Reply
  4. karpik666

    (3) а сколько по времени работает запрос, к сожалению у себя не могу протестировать, как вижу здесь вообще не стоит отбор на календарь, да и на реализации. А что если реализация будет сделана в выходной день? тогда у вас запрос вернет null. И левое соединение никак не обрабатывается, тогда уж лучше сделать внутреннее. И еще один нюанс, если дата реализации 14.10.2015 и кол-во дней просрочки 90 дней, то какую он дату покажет как просрочку?

    Reply
  5. cj512

    (4) karpik666, хорошее замечание. Здесь я описал возможный вариант реализации.

    1. Отбор на календарь не стоит, т.к. в нашем случае актуальный долг мог возникнуть несколько лет назад. В замере производительности запрос на календарь и соединение временной таблицы друг с другом занимает 5-7 мс.

    2. Если реализации выпадает на рабочий день, то можно немного подправить запрос в части соединения.

    Т.е. в этом случае мы выбираем следующий рабочий день от даты реализации. Внутреннее соединение не есть хорошо, т.к. лучше увидеть неверно рассчитанную дату возникновения просрочки, чем не увидеть её вовсе.

    ВЫБРАТЬ
    ВТ.Ссылка,
    ВТ.Дата,
    ВТ.ДнейОтсрочкиДолга КАК ДнейОтсрочкиДолга,
    МИНИМУМ(ВТ_РеглКалендарьСИндексами.ИндексРегламентированногоКалендаря) КАК ИндексДатыДолга
    ПОМЕСТИТЬ ВТ_СИндексом
    ИЗ
    ВТ КАК ВТ
    ЛЕВОЕ СОЕДИНЕНИЕ ВТ_РеглКалендарьСИндексами КАК ВТ_РеглКалендарьСИндексами
    ПО ВТ.Дата <= ВТ_РеглКалендарьСИндексами.ДатаКалендаря
    
    СГРУППИРОВАТЬ ПО
    ВТ.Ссылка,
    ВТ.Дата,
    ВТ.ДнейОтсрочкиДолга

    Показать

    Reply
  6. cj512

    (4) karpik666, но в любом случае, первая таблица к документам реализации не означает получение задолженности. Здесь это всего лишь пример. Как вы получите первоначальную таблицу зависит от вас.

    P.S. В нашем случае дата возникновения долга по разным условиям договора может меняться (долг считается от каждой отгрузки, от полной отгрузки, от определенной даты, от даты подписания документов и др.) .

    Reply
  7. karpik666

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

    Reply
  8. ildarovich

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

    Для полного совершенства публикации не хватает ссылок на другие публикации по той же теме.

    http://infostart.ru/public/99507/ Банковские дни запросом

    http://infostart.ru/public/166349/ Добавить к дате рабочие дни в запросе

    http://infostart.ru/public/320887/ Расчет рабочих дней в запросе <<<- Здесь ровно тот же способ!!!

    http://infostart.ru/public/338386/ Расчет банковских (рабочих) дней (Оригинальный способ)

    http://infostart.ru/public/358354/ Прибавление банковских дней к дате в запросе

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

    Reply
  9. cj512

    (7) karpik666, как вариант можно заполнить производственный календарь по тем праздникам и выходным, по которым есть информация. Как только правительство скажет что куда переносится, то так и сделаем.

    (8) ildarovich, объективно, индексы нужны.

    Reply
  10. pvlunegov

    Просьба указать для каких конфигураций подходит запрос.

    В УТ 10.3 точно подходит, есть регистр нужный.

    Подходил ли для УПП, БП?

    Reply
  11. cj512

    (10) pvlunegov, это пример реализации в расчете банковских дней от какой-то даты в запросе.

    Подходит для УПП и БП (вроде там тоже есть такой регистр), в других конфигурациях, если есть подобный регистр, то можно использовать.

    Reply
  12. gaabora

    А на сколько лет впередназад обычно заполнены регистры, содержащие данные календаря в околотиповых конфигурациях?

    Reply
  13. kapustinag

    Запрос (0) не совсем правильный, кстати. Если документ реализации был в выходной или праздничный день, индекс этого дня (ИндексДатыДолга) отсутствует, и, следовательно, не определяется дата возникновения просроченного долга.

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

    Reply
  14. cj512

    (13) kapustinag, почему бы и нет. Внесу исправление в статью от вашего имени.

    Reply
  15. kapustinag

    (14) После формирования временной таблицы ВТ_РеглКалендарьСИндексами я добавил формирование еще одной временной таблицы;

    ВЫБРАТЬ

    ВТ_РеглКалендарьСИндексами.ДатаКалендаря,

    ЕстьNULL(Таб.ДатаКалендаря,КОНЕЦПЕРИОДА(ВТ_РеглКалендарьСИндексами.ДатаКалендаря,ДЕНЬ)) КАК СледующаяРабочаяДата,

    ВТ_РеглКалендарьСИндексами.ИндексРегламентированногоКалендаря

    ПОМЕСТИТЬ ВТ_РеглКалендарьСИндексами2

    ИЗ ВТ_РеглКалендарьСИндексами КАК ВТ_РеглКалендарьСИндексами

    ЛЕВОЕ СОЕДИНЕНИЕ ВТ_РеглКалендарьСИндексами КАК Таб

    ПО ВТ_РеглКалендарьСИндексами.ИндексРегламентированногоКалендаря = Таб.ИндексРегламентированногоКалендаря-1

    ;

    Таким образом, строки этой временной таблицы содержат, кроме даты строки, дату следующего рабочего дня.

    После этого запрос, формирующий временную таблицу ВТ_СИндексом, меняем вот таким образом:

    ВЫБРАТЬ

    ВТ.Ссылка,

    ВТ.Дата,

    ВТ.ДнейОтсрочкиДолга,

    ВТ_РеглКалендарьСИндексами.ИндексРегламентированногоКалендаря КАК ИндексДатыДолга

    ПОМЕСТИТЬ ВТ_СИндексом

    ИЗ

    ВТ КАК ВТ

    ЛЕВОЕ СОЕДИНЕНИЕ ВТ_РеглКалендарьСИндексами2 КАК ВТ_РеглКалендарьСИндексами

    ПО ВТ.Дата >= ВТ_РеглКалендарьСИндексами.ДатаКалендаря И

    ВТ.Дата < ВТ_РеглКалендарьСИндексами.СледующаяРабочаяДата

    ;

    Всё.

    При формировании поля «следующая рабочая дата» использую конструкцию ЕстьNULL(…) — для того чтобы в последней строке календаря это поле тоже было заполнено.

    Особого падения производительности не заметил. Но и точных замеров не делал.

    Может быть, пойдет и вариант, когда вместо дополнительной временной таблицы получаем дату следующего рабочего дня сразу при формировании ВТ_РеглКалендарьСИндексами — ценой еще одного внутреннего соединения.

    Reply

Leave a Comment

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