Организация системы отслеживания и отключения КТВ должников на BGBS с использованием CRM плагина

Материал из BiTel WiKi

(Различия между версиями)
Перейти к: навигация, поиск
Строка 1: Строка 1:
-
Ставится задача автоматического массового выявления, оповещения и отключения должников по КТВ.
 
-
 
-
В планировщике заданий добавляем генерацию события таймера. Событие генерируется каждые сутки в 0 часов 1 минуту.
 
-
 
-
{|
 
-
|- valign=top
 
-
| [[Изображение:ktv_debt_fix_timer.png|thumb|300px|Настройка задачи в планировщике]]
 
-
|}
 
-
 
-
Общий алгоритм работы следующий:
 
-
# Раз в месяц по таймеру запускается задача выявления должников. В параметрах договоров-должников проставляется дата фиксации долга и создается задача на обзвон должников.
 
-
# Далее производится обзвон (либо разнос квитанций), задачи помечаются выполненными и обрабатываются.
 
-
# Следюущая обработка события таймера зафиксировав выполненную задачу обзвона и констатировав, что долг еще есть - создает задачу на отключение должника.
 
-
# При обработке задачи отключения должника автоматически закрывается абонентская плата, устанавливается группа договора долг.
 
-
 
-
Типы задач:
 
-
# Отключение должника (код 3 в данном примере)
 
-
# Обзвон должника (код 23 в данном примере)
 
-
# Подключение должника (код 24 в данном примере)
 
-
 
-
Необходимые параметры договора:
 
-
# Адрес, тип '''Адрес''' (код 9) в данном примере
 
-
# Дата фиксации долга, тип '''Дата''' (код 34 в данном примере)
 
-
 
-
Необходимые группы договоров:
 
-
# Должник - пометка договора, октлюченного за долг (код группы 15 в данном примере)
 
-
 
-
В справочнике групп решения CRM могут быть определены одна или несколько групп, группа может определяться в зависимости от номера квартала. В данном примере есть две группы решения с кодами 1 и 2.
 
-
 
-
В меню '''Автоматизация=>Скрипты поведения''' добавляем обработчик данного события.
 
-
 
-
<source lang="java">
 
-
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.*;
 
-
 
// В зависимости от квартала клиента  
// В зависимости от квартала клиента  
//  возможна передача различных групп решения
//  возможна передача различных групп решения
Строка 77: Строка 36:
GROUP_1 = 1;
GROUP_1 = 1;
GROUP_2 = 2;
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 );
 
-
    }
 
-
}
 
-
</source>
 
-
 
-
Предполагаем, что событие таймера обработано в результате чего получено множество задач типа '''Обзвон должника'''.
 
-
 
-
{|
 
-
|- valign=top
 
-
| [[Изображение:ktv_debt_debt_call_task.png|thumb|300px|Задачи обзвона должника]]
 
-
|}
 
-
 
-
Возможен непосредственный обзвон должников либо генерация квитанций и разнос их по квартирам. Как показывает практика, второй метод более эффективен. Для генерации квитанций подобного  вида:
 
-
 
-
{|
 
-
|- valign=top
 
-
| [[Изображение:ktv_debt_debt_kvit.png|thumb|300px|Квитанции]]
 
-
|}
 
-
 
-
вы можете использовать шаблон отчета по задачам '''Квитанции'''. Для получения отчета необходимо выбрать тип шаблона над левым верхним углом таблицы задач, далее сохранить его в 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]]
 
-
 
-
После разнесения квитанций/обзвонов должников оператор помечает задачи выполненными, указав дату выполнения. Для реагирования на оплаты клиентов создается скрипт на событие '''Приход платежа'''. Скрипт закрывает задачи обзвона и отключения, если клиент оплатил достаточную сумму. Для уже отключенных клиентов создается задача '''Подключение должника'''. Все действия сопровождаются письмами на почтовые рассылки.  Задача на отключение закрывается только в том случае, если она не принята, т.е. монтажник не выехал к клиенту.
 
-
 
-
<source lang="java">
 
-
import bitel.billing.server.contract.bean.*;
 
-
import bitel.billing.server.util.*;
 
-
import java.util.*;
 
-
import ru.bitel.bgbilling.plugins.crm.server.dao.*;
 
-
import ru.bitel.bgbilling.plugins.crm.common.model.*;
 
-
import bitel.billing.server.model.*;
 
-
 
-
cid = event.getContractID();
 
-
time = event.getGenerateTime();
 
-
 
-
// В зависимости от квартала клиента
 
-
//  возможна передача различных групп решения
 
-
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;
 
-
}
 
DISCONNECT_TASK = 3;
DISCONNECT_TASK = 3;
Строка 550: Строка 202:
| [[Изображение:ktv_debt_disconnect_report.png|thumb|300px|Задачи на отключения]]
| [[Изображение:ktv_debt_disconnect_report.png|thumb|300px|Задачи на отключения]]
|}
|}
 +
 +
Выполненные задачи помечаются выполненными и обрабатываются оператором. При обработке задач отрабатывает скрипт для задач типа '''Подключение должника''' и '''Отключение должника'''.
 +
 +
<source lang="java">
 +
import java.sql.*;
 +
import java.util.*;
 +
import bitel.billing.server.contract.bean.*;
 +
import bitel.billing.server.util.*;
 +
import ru.bitel.bgbilling.plugins.crm.server.dao.*;
 +
import ru.bitel.bgbilling.plugins.crm.common.model.*;
 +
import bitel.billing.server.model.*;
 +
 +
DISCONNECT_TASK = 3;
 +
CONNECT_TASK = 24;
 +
 +
// группа должник
 +
GROUP_DOLG = 15;
 +
// группа "пенсионер"
 +
GROUP_PENS = 34;
 +
 +
// код услуги "абонплата"
 +
SERVICE_PAY  = 2;
 +
// код услуги "абонплата долг"
 +
SERVICE_PAY_DOLG = 3;
 +
// код услуги "абонплата пенсионеров"
 +
SERVICE_PAY_PENS = 8;
 +
 +
// код расхода "за повторное включение"
 +
RECONNECT_CHARGE = 5;
 +
DEBT_FIX_PARAM = 34;
 +
// сумма расхода за повторное включение
 +
RECONNECT_SUM = 75;
 +
 +
BALANCE_BORDER = 164.99f;
 +
ADDRESS_PARAM = 9;
 +
 +
FIO_PARAM = "1";
 +
PHONE_PARAM = 2;
 +
 +
cid = event.getContractID();
 +
task = event.getTask();
 +
 +
// В зависимости от квартала клиента
 +
//  возможна передача различных групп решения
 +
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;
 +
 +
report = event.getReport();
 +
 +
serviceManager = new ContractServiceManager( con );
 +
cu = new ContractUtils( con );
 +
cpu = new ContractParamUtils( con );
 +
chm =  new ChargeManager( con );
 +
contractManager = new ContractManager( con );
 +
bu = new BalanceUtils( con );
 +
taskManager = new RegisterTaskManager( con );
 +
 +
if( task.getTypeID() != DISCONNECT_TASK &&  task.getTypeID() != CONNECT_TASK )
 +
{
 +
print( "This task type does't processing.." );
 +
return;
 +
}
 +
 +
if( task.getStatus() != RegisterTask.STATUS_CLOSED )
 +
{
 +
report.append( cu.getContractTitle( cid, true ) );
 +
report.append( " => задача не закрыта\n" );
 +
return;
 +
}
 +
 +
Calendar date = task.getExecuteDate();
 +
if( date == null )
 +
{
 +
report.append( cu.getContractTitle( cid, true ) );
 +
report.append( " => не установлена дата исполнения\n" );
 +
 +
  error( "executeDate == null" );
 +
  return;
 +
}
 +
 +
beforeDay = task.getExecuteDate().clone();
 +
beforeDay.add( Calendar.DAY_OF_YEAR, -1 );
 +
 +
// перечень услуг на дату
 +
services = serviceManager.getContractServiceList( cid, date );
 +
 +
contract = contractManager.getContractByID( cid );
 +
 +
dolgGroup = (contract.getGroups() & (1<<GROUP_DOLG) ) > 0;
 +
print( "dolg group => " + dolgGroup );
 +
 +
pensGroup = (contract.getGroups() & (1L<<GROUP_PENS) ) > 0;
 +
print( "pens group => " + pensGroup );
 +
 +
// отключение должника
 +
if( task.getTypeID() == DISCONNECT_TASK )
 +
{
 +
    if( dolgGroup )   
 +
    {
 +
          report.append( cu.getContractTitle( cid, true ) );
 +
          report.append( " => у договора уже стоит группа долг\n" );
 +
         
 +
          return;
 +
    }
 +
 +
    for( ContractService service : services )
 +
    {
 +
          // закрытие абонплаты 
 +
          if( service.getServiceID() == SERVICE_PAY || service.getServiceID() == SERVICE_PAY_PENS )
 +
          {
 +
                service.setDate2( beforeDay );
 +
                serviceManager.updateContractService( String.valueOf( service.getID() ), service );
 +
          } 
 +
    }
 +
 +
    // открытие абонплаты "долг"
 +
    cs = new ContractService();
 +
    cs.setContractID( cid );
 +
    cs.setServiceID( SERVICE_PAY_DOLG );
 +
    cs.setDate1( task.getExecuteDate() );
 +
    cs.setComment( "Установлена скриптом" );
 +
 +
    serviceManager.updateContractService( "new", cs );
 +
   
 +
    // установка группы "долг"
 +
    cpu.setGroup( cid,  GROUP_DOLG  );
 +
 +
    // если баланс позволяет - сразу открытие задачи "Подключение должника"
 +
    balance = bu.getBalance(  new GregorianCalendar(), cid );
 +
   
 +
    print( "balance=" + balance );
 +
     
 +
    if( balance >= BALANCE_BORDER )
 +
    {
 +
        // есть не закрытая задача "подключение должника"
 +
      connectTask = false;
 +
 +
        List activeTasks = taskManager.getNoClosedTaskList( cid );
 +
        for( RegisterTask task : activeTasks )
 +
        {
 +
print( "task type: " + task.getTypeID() ) ;
 +
          if( task.getTypeID() == CONNECT_TASK  )
 +
          {
 +
              connectTask = true;
 +
              break;
 +
          }
 +
        }
 +
 +
        print( "connectTask => " + connectTask );
 +
        // создание задачи на подключение
 +
        if( !connectTask )
 +
        {
 +
          task = new RegisterTask();
 +
          task.setContractID( cid );
 +
          task.setTypeID(  CONNECT_TASK );
 +
          task.setGroupID( getTaskGroup() );
 +
          task.setOpenUserID( 0 ) ;
 +
          task.setOpenTime( event.getGenerateTime() );
 +
          task.setComment( "" );
 +
          task.setAddressParamID( ADDRESS_PARAM );
 +
 +
          taskManager.updateTask( "new", task );
 +
 +
            report.append( cu.getContractTitle( cid, true ) );
 +
            report.append( " => создана задача на подключение\n" );
 +
 +
            //отправка письма на почту
 +
            filter = new RegisterTaskManager.TaskFilter();
 +
            filter.id = task.getID();
 +
            filter.fioParams = FIO_PARAM;
 +
            filter.phoneParam = PHONE_PARAM;
 +
            filter.processed = -1;
 +
            filter.orders = new ArrayList();
 +
         
 +
            personalTask = taskManager.getTaskList( filter, new Page( 1, 1 ) ).get( 0 );
 +
 +
            message = new StringBuffer( 300 );
 +
            message.append( personalTask.getStreet() );
 +
            message.append( " " );
 +
            message.append( personalTask.getHouse() );
 +
            message.append( " " ); 
 +
            message.append( personalTask.getFlat() );
 +
            message.append( " " ); 
 +
            message.append( personalTask.getContract() ); 
 +
         
 +
            new MailMsg( setup ).sendMessageEx( "bill@ufanet.ru;disp@ufanet.ru;setks@ufanet.ru", "Оплатил отключенный", message.toString(), "text/plain" );
 +
     
 +
            print( "Send message Pay in disconnect process" );
 +
 +
        }
 +
    }
 +
}
 +
// подключение должника
 +
else if( task.getTypeID() == CONNECT_TASK )
 +
{
 +
      if( !dolgGroup )   
 +
    {
 +
          report.append( cu.getContractTitle( cid, true ) );
 +
          report.append( " => у договора нет группы долг\n" );
 +
         
 +
          return;
 +
    }
 +
 +
    for( ContractService service : services )
 +
    {
 +
          // закрытие абонплаты  "долг"
 +
          if( service.getServiceID() == SERVICE_PAY_DOLG )
 +
          {
 +
                service.setDate2( beforeDay );
 +
                serviceManager.updateContractService( String.valueOf( service.getID() ), service );
 +
          } 
 +
    }
 +
 +
    // открытие абонплаты
 +
    cs = new ContractService();
 +
    cs.setContractID( cid );
 +
    if( pensGroup )
 +
    {
 +
      cs.setServiceID( SERVICE_PAY_PENS );
 +
    }
 +
    else
 +
    {
 +
      cs.setServiceID( SERVICE_PAY );
 +
    }
 +
    cs.setDate1( task.getExecuteDate() );
 +
    cs.setComment( "Установлена скриптом" );
 +
 +
    serviceManager.updateContractService( "new", cs );
 +
 +
    // снятие расхода за повторное подключение
 +
    charge = new Charge();
 +
    charge.setContractID( cid );
 +
    charge.setChargeTypeID( RECONNECT_CHARGE );
 +
    charge.setSumma( RECONNECT_SUM );
 +
    charge.setDate( task.getExecuteDate() );
 +
    charge.setComment( "Установлена скриптом" );
 +
 +
    chm.updateCharge( "new", charge );
 +
 +
    print( "setting charge" );
 +
 +
    bu.updateBalance( task.getExecuteDate(), cid );
 +
   
 +
    // сброс группы "Должник"
 +
    cpu.unsetGroup( cid,  GROUP_DOLG  );
 +
    cpu.deleteDateParam( cid,  DEBT_FIX_PARAM );
 +
}
 +
 +
task.setProcessed( true );
 +
</source>
 +
 +
Когда клиент отключен за долг у него устанавливается абонплата '''Долг''' с нулевой ценой. Разумного требованию этому нет, сделано с точки зрения аналитики базы. При обработке задач скрипт выдает отчет по ошибкам и нестыковкам оператору.

Версия 07:09, 25 сентября 2008

// В зависимости от квартала клиента // возможна передача различных групп решения 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;

CONNECT_TASK = 24;

TASK_ADDRESS_PARAM = 9; GROUP_DOLG = 15;

// коды параметров договора ФИО и Телефон FIO_PARAM = "1"; PHONE_PARAM = 2;

DEBT_FIX_PARAM = 34;

DEBT_LIMIT = 260; // минимальная сумма баланса, после которой подключать BALANCE_BORDER = 164.99f;

cpu = new ContractParamUtils( con ); contractManager = new ContractManager( con ); taskManager = new RegisterTaskManager( con ); bu = new BalanceUtils( con );

balance = bu.getBalance( time, cid ); contract = contractManager.getContractByID( cid );

dolgGroup = (contract.getGroups() & (1<<GROUP_DOLG) ) > 0; print( "dolg group => " + dolgGroup );

// оплатил должник - можно подлкючать if( dolgGroup && balance > BALANCE_BORDER ) {

     // есть не закрытая задача "подключение должника"
     connectTask = false;
     filter = new RegisterTaskManager.TaskFilter();
     filter.types =  String.valueOf( CONNECT_TASK );
     filter.cid = cid;
     activeTasks = taskManager.getNoClosedTaskList( cid );
     for( RegisterTask task : activeTasks )
     {
        // задача не закрыта либо закрыта но не обработана 
        if( task.getStatus() != RegisterTask.STATUS_CLOSED ||
            !task.isProcessed() )
        {
            connectTask = true;
            print( "1" );
            break;
        } 
     }
     print( "connectTask => " + connectTask );
      // создание задачи на подключение
     if( !connectTask )
     {
        RegisterTask task = new RegisterTask(); 
        task.setContractID( cid );
        task.setTypeID(  CONNECT_TASK );
        groupId = getTaskGroup();
        
        task.setGroupID( groupId );
        task.setOpenUserID( 0 ) ;
        task.setOpenTime( event.getGenerateTime() );
        task.setComment( "" );
        task.setAddressParamID( TASK_ADDRESS_PARAM );
        taskManager.updateTask( "new", task );          
         //отправка письма на почту
         filter = new RegisterTaskManager.TaskFilter();
         filter.id = task.getID();
         filter.fioParams = FIO_PARAM;
         filter.phoneParam = PHONE_PARAM;
         filter.processed = -1;
          filter.orders = new ArrayList(); 
         
         print( "filter = " + filter + "; taskManager = " + taskManager );
         
         personalTask = taskManager.getTaskList( filter, new Page( 1, 1 ) ).get( 0 );
         
         message = new StringBuffer( 300 );
         message.append( contract.getTitle() );
         message.append( " " );
         message.append( personalTask.getStreet() );
         message.append( " " );
         message.append( personalTask.getHouse() );
         message.append( " " );  
         message.append( personalTask.getFlat() );
          
         new MailMsg( setup ).sendMessageEx( "disp@disp.com", "Оплатил должник", message.toString(), "text/plain" );
         //
         print( "Sending to mail.." );
     }

} // работающий клиент else {

 if( balance <= - ( DEBT_LIMIT - 1 ) )
 {
  print( "it's debt" );
  return;
 }
  
 // закрытие активных задач на дозвон и отключение должника
 List activeTasks = taskManager.getNoClosedTaskList( cid );
 for( RegisterTask task : activeTasks )
 {
    // собственно закрытие таких задач
    if( ( task.getTypeID() == DISCONNECT_TASK && task.getStatus() == RegisterTask.STATUS_OPEN  )  ||  
         (task.getTypeID() == CALL_TASK && task.getStatus() != RegisterTask.STATUS_CLOSED ) )
    {
        task.setResolution(  task.getResolution() + "\nОплатил " +  TimeUtils.formatDate( time ) );
      
        task.setCloseUserID( 0 );
        task.setCloseTime( time );
        task.setExecuteDate( time );
        task.setGroupID( DEFAULT_GROUP );
     
        task.setStatus( RegisterTask.STATUS_CLOSED );
       
        taskManager.updateTask( String.valueOf( task.getID() ), task );       
        print( "Closing DISCONNECT and CALL task: " + task.getID() );
    }
   
    // если принятая задача на отключение - отправка письма
    if( task.getTypeID() == DISCONNECT_TASK && task.getStatus() == RegisterTask.STATUS_ACCEPTED  )
    {

print( "The task already has an ACCEPTED status." );

         //отправка письма на почту
         filter = new RegisterTaskManager.TaskFilter();
         filter.id = task.getID();
         filter.fioParams = FIO_PARAM;
         filter.phoneParam = PHONE_PARAM;
         filter.processed = -1;
         filter.orders = new ArrayList(); 
         
         personalTask = taskManager.getTaskList( filter, new Page( 1, 1 ) ).get( 0 );
        
         message = new StringBuffer( 300 );
         message.append( personalTask.getStreet() );
         message.append( " " );
         message.append( personalTask.getHouse() );
         message.append( " " );  
         message.append( personalTask.getFlat() );
         message.append( " " );  
         message.append( personalTask.getContract() );  
          
         new MailMsg( setup ).sendMessageEx( "disp@disp.com", "Оплатил отключаемый", message.toString(), "text/plain" );
      
         print( "Send message Pay in disconnect" );
    }    
 }
     
 print( "Deleting debt fix date.." );
 cpu.deleteDateParam( cid,  DEBT_FIX_PARAM );

} </source>

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

Задачи на отключения

Выполненные задачи помечаются выполненными и обрабатываются оператором. При обработке задач отрабатывает скрипт для задач типа Подключение должника и Отключение должника.

import java.sql.*;
import java.util.*;
import bitel.billing.server.contract.bean.*;
import bitel.billing.server.util.*;
import ru.bitel.bgbilling.plugins.crm.server.dao.*;
import ru.bitel.bgbilling.plugins.crm.common.model.*;
import bitel.billing.server.model.*;
 
DISCONNECT_TASK = 3;
CONNECT_TASK = 24;
 
// группа должник
GROUP_DOLG = 15;
// группа "пенсионер"
GROUP_PENS = 34;
 
// код услуги "абонплата"
SERVICE_PAY  = 2;
// код услуги "абонплата долг"
SERVICE_PAY_DOLG = 3;
// код услуги "абонплата пенсионеров"
SERVICE_PAY_PENS = 8;
 
// код расхода "за повторное включение"
RECONNECT_CHARGE = 5;
DEBT_FIX_PARAM = 34; 
// сумма расхода за повторное включение
RECONNECT_SUM = 75;
 
BALANCE_BORDER = 164.99f;
ADDRESS_PARAM = 9;
 
FIO_PARAM = "1";
PHONE_PARAM = 2;
 
cid = event.getContractID();
task = event.getTask();
 
// В зависимости от квартала клиента 
//  возможна передача различных групп решения
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;
 
report = event.getReport();
 
serviceManager = new ContractServiceManager( con );
cu = new ContractUtils( con );
cpu = new ContractParamUtils( con );
chm =  new ChargeManager( con );
contractManager = new ContractManager( con );
bu = new BalanceUtils( con );
taskManager = new RegisterTaskManager( con );
 
if( task.getTypeID() != DISCONNECT_TASK &&  task.getTypeID() != CONNECT_TASK ) 
{
 print( "This task type does't processing.." );
 return;
}
 
if( task.getStatus() != RegisterTask.STATUS_CLOSED )
{
 report.append( cu.getContractTitle( cid, true ) );
 report.append( " => задача не закрыта\n" );
 return;
}
 
Calendar date = task.getExecuteDate();
if( date == null )
{
 report.append( cu.getContractTitle( cid, true ) );
 report.append( " => не установлена дата исполнения\n" );
 
  error( "executeDate == null" );
  return;
}
 
beforeDay = task.getExecuteDate().clone();
beforeDay.add( Calendar.DAY_OF_YEAR, -1 );
 
// перечень услуг на дату
services = serviceManager.getContractServiceList( cid, date );
 
contract = contractManager.getContractByID( cid );
 
dolgGroup = (contract.getGroups() & (1<<GROUP_DOLG) ) > 0;
print( "dolg group => " + dolgGroup );
 
pensGroup = (contract.getGroups() & (1L<<GROUP_PENS) ) > 0;
print( "pens group => " + pensGroup );
 
// отключение должника
if( task.getTypeID() == DISCONNECT_TASK )
{
     if( dolgGroup )     
     {
           report.append( cu.getContractTitle( cid, true ) );
           report.append( " => у договора уже стоит группа долг\n" );
 
           return; 
     }
 
     for( ContractService service : services )
     {
          // закрытие абонплаты  
          if( service.getServiceID() == SERVICE_PAY || service.getServiceID() == SERVICE_PAY_PENS )
          {
                 service.setDate2( beforeDay );
                 serviceManager.updateContractService( String.valueOf( service.getID() ), service );
          }  
     } 
 
     // открытие абонплаты "долг"
    cs = new ContractService();
    cs.setContractID( cid );
    cs.setServiceID( SERVICE_PAY_DOLG );
    cs.setDate1( task.getExecuteDate() );
    cs.setComment( "Установлена скриптом" );
 
    serviceManager.updateContractService( "new", cs );
 
    // установка группы "долг" 
    cpu.setGroup( cid,  GROUP_DOLG  );
 
     // если баланс позволяет - сразу открытие задачи "Подключение должника"
     balance = bu.getBalance(  new GregorianCalendar(), cid );
 
     print( "balance=" + balance );
 
     if( balance >= BALANCE_BORDER )
     {
        // есть не закрытая задача "подключение должника"
       connectTask = false;
 
        List activeTasks = taskManager.getNoClosedTaskList( cid );
        for( RegisterTask task : activeTasks )
        {
 		print( "task type: " + task.getTypeID() ) ;
           if( task.getTypeID() == CONNECT_TASK  )
           {
               connectTask = true;
               break;
           }
        }
 
        print( "connectTask => " + connectTask );
         // создание задачи на подключение
        if( !connectTask )
        {
           task = new RegisterTask(); 
           task.setContractID( cid );
           task.setTypeID(  CONNECT_TASK );
           task.setGroupID( getTaskGroup() );
           task.setOpenUserID( 0 ) ;
           task.setOpenTime( event.getGenerateTime() );
           task.setComment( "" );
           task.setAddressParamID( ADDRESS_PARAM );
 
           taskManager.updateTask( "new", task );
 
            report.append( cu.getContractTitle( cid, true ) );
            report.append( " => создана задача на подключение\n" );
 
            //отправка письма на почту
            filter = new RegisterTaskManager.TaskFilter();
            filter.id = task.getID();
            filter.fioParams = FIO_PARAM;
            filter.phoneParam = PHONE_PARAM;
            filter.processed = -1;
            filter.orders = new ArrayList(); 
 
            personalTask = taskManager.getTaskList( filter, new Page( 1, 1 ) ).get( 0 );
 
            message = new StringBuffer( 300 );
            message.append( personalTask.getStreet() );
            message.append( " " );
            message.append( personalTask.getHouse() );
            message.append( " " );  
            message.append( personalTask.getFlat() );
            message.append( " " );  
            message.append( personalTask.getContract() );  
 
            new MailMsg( setup ).sendMessageEx( "bill@ufanet.ru;disp@ufanet.ru;setks@ufanet.ru", "Оплатил отключенный", message.toString(), "text/plain" );
 
            print( "Send message Pay in disconnect process" );
 
        } 
    }
}
// подключение должника
else if( task.getTypeID() == CONNECT_TASK )
{
      if( !dolgGroup )     
     {
           report.append( cu.getContractTitle( cid, true ) );
           report.append( " => у договора нет группы долг\n" );
 
           return; 
     }
 
     for( ContractService service : services )
     {
          // закрытие абонплаты  "долг"
          if( service.getServiceID() == SERVICE_PAY_DOLG )
          {
                 service.setDate2( beforeDay );
                 serviceManager.updateContractService( String.valueOf( service.getID() ), service );
          }  
     } 
 
     // открытие абонплаты
    cs = new ContractService();
    cs.setContractID( cid );
    if( pensGroup )
    {
       cs.setServiceID( SERVICE_PAY_PENS );
    }
    else
    {
       cs.setServiceID( SERVICE_PAY );
    }
    cs.setDate1( task.getExecuteDate() );
    cs.setComment( "Установлена скриптом" );
 
    serviceManager.updateContractService( "new", cs );
 
    // снятие расхода за повторное подключение
    charge = new Charge();
    charge.setContractID( cid ); 
    charge.setChargeTypeID( RECONNECT_CHARGE );
    charge.setSumma( RECONNECT_SUM );
    charge.setDate( task.getExecuteDate() );
    charge.setComment( "Установлена скриптом" );
 
    chm.updateCharge( "new", charge );
 
    print( "setting charge" );
 
    bu.updateBalance( task.getExecuteDate(), cid );
 
    // сброс группы "Должник" 
    cpu.unsetGroup( cid,  GROUP_DOLG  ); 
    cpu.deleteDateParam( cid,  DEBT_FIX_PARAM );
}
 
task.setProcessed( true );

Когда клиент отключен за долг у него устанавливается абонплата Долг с нулевой ценой. Разумного требованию этому нет, сделано с точки зрения аналитики базы. При обработке задач скрипт выдает отчет по ошибкам и нестыковкам оператору.

Личные инструменты