Неоплаченные долги при распределении оплаты по правилу ФИФО одним запросом и намного быстрее, чем Вы думали




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

99 Comments

  1. speshuric

    (0) 1. А это правильно, что в первом запросе «Остатки(&Дата, …», а во втором «ВзаиморасчетыСКонтрагентами.Обороты( , &Дата, «? Что будет, если будут обороты в момент &Дата?

    2. Зачем во втором запросе «.Обороты» да еще и с периодичностью «Регистратор»? Не проще было прямо из движений их сформировать? Просто если посмотреть SQL-запрос, который при этим возникает, то он способен сбить с толку оптимизатор SQL.

    Reply
  2. ildarovich

    (1) Спасибо за вопросы и замечания. Действительно, отчет совсем свежий и неточности еще могут быть. Уверен, определенный «тюнинг» запросу еще нужен.

    1) Вопрос в голове был. Я действительно не проверил. Показалось, что конечный остаток с параметром 0:00:00 учитывает обороты в момент 0:00:00, а брал просто остаток. Проверю — исправлю (уже исправил! — взял остаток на следующую секунду — так кажется логичнее, если ищем отгрузки).

    2) Думал про движения, но мне нужно было простым способом отобрать именно «отгрузки». Я решил, что считать отгрузкой удобнее всего факт, когда по документу в целом (регистратору) будет «плюс» долга. То есть расчет на случай, когда в документе несколько разнонаправленных движений по регистру. Возможно, в целевой конфигурации такого в этом регистре никогда не бывает, тогда действительно можно взять движения.

    Reply
  3. ildarovich

    Добавил отчет «Просроченные долги». Изменился только последний запрос — потребовалось ввести несколько дополнительных полей. Допустимое число дней задолженности хранится в соответствующем реквизите договора контрагента независимо от установленной галочки «Контролировать число дней задолженности». Реквизит оказывается в отчете и меняется прямо из отчета как показано на скриншоте.

    Reply
  4. sapit
    Reply
  5. sapit

    Да, предварительно делайте выборку по договору/складу (отбирая с остатком <> 0) и фильтруйте по этим данным, иначе в обработку попадут лишние данные, а в целом работает очень шустро

    Reply
  6. ildarovich

    (4) Насколько я понял, Вы привели вариант реализация общеизвестного метода. Его основная отличительная особенность — соединение таблицы оборотов самой с собой по условию

    ТаблицаПоследующихПриходов.Регистратор.МоментВремени >= ОсновнаяТаблицаРегистра.Регистратор.МоментВремени

    Такое соединение называется «тэта-соединением». Оно приводит к тому, что в результате в выборке до группировки оказывается N#k8SjZc9Dxk2/2 строчек, где N — число документов по одному договору в рассматриваемом периоде. Это значит, что если у Вас 100 документов отгрузки, то потребуется просуммировать 5000 строчек, а если 1000 документов отгрузки, то 500000. Эта нелинейная квадратичная зависимость и невозможность ограничить интервал снизу приводит к тому, что со временем производительность отчета быстро деградирует. Это реальная проблема. В больших торговых организациях отчет может работать десятки минут и часы. Возможно, в Вашей организации нет контрагентов с большим количеством отгрузок по одному договору и эта проблема не чувствуется.

    Мне долгое время казалось, что для реляционных СУБД — это неизбежность и сделать ничего нельзя, а при желании ускорить отчет нужно считать нарастающий итог в коде или в СКД или сворачивать базу. Относительно недавно удалось преодолеть квадратичную зависимость и снизить ее до линейной (метод «Баттерфляй»). Подобный подход применен и здесь: повторением большого количества очень простых запросов, не требующих самосоединения оборотов, удалось создать метод, время работы которого не квадратично, а ЛИНЕЙНО зависит от числа отгрузок по одному договору. Это актуально для больших организаций — не нужно ничего «сворачивать», чтобы отчеты работали с нормальной скоростью.

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

    Reply
  7. wolfsoft

    Достойная публикация.

    Reply
  8. speshuric

    (6)»Мне долгое время казалось, что для реляционных СУБД — это неизбежность и сделать ничего нельзя». С полноценным, необрезанным TSQL и CTE — что нарастающий итог, что «глубина долга» решаются достаточно хорошо — быстро работает, лаконично, понятно.

    А вот, кстати, LIFO — очень и очень непросто. Лет 7 назад я написал LIFO без курсоров на чистом TSQL, но после первой возникшей необходимости исправления, переписал на курсоры, чтобы можно было кому-нибудь объяснить, как это работает без бочки коньяка. Курсором, кстати, работает медленно, но линейно от количества записей.

    Reply
  9. ildarovich

    (8) Есть сомнения, что оконные функции входят в стандарт T-SQL. Думаю, что когда стандарт расширяется, у каждого нововведения есть и сторонники и противники. А то, что эти функции реализованы в нескольких коммерческих СУБД и востребованы может быть еще недостаточно, если они противоречат каким-либо базовым принципам.

    Говоря о реляционных СУБД я имел ввиду не какую-то конкретную СУБД, а принципы реляционной алгебры. Так вот, с этой точки зрения не то, чтобы оконные функции, а даже конструкция ORDER BY не является частью реляционной алгебры.

    На стр.194 в строке 26 книги «SQL и реляционная алгебра» читаем

    Пожалуйста, не поймите меня неправильно, я вовсе не хочу сказать, что конструкция ORDER BY бесполезна; однако я утверждаю, что в реляционном выражении для нее нет места…

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

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

    Reply
  10. speshuric

    (9)

    Если говорить про стандарты, то CTE вошли в SQL-99, оконные функции вошли в SQL-2003. В MS SQL начали появляться в 2005-м и 2008-м, но с некоторыми ограничениями. LAG и LEAD, которые есть в оракле и в MSSQL2012 кажется пока еще не в стандарте (а мощные функции, кстати).

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

    • SQL далёк-далёк от реляционной модели.
    • Реляционная модель отделена от реализации. Нельзя говорить о времени выполнения того или иного реляционного выражения.
    • Реляционная модель, к сожалению, подвисла. Она прекрасна, но в ней много белых пятен, а пятна эти не торопятся заполняться. К сожалению Дейт и Дарвен не вытянули альтернативную реализацию РСУБД (не SQL). Я могу попробовать сделать небольшой обзор на эту тему, но это явно не влезет в коммент.

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

    Reply
  11. lar_nm

    Все классно работает и быстро! Благодарю и уважаю.

    Reply
  12. ildarovich

    (10) Спасибо за информацию про стандарты и функции. Возьму на заметку.

    1) 2) 3) — согласен со всем этим.

    Да, эффективные инструменты есть. Решая конкретную задачу, не стал бы отметать доступные инструменты, просто порадовался, что все «летает» и пошел бы дальше. Например, в статье «Опять двойка» в случае с использованием ЗаписьXML (Ваша находка) для быстрой конкатенации строк я явно попал впросак, просто не найдя этой возможности. Но тут пока оконных функций в запросах не появилось. И не думаю, что это в приоритетах у 1С. И может подойти данный метод.

    Что же касается теории — можно было бы попытаться копнуть глубже, но кто это сейчас оценит? Тем более, кажется, что такого рода приемы должны были (не стал проверять) в свое время запатентованы как схемотехнические решения для построения быстродействующих АЦП, сумматоров и прочее (В IEEE Transactions нужно покопаться). То есть теоретическим изысканиям должны предшествовать обоснования новизны, а я в ней не уверен.

    Reply
  13. Angry

    Интересны способ.

    Но если отойти от чистой теории и спустится к тому что имеем на практике, т.е. 1С.

    Надо вспомнить что для оптимизации 1С хранит итоги за месяц. Не будет ли эффективней механизм если будет опираться на эти итоги?

    Reply
  14. ildarovich

    (13) Вообще Вы правы. Но ускорение, которого можно добиться таким образом, не столь значительно, как может показаться на первый взгляд. Дело в том, что всего через примерно девять (меньше трети) делений, метод выходит на уровень, на котором интервал неопределенности меньше месяца, где итоги уже не работают. Оптимизировать можно первые девять делений. Я не стал усложнять запрос ради относительно небольшого выигрыша в 30 %. Причем этот максимальный выигрыш будет достигаться только когда месяцев существенно меньше чем документов.

    Но это вполне можно сделать. Нужно только добавить таблицу еще и месячных оборотов и первые деления делать по ней. В том случае, когда мы начинаем не с 17 лет, а, например, с трех лет (когда долги, по идее списываются), выигрыш будет только 20%.



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

    Reply
  15. Pawlick

    Очень интересное решение, очень.

    Также нужно изучать и чаще вспоминать математику

    Золотые слова, ildarovich, золотые слова…

    Reply
  16. ejik2012

    Подскажите, Шаг134217728 будет выглядеть так?

    ВЫБРАТЬ
    Шаг.Договор,
    Шаг.Край,
    Шаг.Долг,
    ЕСТЬNULL(СУММА(Обороты.СуммаВзаиморасчетовОборот), 0) КАК ПолуСумма,
    ВЫБОР
    КОГДА Шаг.Долг > ЕСТЬNULL(СУММА(Обороты.СуммаВзаиморасчетовОборот), 0)
    ТОГДА -1
    ИНАЧЕ 0
    КОНЕЦ КАК Сдвиг
    ПОМЕСТИТЬ Шаг134217728
    ИЗ
    (ВЫБРАТЬ
    Шаг.Договор КАК Договор,
    Шаг.Долг + Шаг.Сдвиг * Шаг.ПолуСумма КАК Долг,
    ДОБАВИТЬКДАТЕ(Шаг.Край, СЕКУНДА, 268435456 * (Шаг.Сдвиг — 0.5) + 1) КАК Центр,
    ДОБАВИТЬКДАТЕ(Шаг.Край, СЕКУНДА, 268435456 * Шаг.Сдвиг) КАК Край
    ИЗ
    Шаг268435456 КАК Шаг) КАК Шаг
    ЛЕВОЕ СОЕДИНЕНИЕ Обороты КАК Обороты
    ПО Шаг.Договор = Обороты.Договор
    И (Обороты.Период МЕЖДУ Шаг.Центр И Шаг.Край)
    
    СГРУППИРОВАТЬ ПО
    Шаг.Договор,
    Шаг.Край,
    Шаг.Долг
    

    Показать

    Reply
  17. ildarovich

    (16) Да, в точности так!

    Reply
  18. EarlyBird

    Коллеги, ваш интеллект потрясает, респект.

    Я вообще ничего не понял из вашей дискуссии )

    Reply
  19. relanium86

    На базе УПП объемом порядка 80Гб отчет выполняется по 60 счету 32сек. а по накоплению итогов у меня шуршало минут 10 в среднем.

    Reply
  20. mxm2

    Как-будто еще одна особенность отчета есть: если в момент возникновения долга была переплата (аванс), то сумма просроченного долга считается неверно (в нее добавляется сумма аванса).

    Reply
  21. ildarovich

    (20) У меня эта ситуация не воспроизводится. Но, возможно, такая ошибка была в самой первой версии размещенного отчета, которая была сразу же исправлена. Сейчас везде проверяется, что по регистратору

    Обороты.СуммаВзаиморасчетовОборот > 0

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

    Reply
  22. distorshion

    А есть версия кредиторской задолженности?

    Reply
  23. ildarovich

    (22) distorshion, если разговор о УТ 10.3, УПП 1.3 или КА, то сделать ничего не стоит — нужно поменять знаки в трех местах. Например,

    Остатки.СуммаВзаиморасчетовОстаток КАК Долг

    поменять на

    -Остатки.СуммаВзаиморасчетовОстаток КАК Долг

    И в таком же количестве мест перевернуть условия. Например,

    Остатки.СуммаВзаиморасчетовОстаток > 0

    поменять на

    Остатки.СуммаВзаиморасчетовОстаток < 0

    Ну и протестировать, конечно.

    Reply
  24. distorshion

    Спасибо!!!!! Мегареспект тебе добрый человек.

    Reply
  25. Rustig

    (0) как много я пропустил ваших статей! (январь, февраль 2014)

    плюс за идею

    плюс за реализацию

    круто

    Reply
  26. Pawlick

    Все таки по моему отчет имеет одну особенность.

    У меня эта ситуация не воспроизводится.

    Я провел рад экспериментов, и вот что выяснил:

    Завожу нового контрагента (для чистоты экперимента).

    Далее:

    Реализация — ПКО — Возврат товаров — РКО

    , где СуммаРеализации > СуммыПКО И СуммаВозврата < СуммыПКО И СуммаВозврата > СуммыРКО

    В отчет по какой то причине не попадает сумма РКО (сумма возврата ДС)

    Сначала я решил, что просто не учитываются суммы возвратов денежных средств покупателям (возможно потому, что «Обороты.Регистратор ССЫЛКА Документ.РеализацияТоваровУслуг»)

    Но потом ввел еще один такой же комплект документов:

    Реализация_2 — ПКО_2 — ВозвратТоваров_2 — РКО_2,

    и в отчете не учитывается уже сумма РКО_2, а сумма РКО — учитывается !

    Тогда я решил, что это происходит тогда, когда последним регистратором ВзаиморасчетовСКонтрагентами по договору идет РКО с видом операции «Возврат ДС покупателю»: документ, который делает запись с видом движения «Расход» и отрицательным знаком. Но покопавшись глубже выяснил вот что:

    1. Снимаю проведение со «второго комплекта» документов (проведенными остаются только Реализация-ПКО-ВозвратТоваров-РКО). Сумма РКО в отчет не попадает;

    2. Провожу Реализацию_2. Сумма РКО в отчет не попадает;

    3. Провожу ПКО_2, и… ОП! Сумма РКО попала в отчет!

    4. Провожу ВозвратТоваров_2: сумма РКО в отчете.

    5. Провожу РКО_2: сумма РКО по прежнему в отчете, а вот суммы РКО_2 — нет!

    PS. Если кинете в личку адрес почты скину Вам УТ10 в которой смоделирована ситуация.

    Reply
  27. Pawlick

    В(26) Pawlick,

    Все таки по моему отчет имеет одну особенность.

    ildarovich, все, вопрос закрыт. Ошибка не подтвердилась.

    Простите бога ради за некорректную информацию и беспокойство.

    Reply
  28. ildarovich

    (27) Pawlick, да не за что. — Я как раз за максимально критичное и недоверчивое отношение. Дополнительная проверка лишней никогда не бывает.

    Reply
  29. undo

    Я никогда не смотрел на решение таких задач со стороны математики, и поэтому офигел от написанного.

    Reply
  30. Denium79

    Автору огромное спасибо за отчет! Переписал его для складских остатков в своем УПП, получилась просто бомба! Потрясающее решение! Отчет по дебиторке хотелось бы для БП 2.0, может быть кто нибудь перепишет?

    Reply
  31. sam0511

    В обсуждении не заметил, но..

    Неправильно отображается сумма долга по документам в следующей ситуации: есть предоплата, последующая отгрузка проведена двумя документами в одно время (бывает и такое…). Сумма долга распределяется на два этих документа, хотя, по идее должен отображаться одни. А в остальном супер.

    Reply
  32. ildarovich

    (31) sam0511, как говорится, «это не баг — это фича». — Так и было задумано и я готов настаивать на том, что такое решение правильнее, чем отнесение оплаты на один из выбираемых случайно (на основе GUID) документов-односекундников. Потому, что пользователь не влияет на выбор оплачиваемого документа, а время у них одинаковое. Получается, что отчет при этом (при общепринятом подходе) ведет себя непредсказуемо, а этого быть не должно. Захотят поправить — передвинут нужный документ на секунду вперед.В статье об этом написано (где про ложку сахара).

    Reply
  33. ZLENKO

    Скажите пожалуйста могу ли в вашем варианте получить в отчете какой документ каким был оплачен ?

    Т.е. чтобы увидеть с какой реальной отсрочкой платил клиент ?

    Reply
  34. ZLENKO

    (32) » «это не баг — это фича». — Так и было задумано и я готов настаивать на том, что такое решение правильнее, чем отнесение оплаты на один из выбираемых случайно (на основе GUID) документов-односекундников.»

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

    Reply
  35. ildarovich

    (33) ZLENKO.PRO, нет, к сожалению.

    Это немного другая и чуть более сложная по математике задача. Связывание оплаты с отгрузками (или приходов товара с расходами) по ФИФО запросом. В принципе, в статье «Баттерфляй — метод быстрого расчета нарастающего итога в запросе» показано, что первую часть этой задачи тоже можно решить за время, пропорциональное числу документов. Сейчас, кстати, у меня есть более легкая версия этого запроса. А также у меня есть решение второй половины этой задачи (связывание по ФИФО запросом). То есть быстрое решение запросом задачи 7 из статьи Минимализмы. Но пока массового интереса к этой теме не видно, поэтому с публикацией не тороплюсь.

    Reply
  36. ZLENKO

    (35) «Но пока массового интереса к этой теме не видно, поэтому с публикацией не тороплюсь.»

    Массовый интерес может вызвать разве что какая нить доработка документа Счет-фактура 🙂

    Кстати в http://infostart.ru/public/306536/ задача 7 жаль что два года назад не попалось решение.

    Сломал мозг пока написал распределение оплат и отгрузок в цикле 🙁

    Получались проблемы с нераспределенными «хвостами»..

    Reply
  37. killovolt

    (0) спасибо за идею. На основе отчета реализовал поиск не зачтенных авансов. Т.е. мне надо было по ФИФО найти документ оплаты с которого возникает аванс.

    Reply
  38. sea123

    Работает быстро. НО … при различных отборах разные суммы по одному и тому же контрагенту. По документу Корректировка долга.

    Reply
  39. ildarovich

    (38) sea123, если подробнее опишете ситуацию, чтобы можно было воспроизвести, то буду разбираться. — Что за отборы? Возможно, отборами вы сами исключаете документы, которые должны влиять на остаток. Вот он и меняется. Попробуйте какой-либо другой (медленный) отчет, делающий то же самое. Не будет ли такого же эффекта и там?

    Reply
  40. CheBurator

    Автор а не пробовали разбиение делать не фифти фифти а дветрети к одной трети и одна треь ближе к коцу периода

    Такой вариант по идее должен существенно увеличить скорость

    Reply
  41. ildarovich

    (40) CheBurator, нет, это не так. Метод золотого сечения и метод дихотомии довольно часто путают, но это разные методы — у каждого свои условия применения. Метод золотого сечения используется при поиске минимума (максимума) унимодальной функции. Один замер там не позволяет решить, где дальше искать — нужно обязательно два замера (располагающихся по золотому сечению), чтобы сравнить значения в двух точках и выбрать новый, меньший интервал неопределенности.

    В нашем случае для определения нового интервала неопределенности достаточно одного замера — поэтому и используется дихотомия.

    — Но, с другой стороны, зная закон распределения вероятностей числа дней просрочки, можно делить интервал в соответствии с ним, что действительно будет давать выигрыш! Такой вариант в голове мелькал, но потом как-то забылся на фоне того, что пришлось долго возиться с запросом (его начальный вариант вешал СКД).

    Пожалуй, стоит реализовать не золотое, а «вероятностное» сечение. Спасибо, что помогли вспомнить про эту идею!

    Reply
  42. Glemar

    Спасибо за замечательный отчет!!!

    Одновременно возникла проблема выбора, поэтому прошу совета.

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

    При этом срабатывает контроль взаиморасчетов при проведении накладных. Его конечно можно преодолеть если разрешить «Разрешить проведение…» в Дополнительных правах пользователя. Но тогда фактически реального контроля нет тк он а) необъективен, ибо опирается на несуществующие просрочки, и б) преодолевается пользователем

    Два дальнейших пути (оба не нравятся):

    1. Отключить «ведение взаиморасчетов по документам» в договорах. Но этого сделать система не даст, тк есть документы сформированные «под галочкой». (Хотя и можно завести новые договора)

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

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

    Reply
  43. ildarovich

    (42) Glemar, хочу уточнить, что данный отчет работает НЕЗАВИСИМО от состояния реквизита «ведение взаиморасчетов по документам». В основном расчет на ситуацию, когда эта галочка не проставлена, то есть учет взаиморасчетов по документам не ведется. При этом есть две проблемы:

    1) при снятии этой галочки пропадает видимость реквизита «число дней просрочки»;

    2) также не возможно запретить проведение очередной реализации при просрочке долга.

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

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

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

    Reply
  44. Glemar

    (44)

    Спасибо!

    2) — Действительно,доступ к полю «Допустимое число дней задолженности» творит чудеса. Реквизит сохраняется!

    К сожалению у нас имеются проблемы к корректным подбором Реализаций в документах оплаты, тк отгрузка шла и идет по нескольким договорам, имеются возвратные накладные, клиенты могут сделать проплату на сумму, превышающую задолженность… Будем думать над обработкой, которая бы периодически восстанавливала корректность соотнесения оплат с отгрузками.

    Либо пойдем по пути, предложенному Вами в решении «второй проблемы». Попробуем даже сделать проверку на этапе Сохранения реализации (до Проведения), чтобы Менеджер занялся просрочкой до передачи заявки на склад, а не в последний момент.

    Reply
  45. MakhoninMY

    Скажите пожалуйста можно ли на основе вашего решения рассчитать пени по просроченной задолженности без запроса в цикле по дням? Фактически нужно получить график просроченной задолженности по дням.

    Reply
  46. ildarovich

    (46) MakhoninMY, если я правильно понял вопрос, то вы хотите то же, что и в (33). Тогда тот же ответ — нет, приведенное здесь решение только для просроченных долгов на одну конкретную дату.

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

    Reply
  47. MakhoninMY

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

    Reply
  48. Glemar

    ildarovich,

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

    Или можно где-то настроить список документов, пронимаемых во внимание расчета??

    Reply
  49. ildarovich

    (49) Glemar, тип документов никак не ограничивается — это все документы, делающие движения по регистру ВзаиморасчетыСКонтрагентами,

    ГДЕ
    Обороты.СуммаВзаиморасчетовОборот > 0

    Фактически сопоставления оплат с отгрузками не происходит. На сумму долга набираются последние отгрузки.

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

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

    1) список движений регистра ВзаиморасчетыСКонтрагентами по конкретному договору;

    2) выдача моего отчета;

    3) выдача «правильного» с Вашей точки зрения отчета или мнение «что должно быть».

    Reply
  50. Vodoley

    1. всякий раз говорю «круто» — и собираюсь проработать и остальные статьи автора )) (приятный научный глубокий подход и остроумный стильный текст.)

    2. Собственно сейчас сам мучаюсь с тем как раскидать документы.. по другим документам. (у меня сейчас не типичная задача позакрывать расходы приходами по ФИФО — но очень близкая). Есть ли возможность в 2х словах направить куда смотреть..? (пардон за наглость)

    (У меня пока и тета-соединением не прорисовывается запрос, а благодаря Вашим статьям есть ощущение, что запросом разом можно сделать почти все 🙂 и эффективно). Хотя у меня немного нетипичная проблема — под отгрузки подложить наипозднейшие но предшествующие отгрузкам поступления.. Т.е. понятие типа «аванса» нет — отгрузить виртуально то, что получим позже нельзя :). И вот тут пока не могу уловить идею как конструировать соединения, которые в итоге приведут к «закрыванию» отгрузок более ранними поступлениями. (это похоже на дебиторку — только разворачиваем ось времени в прошлое и стартуя с отгрузок и считая их «приходами», закрываем их поступлениями, полагая их расходами).

    Reply
  51. ildarovich

    (51) Vodoley, если вы имеете ввиду партионный учет, то на Инфостарте было много статей на эту тему. Достаточно набрать в поиске ФИФО (FIFO). В тех статьях были примеры запросов и описание того, как они построены. Речь об оптимальности не шла, но, возможно, на ваших данных этого не потребуется.

    Вообще эта задача (связь приходов с расходами) решается примерно так: все приходы по порядку складываются в столбик (как кубики). Величина секции столбика, соответствующего конкретному приходу, берется равной величине этого прихода. Все расходы также складываются в столбик рядом. Затем приходы с расходами связываются горизонтальными линиями. В комментарии http://forum.infostart.ru/forum24/topic32459/message361861/#message361861 приведен пример подобной схемы.

    Если речь идет не о запросе, то функция, получающая таблицу связей приходов с расходами приведена в статье Минимализмы в разделе 7.

    Reply
  52. sea123

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

    Reply
  53. ildarovich

    (53) sea123, хотелось бы больше информации об обстоятельствах появления расхождений.

    Были ли у проблемного контрагента взаимозачеты между договорами, с другими контрагентами, смотрелись ли остатки в максимальном разрезе договоров или только целиком контрагента, были ли корректировки регистра по нему, с «правильным» знаком ли они делались, были ли корректировки реализации.

    Вообще гибкость настроек, обеспечиваемая СКД может иногда сослужить плохую службу: можно попробовать в любом типовом отчете поставить регистратор не на последний (нижний) уровень группировки и попробовать понять, что выдает отчет. Возможно, возникает подобная ситуация, но, чтобы разобраться, нужны подробности.

    Хотя бы скриншот с разворотом движений по регистратору регистра взаиморасчетов по проблемному контрагенту, скриншот результата моего отчета по нему и скриншот результата «правильного» отчета. Буду очень благодарен за эти данные.

    Reply
  54. Prometeus2011

    Очень бойко можно применить сей метод для построения отчета задолженности по периодам прострочки. Есть такой штатный отчет в десятой УТ «Дебиторская задолженность по периодам». Последний пакет запроса только изменить надо.

    Reply
  55. Prometeus2011

    Сергей, невероятно. Я вот посмотрел Ваши статьи… Мало таких людей я в жизни встречал (одного, кроме вас, если быть точно). Ну круг общения у меня, конечно, не научная элита, но все равно, чувствуется незаурядный склад ума. Это не лесть. Действительно хотелось выразить признание. А сколько времени на проработку материала ушло у Вас? Ну, в частности этого.

    Reply
  56. ildarovich

    (56) Prometeus2011, спасибо, надеюсь на то, что материалы публикаций будут полезными не только в теории, но и на практике. Собственно говоря, работа с 1С мне и нравится, в основном, своей близостью к практике.

    Чистого времени ушло не так уж много: неделя-две. А так, наверное, месяц-полтора заняло. Если тема интересная, то работа идет быстро. Задержка там была в том, что первая версия запроса хорошо работала в консоли, но «вешала» СКД (из-за ошибки в платформе). В итоге запрос получился еще проще.

    Reply
  57. Prometeus2011

    Спасибо.

    Reply
  58. Enya_06

    Спасибо! Алгоритм очень помог при реализации похожей задачи!

    Преклоняюсь перед вами, гении!

    Reply
  59. sapsan322

    Спасибо. Очень круто.

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

    Может кто подскажет?

    Reply
  60. Зеленоград

    Всем комментаторам в этой ветке термин «Тупой одинэсник» неприменим.

    Reply
  61. ildarovich

    (60) sapsan322, сомневаюсь, что в данной задаче имеет смысл делать отбор по реквизиту регистратора. Это специально не разрешено. Так вы можете «выдернуть» из цепочки взаиморасчетов отдельные документы, исказив их логику. Если интересует «проект», сделка, то эта информация должна в регистре отражаться, а к отборам по его измерениям вроде бы доступ есть. Если речь идет об ответственных (менеджерах), то объясните подробнее логику бизнес-задачи — можно будет подумать, как это сделать. Возможно, подтолкнете решение следующей задачи.

    Reply
  62. sapsan322

    (62) Стоит задача сделать отбор по Ответственному менеджеру и ТипуЗаказа.

    ТипЗаказа — это реквизит с типом перечисление и в нем имеется 3 значения: Продажа, Ремонт детали и Ремонт Автомобиля.

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

    Reply
  63. insurgut

    С позволения автора выкладываю переделку отчета под УТ 11.1.

    Reply
  64. ildarovich

    (64) ОК

    Reply
  65. sapit

    Использовал ваш алгоритм в отчете. Применить как описано в публикации не получилось из за «ложки сахара» — при реальном использовании отчета расчет по ФИФО делается по дате оплаты, а не дате отгрузки. Можно добавить к дате оплаты время из даты отгрузки, но по факту оказалось что в эту самую секунду оформлено до 20 документов отгрузки или поставки. Данные документы появляются при массовом вводе документов прошлым числом (реализации за период оформляются на одну дату), поступают из БД поставщика и т.п. И все бы ничего, но округление в такой ситуации вызывает расхождение между конечным остатком по регистру и результатом в отчете. В итоге ваш алгоритм использую для выборки оборотов с небольшим «запасом», а дальше уже обычным перебором. Пока не было времени протестировать причину, но на действительно большой БД время выполнения запроса вашего алгоритма и «стандартного» примерно одинаковое. Оставил ваш, так как в итоге все равно потребовалась промежуточная ТЗ.

    Reply
  66. ildarovich

    (66) sapit,

    1) выигрыш в скорости будет тем больше, чем больше отгрузок у ОДНОГО контрагента. Если на каждого контрагента отгрузок сравнительно немного, то и выигрыш не будет заметен;

    2) вопрос одинакового времени легко решить, добавляя секунды ко времени ОПЛАТЫ, взятые из номера, например. Но я все же еще раз подчеркиваю. Если много отгрузок стоят на одно и то же время оплаты, то с точки зрения неоплаченности их не нужно разделять — лучше рассматривать их как одну большую отгрузку и находить общий процент неоплаченности этой отгрузки и применять затем этот процент к каждой составляющей. Погрешность округления можно исключить, отнеся копейки на последнюю отгрузку в пачке;

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

    ВЫБОР
    КОГДА Обороты.Период = Шаг.Край
    ТОГДА Шаг.Долг / Шаг.Сумма
    ИНАЧЕ 1
    КОНЕЦ * Обороты.СуммаВзаиморасчетовОборот КАК Долг,

    нужно писать

    ВЫБОР
    КОГДА Обороты.Период = Шаг.Край
    ТОГДА Обороты.СуммаВзаиморасчетовОборот * Шаг.Долг / Шаг.Сумма
    ИНАЧЕ Обороты.СуммаВзаиморасчетовОборот
    КОНЕЦ КАК Долг,

    Это исключит лишние ошибки округления!

    Reply
  67. sapit

    (67) по пункту 1 -проверю. 2 -все таки много минусов. Пояснить поставщику(покупателю) что мы оплачиваем пропорционально 10 поставок невозможно. Считать отдельно что оплачено, а что нет — возврат к ведению учета по докуменам расчетов. Отчет в этом случае теряет часть функционала — можно посмотреть ситуацию по компании в целом, но на основании полученных данных решить что оплачиваем именно эту поставку становится невозможным. 3 — проверю.

    Reply
  68. angler225

    Спасибо, отчет помог. И что главное, его удалось доработать под нашу измененную конфигурацию (Количество дней отсрочки реквизит справочника Торговые точки) не сломав голову.

    Reply
  69. yurikmellon

    спасибо, попробую

    Reply
  70. labirk

    Спасибо, действительно очень быстро работает!

    Reply
  71. Ibrogim

    Божественно!

    Думаю приспособить под расчёт прибыли по оплаченным реализациям

    p.s. На Уралмаше улицу, на которой есть остановка «Веер» именуют Вейерштрасса

    Reply
  72. корум

    (68) попробуйте не лепить все документы за день в одну секунду.

    Вопросы поставщиков/покупателей исчезнут автоматически.

    В сутках 86400 секунд, у вас что, по одному контрагенту в один день больше документов??

    Reply
  73. ildarovich

    (72) Ibrogim, учтите, пожалуйста замечание в (67). Сделал «детскую» ошибку: сначала поделил, потом умножил, когда всегда для избежания потери точности нужно сначала умножать, потом делить. В этой задаче это иногда (редко) проявляется в виде копеечных расхождений. Рецепт исправления в (67).

    Reply
  74. Tciban

    Уважаемый Сергей! Применил вашу обработку к старенькой УППшечке — все летает, алгоритм великолепен. Но мне нужно рассчитывать дни просрочки и даты получения денег как дата документа + отсрочка в рабочих днях. Попробовал пару способов, описанных на инфостарте — сразу начинаются резкие тормоза, при попытке исполнить отчет без отборов так и вовсе оут оф мемори! Пожалуйста подскажите как лучше всего реализовать мою задачу! Ссылочку бы на наилучший способ прибавления рабочих дней к датам в запросе!

    Reply
  75. CheBurator

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

    вдобавок есть толкование (встречался разное) — например 1. если календарная дата (острочка в календарных днях например) платежа попадает на нерабочий ден, то дата платежа переносится на первый рабочий день. или 2. попало на календарный нерабочий день — значит так и есть, банк не работает платеж пройдет позже. получится просрочка. поэтому платить надо думать раньше.. 😉

    для уточнения надо смотреть НПА

    Reply
  76. nucha

    Реально быстрый отчет.

    Reply
  77. nucha

    (77) Оказывается этот алгоритм применяют школьники в игре угадай число. Один загадывает число до 10. Другой пытаясь его отгадать называет: 5. Первый должен ответить больше оно загаданного или меньше. Далее отгадывающий говорит: 3 если меньше 5 и 7 если больше 5 и т.д.

    Reply
  78. ZLENKO

    Хотел бы воссстановить историческую справедливость. Я был автором первого отчета в 2009 г. на infostart с использованием методики FIFO в запросе. Сейчас представлено бесплатное доработанное переиздание этого отчета http://infostart.ru/public/117647/ Сам отчет был написан в 2007 г. Просьба добавить в раздел «Некоторые ссылки». В качестве доказательства прилагаю скриншот 🙂

    Reply
  79. ZLENKO

    Еще один proof link исторической справедливости прилагаю

    Reply
  80. ildarovich

    (79) Сделано

    Reply
  81. ZLENKO

    (81) Спасибо 🙂

    Reply
  82. German_Tagil

    Мда надо потрогать и подумать …

    Reply
  83. zhry

    Здравствуйте!

    Мне кажется в запросе есть ошибка в конце.

    ИЗ
    Шаг0 КАК Шаг
    ВНУТРЕННЕЕ СОЕДИНЕНИЕ РегистрНакопления.ВзаиморасчетыСКонтрагентами.Обороты( , &Дата,    Регистратор, ДоговорКонтрагента В (ВЫБРАТЬ Шаг.ДоговорКонтрагента ИЗ Шаг0 КАК Шаг)) КАК Обороты
    ПО Шаг.ДоговорКонтрагента = Обороты.ДоговорКонтрагента
    И Шаг.Край < = Обороты.Период

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

    Reply
  84. ildarovich

    (84) Сейчас отвечу наспех, по тексту в статье, не заглядывая в текст отчета в обработке. Наверное, вы правы. Видимо, о том же было замечание в (20). Учитывая еще (67), концовку действительно следует переписать. Но умозрительно кажется, что указанная вами неточность, должна приводить не к искажению суммы долга в строке, а к появлению в выдаче лишних строк с отрицательным долгом, которые легко убрать настройками в СКД.

    Вспоминая свои тогдашние размышления, хотел для статьи написать запрос покороче, без лишних временных таблиц. А так, конечно, до временной таблицы Обороты нужно получить таблицу оборотов с регистратором, которую затем соединять с таблицей Шаг0.

    Reply
  85. ildarovich

    (84) + (85) Скачал отчеты, посмотрел как в них. Все оказалось гораздо проще. Оказалось, что в статье по сравнению с отчетом не хватает условия

    ГДЕ
    Обороты.СуммаВзаиморасчетовОборот > 0

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

    Исправил теперь в статье, учел комментарий (67) про точность в статье и в обработках.

    Reply
  86. zhry

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

    Reply
  87. krava_vlad

    Есть ли вариант переделанного отчета под БУ?

    Reply
  88. ildarovich

    На данный момент у меня такого отчета нет, его необходимость для меня сомнительна. Вроде бы и счет 62 и 60 имеют аналитику по «документу расчета», откуда без труда можно увидеть просроченность. Да и штатный отчет имеется (ЗадолженностьПокупателейПоСрокамДолга).

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

    Reply
  89. dimaxx

    Этот же отчет, только с анализом уже оплаченных документов. Цены бы ему не было. Чтобы видеть как закрываются документы в срок или нет и в какой срок с условием отсрочки… А так да скорость поражает. Спасибо за отчет очень интересный.

    Reply
  90. albert

    Тот же вопрос, что и в (75). Как переписать запрос для расчета просрочки по рабочим дням (точнее 2 варианта в зависимости от реквизита договора) с сохранением быстродействия?

    Reply
  91. powerpc

    Автора однозначно в лауреаты нобелевской премии!

    Reply
  92. evn-zorin

    У автора очень грамотные посты, интересно, чем он занимается в повседневной жизни, профессор, доктор математических наук?

    Reply
  93. ildarovich

    (93) на самом деле доцент, кандидат технических наук, в повседневной жизни занимаюсь внедрением 1С, программирую

    Reply
  94. vendim

    Спасибо огромное! На базе приведённого автором алгоритма сделал невозможное: отчёт, который формировался часами и нередко вываливался из-за переполнения памяти на скуле, теперь формируется всего 2 минуты! Фантастика!

    Reply
  95. ivv1970

    (14)



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

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

    Reply
  96. IssakN
    Reply
  97. Mellow

    Добрий день!

    Подскажите пожалуйста почему когда добавляеш сделку и пробееш сделать групировку по сделка суми не сходятся с стандартним отчетом «Взаиморасчети с контрагентами»

    Reply
  98. 028

    Здесь будет работать?

    Управление торговым предприятием для Казахстана, редакция 2.0

    Reply
  99. ildarovich

    Не знаю. У меня этой конфигурации нет и не знаю, на основе чего она написана. Если на базе УТ10, то будет.

    Reply

Leave a Comment

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