Организация системы отслеживания и отключения КТВ должников на BGBS с использованием CRM плагина
Материал из BiTel WiKi
(Различия между версиями)
Admin (Обсуждение | вклад) (Новая: Ставится задача автоматического массового выявления, оповещения и отключения должников по КТВ. В пл...) |
Admin (Обсуждение | вклад) |
||
Строка 3: | Строка 3: | ||
В планировщике заданий добавляем генерацию события таймера. Событие генерируется каждые сутки в 0 часов 1 минуту. | В планировщике заданий добавляем генерацию события таймера. Событие генерируется каждые сутки в 0 часов 1 минуту. | ||
- | [[Изображение:ktv_debt_fix_timer.png]] | + | {| |
+ | |- valign=top | ||
+ | | [[Изображение:ktv_debt_fix_timer.png|thumb|300px|Настройка задачи в планировщике]] | ||
+ | |} | ||
Общий алгоритм работы следующий: | Общий алгоритм работы следующий: | ||
Строка 15: | Строка 18: | ||
# Обзвон должника (код 23 в данном примере) | # Обзвон должника (код 23 в данном примере) | ||
# Подключение должника (код 24 в данном примере) | # Подключение должника (код 24 в данном примере) | ||
- | |||
- | |||
Необходимые параметры договора: | Необходимые параметры договора: | ||
- | # Адрес, тип Адрес (код 9) в данном примере | + | # Адрес, тип '''Адрес''' (код 9) в данном примере |
# Дата фиксации долга, тип '''Дата''' (код 34 в данном примере) | # Дата фиксации долга, тип '''Дата''' (код 34 в данном примере) | ||
Необходимые группы договоров: | Необходимые группы договоров: | ||
# Должник - пометка договора, октлюченного за долг (код группы 15 в данном примере) | # Должник - пометка договора, октлюченного за долг (код группы 15 в данном примере) | ||
+ | |||
+ | В справочнике групп решения CRM могут быть определены одна или несколько групп, группа может определяться в зависимости от номера квартала. В данном примере есть две группы решения с кодами 1 и 2. | ||
В меню '''Автоматизация=>Скрипты поведения''' добавляем обработчик данного события. | В меню '''Автоматизация=>Скрипты поведения''' добавляем обработчик данного события. | ||
Строка 37: | Строка 40: | ||
import ru.bitel.bgbilling.plugins.crm.common.model.*; | import ru.bitel.bgbilling.plugins.crm.common.model.*; | ||
- | / | + | // В зависимости от квартала клиента |
- | + | // возможна передача различных групп решения проблемы. | |
int getTaskGroup() { | int getTaskGroup() { | ||
- | + | quarter = ""; | |
query = | query = | ||
- | "SELECT quarter. | + | "SELECT quarter.title FROM contract_parameter_type_2 AS cp " + |
" LEFT JOIN address_house AS house ON cp.hid=house.id " + | " LEFT JOIN address_house AS house ON cp.hid=house.id " + | ||
" LEFT JOIN address_quarter AS quarter ON house.quarterid=quarter.id " + | " LEFT JOIN address_quarter AS quarter ON house.quarterid=quarter.id " + | ||
Строка 54: | Строка 57: | ||
rs = ps.executeQuery(); | rs = ps.executeQuery(); | ||
if( rs.first() ) { | if( rs.first() ) { | ||
- | + | quarter = rs.getString( 1 ); | |
+ | } | ||
+ | |||
+ | result = 0; | ||
+ | |||
+ | if( guarter.equals( "1" ) ) | ||
+ | { | ||
+ | result = GROUP_1; | ||
+ | } | ||
+ | else | ||
+ | { | ||
+ | result = GROUP_2; | ||
} | } | ||
Строка 60: | Строка 74: | ||
} | } | ||
+ | // коды групп решения задач | ||
+ | GROUP_1 = 1; | ||
+ | GROUP_2 = 2; | ||
+ | // код типов задач на отключения и обзвон | ||
DISCONNECT_TASK = 3; | DISCONNECT_TASK = 3; | ||
CALL_TASK = 23; | CALL_TASK = 23; | ||
- | + | ||
+ | // код параметра договора "Дата фиксации долга" | ||
DEBT_FIX_PARAM = 34; | DEBT_FIX_PARAM = 34; | ||
- | TASK_ADDRESS_PARAM = 9; | + | TASK_ADDRESS_PARAM = 9; |
+ | // сколько дней ждать после обзвона | ||
DAYS_AFTER_CALL = 3; | DAYS_AFTER_CALL = 3; | ||
- | + | // группа "Должник" | |
- | + | ||
- | + | ||
GROUP_DOLG = 15; | GROUP_DOLG = 15; | ||
+ | // группы "ЕРКЦ", "Условно расторгнут", "VIP" - договора с такими группами не наблюдаются | ||
GROUP_ERKC = 43; | GROUP_ERKC = 43; | ||
GROUP_USL_RAST = 21; | GROUP_USL_RAST = 21; | ||
GROUP_VIP = 12; | GROUP_VIP = 12; | ||
+ | // размер ежемесячного платежа (абонплаты) обычного | ||
MONTHLY_CHARGE = 130; | MONTHLY_CHARGE = 130; | ||
+ | // размер ежемесячного платежа (абонплаты) уменьшенного | ||
MONTHLY_CHARGE_CHEAP = 105; | MONTHLY_CHARGE_CHEAP = 105; | ||
- | + | // дата подлкючения к ЕРКЦ | |
ERKC_ACTIVE_DATE_PARAM = 52; | ERKC_ACTIVE_DATE_PARAM = 52; | ||
- | |||
cid = event.getContractID(); | cid = event.getContractID(); | ||
Строка 87: | Строка 107: | ||
print( "cid=" + cid ); | print( "cid=" + cid ); | ||
+ | // проверка флага события таймера, обрабатываем только события с флагом = 1 | ||
if( event.getFlag() != 1 ) { | if( event.getFlag() != 1 ) { | ||
print( "Flag != 1, skipping.." ); | print( "Flag != 1, skipping.." ); | ||
Строка 152: | Строка 173: | ||
// Конец проверок --------------------------------------------------------------------------------------------------------- | // Конец проверок --------------------------------------------------------------------------------------------------------- | ||
- | debt = false; | + | debt = false; |
- | + | ||
float balance = bu.getBalance( time, cid ); | float balance = bu.getBalance( time, cid ); | ||
Строка 193: | Строка 213: | ||
} | } | ||
} | } | ||
- | |||
if( erkcGroup ) { | if( erkcGroup ) { | ||
Строка 205: | Строка 224: | ||
return; | return; | ||
} | } | ||
- | |||
Строка 219: | Строка 237: | ||
disconnectTask = null; | disconnectTask = null; | ||
+ | // список задач после даты фиксции долга | ||
taskList = rtm.getAfterDateTaskList( cid, fixDate ); | taskList = rtm.getAfterDateTaskList( cid, fixDate ); | ||
for( RegisterTask task : taskList ) | for( RegisterTask task : taskList ) | ||
{ | { | ||
+ | // найдена задача на обзвон | ||
if( task.getTypeID() == CALL_TASK ) | if( task.getTypeID() == CALL_TASK ) | ||
{ | { | ||
callTask = task; | callTask = task; | ||
} | } | ||
- | + | // найдена задача на отключение | |
if( task.getTypeID() == DISCONNECT_TASK ) | if( task.getTypeID() == DISCONNECT_TASK ) | ||
{ | { | ||
- | + | disconnectTask = task; | |
} | } | ||
} | } | ||
Строка 235: | Строка 255: | ||
print( "callTask = " + callTask + "; disconnectTask = " + disconnectTask ); | print( "callTask = " + callTask + "; disconnectTask = " + disconnectTask ); | ||
- | // долг есть а задачи на обзвон нет | + | // долг есть а задачи на обзвон нет - создание задачи на обзвон |
if( callTask == null ) | if( callTask == null ) | ||
{ | { | ||
Строка 250: | Строка 270: | ||
groupId = getTaskGroup(); | groupId = getTaskGroup(); | ||
- | |||
- | |||
- | |||
- | |||
- | |||
callTask.setGroupID( groupId ); | callTask.setGroupID( groupId ); | ||
Строка 260: | Строка 275: | ||
rtm.updateTask( "new", callTask ); | rtm.updateTask( "new", callTask ); | ||
} | } | ||
- | // статус задачи открыт - обновляем информацию о долге | + | // статус задачи открыт - обновляем информацию о долге в комментарии задачи |
else if ( callTask.getStatus() == RegisterTask.STATUS_OPEN ) { | else if ( callTask.getStatus() == RegisterTask.STATUS_OPEN ) { | ||
callTask.setComment( "Долг: " + Utils.formatCost( balance ) ) ; | callTask.setComment( "Долг: " + Utils.formatCost( balance ) ) ; | ||
Строка 273: | Строка 288: | ||
} | } | ||
if ( disconnectTask == null ) { | if ( disconnectTask == null ) { | ||
- | // после обзвона прошел срок | + | // после обзвона прошел срок - создание задачи отключения |
if ( TimeUtils.daysDelta( callTask.getExecuteDate(), time ) >= DAYS_AFTER_CALL ) { | if ( TimeUtils.daysDelta( callTask.getExecuteDate(), time ) >= DAYS_AFTER_CALL ) { | ||
disconnectTask = new RegisterTask(); | disconnectTask = new RegisterTask(); | ||
Строка 286: | Строка 301: | ||
groupId = getTaskGroup(); | groupId = getTaskGroup(); | ||
- | |||
- | |||
- | |||
- | |||
disconnectTask.setGroupID( groupId ); | disconnectTask.setGroupID( groupId ); | ||
disconnectTask.setComment( "Долг: " + Utils.formatCost( balance ) ) ; | disconnectTask.setComment( "Долг: " + Utils.formatCost( balance ) ) ; | ||
Строка 297: | Строка 308: | ||
} | } | ||
} | } | ||
- | // открыта задача на отключение | + | // открыта задача на отключение - обновление информации о долге в комментарии задачи |
else if ( disconnectTask.getStatus() == RegisterTask.STATUS_OPEN ) { | else if ( disconnectTask.getStatus() == RegisterTask.STATUS_OPEN ) { | ||
disconnectTask.setComment( "Долг: " + Utils.formatCost( balance ) ) ; | disconnectTask.setComment( "Долг: " + Utils.formatCost( balance ) ) ; | ||
Строка 304: | Строка 315: | ||
} | } | ||
</source> | </source> | ||
+ | |||
+ | Предполагаем, что событие таймера обработано в результате чего получено множество задач типа '''Обзвон должника'''. |
Версия 06:10, 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 ); } }
Предполагаем, что событие таймера обработано в результате чего получено множество задач типа Обзвон должника.