Организация системы отслеживания и отключения КТВ должников на BGBS с использованием CRM плагина
Материал из BiTel WiKi
Внимание!!
Решение устарело, т.к. плагин CRM более не поддерживается. Для решения подобных задач рекомендуем использовать BGCRM. Организация отключения должников КТВ
Ставится задача автоматического массового выявления, оповещения и отключения должников по КТВ.
В планировщике заданий добавляем генерацию события таймера. Событие генерируется каждые сутки в 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 ); } }
Предполагаем, что событие таймера обработано в результате чего получено множество задач типа Обзвон должника.
Возможен непосредственный обзвон должников либо генерация квитанций и разнос их по квартирам. Как показывает практика, второй метод более эффективен. Для генерации квитанций подобного вида:
вы можете использовать шаблон отчета по задачам Квитанции. Для получения отчета необходимо выбрать тип шаблона над левым верхним углом таблицы задач, далее сохранить его в 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
После разнесения квитанций/обзвонов должников оператор помечает задачи выполненными, указав дату выполнения. Для реагирования на оплаты клиентов создается скрипт на событие Приход платежа. Скрипт закрывает задачи обзвона и отключения, если клиент оплатил достаточную сумму. Для уже отключенных клиентов создается задача Подключение должника. Все действия сопровождаются письмами на почтовые рассылки. Задача на отключение закрывается только в том случае, если она не принята, т.е. монтажник не выехал к клиенту.
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; } // коды групп решения задач 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 ); }
Для не оплативших в течении указанного количества дней после выполненной задачи обзвона должников создаются задачи на Отключение должника. Они могут быть распечатаны в виде нарядов монтажникам, для чего используется шаблон отчета журнала задач Отчет по подключению (файл выложен выше в статье). Этот же шаблон можно использовать при создании нарядов на подключение.
Выполненные задачи помечаются выполненными и обрабатываются оператором. При обработке задач отрабатывает скрипт для задач типа Подключение должника и Отключение должника.
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 );
Когда клиент отключен за долг у него устанавливается абонплата Долг с нулевой ценой. Разумного требованию этому нет, сделано с точки зрения аналитики базы. При обработке задач скрипт выдает отчет по ошибкам и нестыковкам оператору.