Управление статусом договора по состоянию баланса
Материал из BiTel WiKi
Предлагаю вариант управления начислением абонплаты в зависимости от баланса.
Всего используется два скрипта:
1. При наступлении события по таймеру (например, каждое первое число в 00:00) необходимо определить сумму необходимую для списания абонплаты и сравнить ее с балансом договора. Если средств недостаточно, то изменить статус договора на "Приостановлен" (в этом состоянии абонплата не начисляется)
2. При наступлении события "Поступление платежа", если договор в состоянии "Приостановлен", то проверяется также как и в первом случае достаточность средств. Если денег хватает, то статус изменяется на "Активен" и производится начисление абонплат.
1.
import bitel.billing.server.contract.bean.*; import bitel.billing.server.tariff.bean.*; import bitel.billing.server.script.event.*; import bitel.billing.server.service.bean.*; import bitel.billing.server.tariff.*; import bitel.billing.server.util.*; import bitel.billing.server.npay.*; import bitel.billing.server.task.bean.RunTaskData; import bitel.billing.server.task.bean.RunTaskDataManager; import java.util.*; // Если это таймер на начало месяца, в п.1, иначе выход // соответственно не забываем добавить параметр запуска в задачу "flag=1" // (вместо 1 подставляем любое число, но оно же должно быть в следующем условии =) if (event.getFlag() != 1 ) { return; } // номер модуля NPAY NPAY_MID = 4; cid = event.getContractID(); cstm = new ContractStatusManager( con ); // округляем день (на 00:00:00) события egt = event.getGenerateTime(); df = egt.clone(); df.clear(); df.set(egt.get(Calendar.YEAR), egt.get(Calendar.MONTH), egt.get(Calendar.DAY_OF_MONTH)); // получаем день, предыдущий событию dt = df.clone(); dt.roll(Calendar.DAY_OF_YEAR, -1); db = df.clone(); db.roll(Calendar.MONTH, -2); //print( df ); //print( dt ); //print( db ); // определяем текущий статус договора StatusContractList = cstm.getStatusList(cid, event.getGenerateTime()); if (StatusContractList.size() == 0) return; StatusContract = StatusContractList.get(0); print("StatusContract.getStatus():"+StatusContract.getStatus() ); contract = new ContractManager(con).getContractByID(cid); contract_groups = contract.getGroups(); // выбираем только нужную группу договоров (нам нужна только 1 группа) if ((contract_groups & (1L)) > 0) { internet = 1; } else { internet = 0; } if( StatusContract.getStatus() == 0 && internet == 1) { // 1. определить нужно ли приостанавливать договор (если есть абонплата и баланс меньше лимита) // 1.1 определить баланс и лимит: если баланс > лимита - выход, если меньше, то см. п.1.2 ctm = new ContractTariffManager( con ); csm = new ContractServiceManager( con ); cu = new ContractUtils( con ); cm = new ContractManager( con ); cn = cm.getContractByID( cid ); tts = cm.getRealtimeTariffTreeSet( cid, df, "npay", NPAY_MID, true ); msu = new ModuleAndServiceUtils( con ); bu = new BalanceUtils( con ); tpm = new TariffPlanManager(con); // получаем текущий баланс (с учетом данного платежа) balance = bu.getBalance(event.getGenerateTime(), cid); limit = cn.getLimit(); fbm = cn.getFakeBalanceMode(); bm = cn.getBalanceMode(); print ( "balance = "+balance ); print ( "limit = "+limit ); print ( "fbm = "+fbm+"; bm = "+bm ); // 1.2 получить наработку для всех услуг типа "Абонплата" и если она больше 0, то в п.2 иначе выход totalCost = 0d; /* Для версии 4.5 начало */ // получаем список услуг serviceList = csm.getContractServiceList( cid, df ); for( Iterator it = serviceList.iterator(); it.hasNext(); ) { service = (ContractService)it.next(); sid = service.getServiceID(); mid = msu.getService(sid).getModuleId(); // print ( "Service.title "+msu.getService(sid).getTitle() ); // print ( "Module.title "+msu.getModule(mid).getTitle() ); // Если услуга относится к модулю "NPay" if( mid == NPAY_MID ) { /* конец */ /* То же, но для версии 4.6 начало */ som = new ServiceObjectManager(con,NPAY_MID); serviceList = som.getServiceObjectList( cid, -1 , -1, -1); for( Iterator it = serviceList.iterator(); it.hasNext(); ) { service = (ServiceObject)it.next(); if ( TimeUtils.dateInRange( TimeUtils.convertCalendarToDate( egt ), service.getDate1(), service.getDate2() ) ) { sid = service.getServiceId(); /* конец */ // считаем сумму абонплаты // перебор тарифов в порядке позиций сначала персональных, потом глобальных for( TariffModuleTree tree : tts.getTreeList( df ) ) { req = new TariffRequest(); req.setRequestParam( "action", "calculate" ); req.setRequestParam( "sid", sid ); // считаем абонплату пропорционально количеству дней до конца месяца(действует для тарифов "Пропорционально периоду") // всего дней в месяце req.setRequestParam( "month_days", df.getActualMaximum(Calendar.DAY_OF_MONTH) ); // всего дней до конца месяца req.setRequestParam( "period_days", df.getActualMaximum(Calendar.DAY_OF_MONTH) - df.get(Calendar.DAY_OF_MONTH) + 1 ); tree.processRequest( req ); cost = (Double) req.getResponseParam( "cost" ); // print ( "cost:" + cost ); if( req.wasAccepted() && cost != null ) { print( cost ); totalCost += cost; break; } } } } print ("totalCost = "+totalCost); if ( totalCost <= 0 || balance-totalCost >= limit) { print ( "Все ОК!" ); return; } // 2. Приостановить договор. установить статус "Приостановлен" if (StatusContract.getDate1().compareTo(df) < 0) { StatusContract.setDate2(dt); cstm.setContractStatus(StatusContract); } else { // или удаляем если дата начала больше даты окончания cstm.deleteStatus(StatusContract.getId()); } //создаем новое сосотояние ncs = new ContractStatus(); ncs.setContractId(cid); ncs.setDate1(df); ncs.setStatus(4); cstm.changeStatus(ncs, 1, true); // 1 - код пользователя, от лица которого производится изменение состояния StatusContract = cstm.getStatus(cid, df); // print("cm.getStatus("+df+"):"+StatusContract.getStatus() ); }
Пересчет абонплаты производится с помощью стандартной задачи для всех договоров. Только учтите, что задача "расчет абонплаты" должен запускаться после таймера с вышеуказанным скриптом с учетом времени его выполнения.
2.
import bitel.billing.server.contract.bean.*; import bitel.billing.server.tariff.bean.*; import bitel.billing.server.script.event.*; import bitel.billing.server.service.bean.*; import bitel.billing.server.tariff.*; import bitel.billing.server.util.*; import bitel.billing.server.npay.*; import bitel.billing.server.task.bean.RunTaskData; import bitel.billing.server.task.bean.RunTaskDataManager; import java.util.*; NPAY_MID = 4; cid = event.getContractID(); cm = new ContractStatusManager( con ); // округляем день (на 00:00:00) платежа egt = event.getGenerateTime(); df = egt.clone(); df.clear(); df.set(egt.get(Calendar.YEAR), egt.get(Calendar.MONTH), egt.get(Calendar.DAY_OF_MONTH)); // получаем день, предыдущий платежу dt = df.clone(); dt.roll(Calendar.DAY_OF_YEAR, -1); // получаем дату начала месяца db = df.clone(); db.set(Calendar.DAY_OF_MONTH, 1); // определяем текущий статус договора StatusContractList = cm.getStatusList(cid, event.getGenerateTime()); if (StatusContractList.size() == 0) return; StatusContract = StatusContractList.get(0); print("StatusContract.getStatus():"+StatusContract.getStatus() ); // если договор приостановлен, то определяем размер абонплаты для снятия и сравниваем с балансом после платежа if( StatusContract.getStatus() == 4 ) { ctm = new ContractTariffManager( con ); csm = new ContractServiceManager( con ); cu = new ContractUtils( con ); cman = new ContractManager( con ); cn = cman.getContractByID( cid ); tts = cman.getRealtimeTariffTreeSet( cid, df, "npay", NPAY_MID, true ); msu = new ModuleAndServiceUtils( con ); bu = new BalanceUtils( con ); // получаем текущий баланс (с учетом данного платежа) balance = bu.getBalance(event.getGenerateTime(), cid); limit = cn.getLimit(); fbm = cn.getFakeBalanceMode(); bm = cn.getBalanceMode(); print ( "balance = "+balance ); print ( "limit = "+limit ); print ( "fbm = "+fbm+"; bm = "+bm ); totalCost = 0d; /* Для версии 4.5 начало */ // получаем список услуг serviceList = csm.getContractServiceList( cid, df ); for( Iterator it = serviceList.iterator(); it.hasNext(); ) { service = (ContractService)it.next(); sid = service.getServiceID(); mid = msu.getService(sid).getModuleId(); // print ( "Service.title "+msu.getService(sid).getTitle() ); // print ( "Module.title "+msu.getModule(mid).getTitle() ); // Если услуга относится к модулю "NPay" if( mid == NPAY_MID ) { /* конец */ /* То же, но для версии 4.6 начало */ serviceList = som.getServiceObjectList( cid, -1 , -1, -1); for( Iterator it = serviceList.iterator(); it.hasNext(); ) { service = (ServiceObject)it.next(); if ( TimeUtils.dateInRange( TimeUtils.convertCalendarToDate( egt ), service.getDate1(), service.getDate2() ) ) { sid = service.getServiceId(); /* конец */ // считаем сумму абонплаты // перебор тарифов в порядке позиций сначала персональных, потом глобальных for( TariffModuleTree tree : tts.getTreeList( df ) ) { req = new TariffRequest(); req.setRequestParam( "action", "calculate" ); req.setRequestParam( "sid", sid ); // считаем абонплату пропорционально количеству дней до конца месяца(действует для тарифов "Пропорционально периоду") // всего дней в месяце req.setRequestParam( "month_days", df.getActualMaximum(Calendar.DAY_OF_MONTH) ); // всего дней до конца месяца req.setRequestParam( "period_days", df.getActualMaximum(Calendar.DAY_OF_MONTH) - df.get(Calendar.DAY_OF_MONTH) + 1 ); tree.processRequest( req ); cost = (Double) req.getResponseParam( "cost" ); print ( "cost:" + cost ); if( req.wasAccepted() && cost != null ) { print( cost ); totalCost += cost; break; } } } } print (totalCost+" : "+balance+" - "+limit); // если денег на продление услуг хватает, то меняем статус договора и даем задание на обсчет услуг с абонплатой if (totalCost > 0 && totalCost < (balance - limit)) { //обновляем дату окончания предыдущего состояния if (StatusContract.getDate1().compareTo(df) < 0) { StatusContract.setDate2(dt); cm.updateStatus(StatusContract); } else { // или удаляем если дата начала больше даты окончания cm.deleteStatus(StatusContract.getId()); } //создаем новое сосотояние ncs = new ContractStatus(); ncs.setContractId(cid); ncs.setDate1(df); ncs.setStatus(0); cm.changeStatus(ncs); StatusContract = cm.getStatus(cid, df); print("cm.getStatus("+df+"):"+StatusContract.getStatus() ); // запускаем пересчет абонплат для договора cid taskData = new RunTaskData(new Recalculator(NPAY_MID, db, "", 0, ""+cid)); rtdm = new RunTaskDataManager(con); rtdm.addTask(taskData); } }
Обратите внимание на изменения по работе с услугами модуля NPAY в версии 4.6
Если есть вопросы по скриптам, пишите в личку