Как в ЗУП 3.0 правильно получить тариф/оклад по сотруднику




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

27 Comments

  1. qwinter

    Изобретаете велосипед? Что мешает для этого использовать регистр сведений «Значения совокупных тарифных ставок сотрудников»?

    Reply
  2. monkbest

    (1) qwinter, ответ кроется в названии регистра 🙂 прочитайте его заново по слогам и станет ясно, что это не оклад/тариф. Если у человека часововй тариф, то в него пишется «совокупная тарифная ставка», которая примерно плановый его доход за месяц.

    по крайней мере для релиза 3 0 20 45 я прав

    Еще, я не сам придумал это, эта статья — результат моих раскопок в типовой конфе. Копал через расчетный листок. Т.е. это не мой велосипед, так делают авторы конфигурации, я лишь доношу результат своих раскопок.

    Задача у меня была написать внешний отчет, где рядом с ФИО сотрудника писалась бы его форма оплаты и его тариф/оклад. Мой запрос как раз выдает и имя показателя и его значение.

    Reply
  3. Gauss

    (2) пишете «Найдя плановое начисление…Вы не узнаете его размер»

    Странно, открыв форму списка РС «Плановые начисления» вижу колонку «Размер».

    Он совпадает со значениями в РС «ЗначенияПериодическихПоказателейРасчетаЗарплатыСотрудников»­.

    В каких случаях они могут отличаться?

    Reply
  4. monkbest

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

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

    Reply
  5. Chernika80

    Надо попробовать…

    У меня бОльшая часть отчетов в 2.5 для кадровиков завязана на «окладе», не знала о такой тонкости в 3.0

    спасиб 🙂

    Reply
  6. qwinter

    (2) хоть бы проверили перед ответом. При часовой тарифной ставке она туда и пишется. Изобретатели велосипедов покоряют этот мир(((

    Reply
  7. monkbest

    Давай до свиданья, сам дурак… много чего можно ответить на это. Но давайте разберем все по полочкам:

    1. Суть статьи — рассказать как в 3.0 «правильно» получить тариф/оклад. Правильно — значит так, как считают сами создатели. Есть типовой отчет «Анализ начислений и удержаний» в котором есть вариант «Расчетный листок», его вывод был мной проанализирован из тысяч строк кода выдернуто необходимое. Если со временем 1С переделает отчет на другие данные — ради бога, будет новая статья, или не будет.

    2. «Изобретатели велосипедов » — говорите Вы — Вы троль. Эта фраза применима к тому, что уже кто-то сделал, а я заново повторяю. Кто-то раскопал этот алгоритм до меня и донес до публики? Или Вы видели документацию от 1С по ЗУП 3.0 живьем? Вы просто увидели, что похожую информацию можно вытащить из другого регистра и начали кидаться какашками.

    3. Вам эта информация не интересна — Давай до свиданья, читай другое

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

    5. Я не знаю алгоритма заполнения регистра сведений «значения совокупных тарифных ставок», в моей базе он заполняется так, как я Вам написал. Раз его заполнение видимо зависит от настроек, то это не есть надежный источник данных, у него видимо, другое назначение

    6. В регистре «значения совокупных тарифных ставок» нет показателя, нет начисления, только вид совокупной тарифной ставки — месячная / часовая /… но имени показателя и начисления там нет.

    Reply
  8. qwinter

    (7) такое заполнение этого регистра является ошибкой настройки, а точнее установкой у показателя «Час. тариф» признака, что он является месячной тарифной ставкой. При данной настройке кстати у Вас будут неверно считаться ночные и праздничные часы.

    Reply
  9. monkbest

    (8) qwinter, Вы совершенно правы. Этот элемент справочника предопределенный и его править нельзя, но по чьей-то воле в нем галка «является тарифной ставкой» не стоит. Виню в этом кривой перенос из 7.7, ведь он выполнял первоначальное заполнение справочников. Перенос выполнялся на ранних релизах ЗУП 3.0, в конце 2013ого года, косяков в тех релизах было мама не горюй. Исправлять это сейчас пока смысла нет, т.к. формулы ночных, праздничных, простоев, тех.осмотров,… написаны так, что они работают верно, при кривом показателе «стоимость часа».»стоимость часа» — как Вы и говорите действительно считается криво. Но исправление показателя «Час. тариф» само по себе не приведет к верному расчету, потребуется перепроводить кучу документов за весь период. Есть шанс сделать хуже.

    Но это все не относится к теме статьи 🙂 см. п.1 и п.6 моего предыдущего поста

    Давайте накатаем статью про тарифные ставки, как они считаются, как за ними следить, на что они влияют и какие грабли тут есть. ЗУП 3.0 вообще пока непаханое поле и тут можно про каждый регистр целую статью писать. Документации от 1С пока толком нет, книга на ИТС про ЗУП 3.0 не содержит полезной информации, в ней нет описания внутренних механизмов и причинно-следственных связей между реквизитами, там фразы в стиле «в поле сотрудник выберите сотрудника», «у обособленного подразделения поставьте галку обособленное подразделение».

    Reply
  10. RuslanKhanow

    (9) А как эти данные сейчас выводить,например мне нужно получить размер оклада сотрудник?

    Простите за ,возможно, глупый вопрос,я только начал учиться

    Reply
  11. monkbest

    (10) RuslanKhanow, уберите в моем запросе условие по сотруднику и получите всех

    Reply
  12. m.s.moiseev

    Только сегодня этим занимался, жаль не увидел вашу статью. При проведении документа ПриемНаРаботу в случае если мы попытались добавить 2 вида оклада, то происходит проверка:

    Начисления.ОбозначениеВТабелеУчетаРабочегоВремени <> ЗНАЧЕНИЕ(Справочник.ВидыИспользованияРабочегоВремени.ПустаяСсылка)

    И Начисления.ЗачетНормыВремени = ИСТИНА

    Если результат отсеивания больше 1, то возвращает ошибку.

    Вот текст запроса полностью

    ВЫБРАТЬ

    ПлановыеНачисленияСрезПоследних.Сотрудник,

    ПлановыеНачисленияСрезПоследних.Начисление,

    ПлановыеНачисленияСрезПоследних.Размер,

    ПлановыеНачисленияСрезПоследних.Начисление.ОбозначениеВТабелеУчетаРабочегоВремени КАК ОбозначениеВТабелеУчетаРабочегоВремени,

    ПлановыеНачисленияСрезПоследних.Начисление.ЗачетНормыВремени КАК ЗачетНормыВремени

    ПОМЕСТИТЬ ВТНачисленияВсеБезОтбора

    ИЗ

    РегистрСведений.ПлановыеНачисления.СрезПоследних(

    &КонецПериода {(&КонецПериода)},

    Сотрудник В

    (ВЫБРАТЬ

    ВТСотрудники.Сотрудник

    ИЗ

    ВТСотрудники КАК ВТСотрудники)) КАК ПлановыеНачисленияСрезПоследних

    ГДЕ

    ПлановыеНачисленияСрезПоследних.Используется = ИСТИНА

    ;

    ////////////////////////////////////////////////////////////­////////////////////

    ВЫБРАТЬ

    Начисления.Сотрудник КАК Сотрудник,

    Начисления.Начисление КАК Начисление,

    Начисления.Размер КАК Размер

    ИЗ

    ВТНачисленияВсеБезОтбора КАК Начисления

    ГДЕ

    Начисления.ОбозначениеВТабелеУчетаРабочегоВремени <> ЗНАЧЕНИЕ(Справочник.ВидыИспользованияРабочегоВремени.ПустаяСсылка)

    И Начисления.ЗачетНормыВремени = ИСТИНА

    Reply
  13. Lapitskiy

    круто

    Reply
  14. chmv

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

    Reply
  15. dock

    обожаю 3.0

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

    и, барабанная дробь, приведенный запрос нам не выдает ни одного вида начисления! (на дату После окончания действия данного временного начисления)

    в чем суть: в запросе используется СрезПоследних, если была введена запись с периодом действия, то срезом последних именно она и будет выведена.

    а теперь у нас есть условие:

    ПлановыеНачисленияСрезПоследних.ДействуетДо = ДАТАВРЕМЯ(1, 1, 1)

    ИЛИ ПлановыеНачисленияСрезПоследних.ДействуетДо > КОНЕЦПЕРИОДА(&Период, ДЕНЬ)

    И этим условием мы откидываем наше начисление…

    Итог — по данным запроса назначенных видов начисления нет…

    Reply
  16. monkbest

    (15) dock, а ЗначениеПоОкончании не будет заполнено?

    Reply
  17. AliceLight

    Хм, у меня такой способ вернул «Ежемесячную премию» (потому что в начальной штатной расстановке она стояла первее, чем оклад). Ручки пользователя и порядок, в котором пользователь расставит начисления, предугадать сложно. Выкрутилась так: получила данные оклада из регистра сведений «ТекущаяТарифнаяСтавкаСотрудников» — именно оттуда берутся данные при открытии формы элемента Сотрудник для поля «Оклад (тариф)». Напрягает только мысль, возможно ли, что там будут две записи по сотруднику…

    Reply
  18. dock

    (16) так, с периодами правильно берет, с этим каюсь, не досмотрел.

    но остается проблема в том, что отбирать «основное» начисление по полю «порядок» — однозначно неверно. В ЗУП 3 прекрасно работает, если «основное» начисление добавлено и в самый конец списка 🙂

    вот тут нужно копать чуть чуть глубже — делать связку с настройкой «»Показатели, определяющие состав совокупной тарифной ставки».

    в принципе, если нужно просто получить в коде начисления, можно воспользоваться следующими функциями:

    КадровыеДанные = КадровыйУчет.КадровыеДанныеСотрудников(Истина, Объект.Сотрудник, «ТекущаяОрганизация,ФизическоеЛицо,ДатаПриема,ДатаУвольнения,ВидЗанятости,Состояние,Должность,Подразделение, Территория, ОплатаТрудаПериодЗаписи, ОплатаТрудаРегистратор, ТарифнаяСтавка, ФОТ, Надбавка,ОсновноеНачисление, ПоказательТарифнойСтавки, СовокупнаяТарифнаяСтавкаПериодРегистрации, СовокупнаяТарифнаяСтавкаРегистратор, СовокупнаяТарифнаяСтавка, ВидСовокупнойТарифнойСтавки, ГрафикРаботыПериодРегистрации, ГрафикРаботыРегистратор, ГрафикРаботы», Объект.Дата);

    //получаем данные о начислениях

    ДанныеНачисленийСотрудников =ЗарплатаКадрыРасширенный.ДействующиеНачисленияСотрудников(СотрудникиДаты, Неопределено, «Начисления»,»Начисление», Истина);

    Полное описание параметров смотрим в коде — там на целую отдельную статью хватит

    Reply
  19. dock

    Для тех, кто уже обновился: в 3.1 — этот метод уже не сработает.

    Почему ? Изменилось содержание: ресурс «ИспользуетсяПоОкончании» теперь не используется…

    Reply
  20. monkbest

    (19) так точно, жизнь идет своим чередом и старые методы уже не работают. Да и в типовые ПФ тариф оклад берется уже совсем другим способом.

    Появилось много регистров сведений из которых можно почерпнуть данную информацию, но из какого правильнее не понятно, для общего сведенья общий принцип изменений таков:

    для каждого регистра с датой окончания появился новый, не подчиненный регистратору с таким же названием но в конце со словом «Периодами»

    в этих регистрах есть дата С и дата ПО, из таких регистров и надо брать теперь всю информацию о должностях, стажах и пр.

    В этих регистрах в случае даты окончания создается по две записи

    Вот нюансы:

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

    либо передавайте параметр сразу конец либо пишите КОНЕЦПЕРИОДА(&Период, ДЕНЬ)>Рег.ДатыС И КОНЕЦПЕРИОДА(&Период, ДЕНЬ)<Рег.ДатыПо

    2. Последняя запись дату ПО имеет не пустую, а большой нереальный год. Это значит, что мы смело можем писать &Период<Рег.ДатаПо

    не надо проверять на заполненость дату окончания это ПЛЮС 1Су

    3. Они не подчинены регистратору, это потенциальная опасность, что данные расползутся из-за непредвиденных манипуляций (обмены, аварийные завершения, кривой код обработок). Если видите косяк постарайтесь инициализировать их содержимое. Наблюдения показывают, что перепроведение документов не помогают, там у них сложные условия на измененность исходных данных.

    Reply
  21. YanTsys

    Штатные ресурсы регистра ПлановыеНачисления переименованы в

    УдалитьИспользуетсяПоОкончании

    УдалитьРазмерПоОкончании

    более актуального решения нет?

    Reply
  22. monkbest

    (21) в 3.1 все по другому

    есть, но не в этой статье 🙂

    Reply
  23. YanTsys

    (22) пока пробую вариант

     ТЗС = КадровыйУчет.КадровыеДанныеСотрудников(Истина, МассивСотрудников, «ТарифнаяСтавка,ОсновноеНачисление», ДатаРасчета);
    

    Эта функция много чего может возвращать, жаль нет нормального описания 😉 …

    Reply
  24. dock

    (23) внимательнее читай комментарии 🙂

    // Список полей, допустимых в параметре КадровыеДанные см. КадровыйУчетВнутренний.ЗапросВТКадровыеДанныеСотрудников.

    любят в типовых немного поглубже закопать…

    и да, в последних версиях 3.1 стало попроще — достаточно просто среза последних по регистру «ПлановыеНачисления» : если устанавливаются плановые начисления без окончания даты окончания, то производится одна запись (для одного вида начисления). Если имеется дата окончания, то две записи.

    Reply
  25. monkbest

    (24) нет нет нет!!!

    в срезах последних регистров подчиненных регистратору мы теряем информацию по окончанию. В них хранится только одна запись!

    нужно искать регистр сведений не подчиненный регистратору с таким же названием и постфиксом «Периодами»

    в нем есть дата начала и кончания.

    В нем берем условие на КонецПериода(&Период, День) Между Регистр.ДатаНачала И регситр.ДатаКонца

    Обязательно конец дня! Т.к. в теории в один день имеем право делать несколько приказов. Например в кадровом переводе сказали оклад 10000 рублей, и тем же днем изменеием начислений делаем оклад 12000. В периодах увидем обе записи и приоритетная будет в конце дня.

    Reply
  26. Simas79

    Для 3.1 уже не работает запрос

    Reply
  27. monkbest

    (26) ну таки да 🙂 там же структура данных совсем другая. В заголовке написано «3.0»

    Reply

Leave a Comment

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