Организация системы отслеживания и отключения КТВ должников на BGBS с использованием CRM плагина
Материал из BiTel WiKi
Admin (Обсуждение | вклад) |
Admin (Обсуждение | вклад) |
||
Строка 317: | Строка 317: | ||
Предполагаем, что событие таймера обработано в результате чего получено множество задач типа '''Обзвон должника'''. | Предполагаем, что событие таймера обработано в результате чего получено множество задач типа '''Обзвон должника'''. | ||
+ | |||
+ | {| | ||
+ | |- valign=top | ||
+ | | [[Изображение:ktv_debt_debt_call_task.png|thumb|300px|Задачи обзвона должника]] | ||
+ | |} | ||
+ | |||
+ | Возможен непосредственный обзвон должников либо генерация квитанций и разнос их по квартирам. Как показывает практика, второй метод более эффективен. Для генерации квитанций подобного вида: | ||
+ | |||
+ | {| | ||
+ | |- valign=top | ||
+ | | [[Изображение:ktv_debt_debt_kvit.png|thumb|300px|Квитанции]] | ||
+ | |} | ||
+ | |||
+ | вы можете использовать шаблон отчета по задачам '''Квитанции'''. Сам XSL шаблон вложен в файл в конце данной статьи, для получения отчета необходимо выбрать тип шаблона над левым верхним углом таблицы задач, далее сохранить его в HTML файл и печатать. Непосредственная печать из биллинга невозможна, т.к. встроенный HTML компанент JAVA плохо поддерживает CSS, который используется для разделения страниц. | ||
+ | |||
+ | Шаблоны квитанций прописываются в конфигурации сервера биллинга следующим образом: | ||
+ | <pre> | ||
+ | register.task.report.format=register_tasks.xsl:Отчет по подключению;register_tasks_1.xsl:Отчет по обслуживанию;register_tasks_2.xsl:Отчет по должникам;register_tasks_3.xsl:Квитанции | ||
+ | </pre> | ||
+ | Файлы шаблонов вы можете загрузить здесь: [[Медиа:ktv_debt_xsl.zip]] |
Версия 06:35, 25 сентября 2008
Ставится задача автоматического массового выявления, оповещения и отключения должников по КТВ.
В планировщике заданий добавляем генерацию события таймера. Событие генерируется каждые сутки в 0 часов 1 минуту.
Общий алгоритм работы следующий:
- Раз в месяц по таймеру запускается задача выявления должников. В параметрах договоров-должников проставляется дата фиксации долга и создается задача на обзвон должников.
- Далее производится обзвон (либо разнос квитанций), задачи помечаются выполненными и обрабатываются.
- Следюущая обработка события таймера зафиксировав выполненную задачу обзвона и констатировав, что долг еще есть - создает задачу на отключение должника.
- При обработке задачи отключения должника автоматически закрывается абонентская плата, устанавливается группа договора долг.
Типы задач:
- Отключение должника (код 3 в данном примере)
- Обзвон должника (код 23 в данном примере)
- Подключение должника (код 24 в данном примере)
Необходимые параметры договора:
- Адрес, тип Адрес (код 9) в данном примере
- Дата фиксации долга, тип Дата (код 34 в данном примере)
Необходимые группы договоров:
- Должник - пометка договора, октлюченного за долг (код группы 15 в данном примере)
В справочнике групп решения CRM могут быть определены одна или несколько групп, группа может определяться в зависимости от номера квартала. В данном примере есть две группы решения с кодами 1 и 2.
В меню Автоматизация=>Скрипты поведения добавляем обработчик данного события.
import java.sql.*; import java.util.*; import bitel.billing.server.contract.bean.*; import bitel.billing.server.script.bean.event.*; import bitel.billing.server.util.*; import ru.bitel.bgbilling.plugins.crm.server.dao.*; import ru.bitel.bgbilling.plugins.crm.common.model.*; // В зависимости от квартала клиента // возможна передача различных групп решения проблемы. int getTaskGroup() { quarter = ""; query = "SELECT quarter.title FROM contract_parameter_type_2 AS cp " + " LEFT JOIN address_house AS house ON cp.hid=house.id " + " LEFT JOIN address_quarter AS quarter ON house.quarterid=quarter.id " + " WHERE cp.cid=? AND cp.pid=?"; ps = con.prepareStatement( query ); ps.setInt( 1, cid ); ps.setInt( 2, TASK_ADDRESS_PARAM ); rs = ps.executeQuery(); if( rs.first() ) { quarter = rs.getString( 1 ); } result = 0; if( guarter.equals( "1" ) ) { result = GROUP_1; } else { result = GROUP_2; } return result; } // коды групп решения задач GROUP_1 = 1; GROUP_2 = 2; // код типов задач на отключения и обзвон DISCONNECT_TASK = 3; CALL_TASK = 23; // код параметра договора "Дата фиксации долга" DEBT_FIX_PARAM = 34; TASK_ADDRESS_PARAM = 9; // сколько дней ждать после обзвона DAYS_AFTER_CALL = 3; // группа "Должник" GROUP_DOLG = 15; // группы "ЕРКЦ", "Условно расторгнут", "VIP" - договора с такими группами не наблюдаются GROUP_ERKC = 43; GROUP_USL_RAST = 21; GROUP_VIP = 12; // размер ежемесячного платежа (абонплаты) обычного MONTHLY_CHARGE = 130; // размер ежемесячного платежа (абонплаты) уменьшенного MONTHLY_CHARGE_CHEAP = 105; // дата подлкючения к ЕРКЦ ERKC_ACTIVE_DATE_PARAM = 52; cid = event.getContractID(); time = event.getGenerateTime(); print( "cid=" + cid ); // проверка флага события таймера, обрабатываем только события с флагом = 1 if( event.getFlag() != 1 ) { print( "Flag != 1, skipping.." ); return; } bu = new BalanceUtils( con ); rtm = new RegisterTaskManager( con ); cm = new ContractManager( con ); cpu = new ContractParamUtils( con ); tm = new ContractTariffManager( con ); contract = cm.getContractByID( cid ); // Проверки --------------------------------------------------------------------------------------------------------------- // Договора ЕРКЦ не контролируются на долги, ЕРКЦ занимается этим сам erkcGroup = ( contract.getGroups() & (1L<<GROUP_ERKC) ) > 0; if( erkcGroup ) { print( "erkc group" ); erkcActiveDate = cpu.getDateParam( cid, ERKC_ACTIVE_DATE_PARAM ); if ( erkcActiveDate == null ) { error( "ERKC Activation Date is not set" ); return; } else { if ( time.before( erkcActiveDate ) ) { print( "ERKC is not yet activated. Activation date: " + erkcActiveDate.get( Calendar.DAY_OF_MONTH ) + "." + ( erkcActiveDate.get( Calendar.MONTH ) + 1 ) + "." + erkcActiveDate.get( Calendar.YEAR ) ); } else { print( "ERKC activated. No debts controlled" ); //return; } } } // Условно расторгнутые uslRastorg = ( contract.getGroups() & (1L<<GROUP_USL_RAST) ) > 0; if( uslRastorg ) { print( "usl rastorg group" ); return; } // Должники dolgGroup = ( contract.getGroups() & (1L<<GROUP_DOLG) ) > 0; if( dolgGroup ) { print( "dolg group" ); return; } // VIP-клиенты vipGroup = ( contract.getGroups() & (1L<<GROUP_VIP) ) > 0; if( vipGroup ) { print( "vip group" ); return; } // Кредитные договора if( contract.getBalanceMode() == Contract.CREDIT_BALANCE_MODE ) { print( "credit mode" ); return; } // Конец проверок --------------------------------------------------------------------------------------------------------- debt = false; float balance = bu.getBalance( time, cid ); // Выбираем лимит под тариф tp = tm.getContractTariff( cid, time ); if( tp == null ) { error("no active tariff plans" ); return; } tpid = tp.getTariffPlanID(); switch( tpid ) { case 10: monthlyCharge = MONTHLY_CHARGE; break; case 12: monthlyCharge = MONTHLY_CHARGE_CHEAP; break; case 14: monthlyCharge = MONTHLY_CHARGE_CHEAP; break; default: monthlyCharge = MONTHLY_CHARGE; break; } // до 20-го числа за должников считаем тех, у кого баланс меньше 3-х абонок, // после 20-го -- меньше 2-х абонок if( time.get( Calendar.DATE ) < 20 ) { print("do 20"); if( balance < - 3 * monthlyCharge + .01 ) { debt = true; } } else { print("posle 20"); if( balance < - 2 * monthlyCharge + .01 ) { debt = true; } } if( erkcGroup ) { if( balance < - 6 * monthlyCharge + .01 ) { debt = true; print( "Debug ERKC < 6 month debt" ); } } if( !debt ) { return; } fixDate = cpu.getDateParam( cid, DEBT_FIX_PARAM ); if( fixDate == null ) { fixDate = (Calendar)time.clone(); cpu.setDateParam( cid, DEBT_FIX_PARAM, fixDate ); } print( "fixDate=" + TimeUtils.formatDate( fixDate ) ); callTask = null; disconnectTask = null; // список задач после даты фиксции долга taskList = rtm.getAfterDateTaskList( cid, fixDate ); for( RegisterTask task : taskList ) { // найдена задача на обзвон if( task.getTypeID() == CALL_TASK ) { callTask = task; } // найдена задача на отключение if( task.getTypeID() == DISCONNECT_TASK ) { disconnectTask = task; } } print( "callTask = " + callTask + "; disconnectTask = " + disconnectTask ); // долг есть а задачи на обзвон нет - создание задачи на обзвон if( callTask == null ) { callTask = new RegisterTask(); callTask.setContractID( cid ); callTask.setAddressParamID( TASK_ADDRESS_PARAM ); callTask.setOpenTime( time ); callTask.setOpenUserID( 0 ); callTask.setTypeID( CALL_TASK ); callTask.setComment( "Долг: " + Utils.formatCost( balance ) ) ; groupId = getTaskGroup(); callTask.setGroupID( groupId ); print( "creating call task" ) ; rtm.updateTask( "new", callTask ); } // статус задачи открыт - обновляем информацию о долге в комментарии задачи else if ( callTask.getStatus() == RegisterTask.STATUS_OPEN ) { callTask.setComment( "Долг: " + Utils.formatCost( balance ) ) ; rtm.updateTask( String.valueOf( callTask.getID() ), callTask ); } // задача дозвона выполнена else if ( callTask.getStatus() == RegisterTask.STATUS_CLOSED ) { // задачи на отключения нет if( erkcGroup ) { print( "For ERKC orders don't need taks for turn off" ) ; return; } if ( disconnectTask == null ) { // после обзвона прошел срок - создание задачи отключения if ( TimeUtils.daysDelta( callTask.getExecuteDate(), time ) >= DAYS_AFTER_CALL ) { disconnectTask = new RegisterTask(); disconnectTask.setContractID( cid ); disconnectTask.setAddressParamID( TASK_ADDRESS_PARAM ); disconnectTask.setOpenTime( time ); disconnectTask.setOpenUserID( 0 ); disconnectTask.setTypeID( DISCONNECT_TASK ); groupId = getTaskGroup(); disconnectTask.setGroupID( groupId ); disconnectTask.setComment( "Долг: " + Utils.formatCost( balance ) ) ; print( "creating disconnect task." ); rtm.updateTask( "new", disconnectTask ); } } // открыта задача на отключение - обновление информации о долге в комментарии задачи else if ( disconnectTask.getStatus() == RegisterTask.STATUS_OPEN ) { disconnectTask.setComment( "Долг: " + Utils.formatCost( balance ) ) ; rtm.updateTask( String.valueOf( disconnectTask.getID() ), disconnectTask ); } }
Предполагаем, что событие таймера обработано в результате чего получено множество задач типа Обзвон должника.
Возможен непосредственный обзвон должников либо генерация квитанций и разнос их по квартирам. Как показывает практика, второй метод более эффективен. Для генерации квитанций подобного вида:
вы можете использовать шаблон отчета по задачам Квитанции. Сам XSL шаблон вложен в файл в конце данной статьи, для получения отчета необходимо выбрать тип шаблона над левым верхним углом таблицы задач, далее сохранить его в HTML файл и печатать. Непосредственная печать из биллинга невозможна, т.к. встроенный HTML компанент JAVA плохо поддерживает CSS, который используется для разделения страниц.
Шаблоны квитанций прописываются в конфигурации сервера биллинга следующим образом:
register.task.report.format=register_tasks.xsl:Отчет по подключению;register_tasks_1.xsl:Отчет по обслуживанию;register_tasks_2.xsl:Отчет по должникам;register_tasks_3.xsl:Квитанции
Файлы шаблонов вы можете загрузить здесь: Медиа:ktv_debt_xsl.zip