Управление статусом договора по состоянию баланса

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

Перейти к: навигация, поиск

Предлагаю вариант управления начислением абонплаты в зависимости от баланса.

Всего используется два скрипта:

1. При наступлении события по таймеру (например, каждое первое число в 00:00) необходимо определить сумму необходимую для списания абонплаты и сравнить ее с балансом договора. Если средств недостаточно, то изменить статус договора на "Приостановлен" (в этом состоянии абонплата не начисляется)

2. При наступлении события "Поступление платежа", если договор в состоянии "Приостановлен", то проверяется также как и в первом случае достаточность средств. Если денег хватает, то статус изменяется на "Активен" и производится начисление абонплат.


1.

import bitel.billing.common.TimeUtils.*;
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 );
        tu  = new TimeUtils();
	tpm = new TariffPlanManager(con);	
 
 
 
	// получаем текущий баланс (с учетом данного платежа)
	balance = bu.getBalance(tu.convertCalendarToDate(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.common.TimeUtils.*;
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 );
        tu  = new TimeUtils();
	bu  = new BalanceUtils( con );
 
 
	// получаем текущий баланс (с учетом данного платежа)
	balance = bu.getBalance(tu.convertCalendarToDate(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
начало
*/
        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+" : "+balance+" - "+limit);
 
 
 
	// если денег на продление услуг хватает, то меняем статус договора и даем задание на обсчет услуг с абонплатой
 	if (totalCost > 0 && totalCost < (balance - limit)) {
		//обновляем дату окончания предыдущего состояния
		if (StatusContract.getDate1().compareTo(df) < 0) {
			StatusContract.setDate2(dt);
			cm.setContractStatus(StatusContract);
		} else {
			// или удаляем если дата начала больше даты окончания 
			cm.deleteStatus(StatusContract.getId());
		}
		//создаем новое сосотояние
		ncs = new ContractStatus();
		ncs.setContractId(cid);
		ncs.setDate1(df);
		ncs.setStatus(0);
		cm.changeStatus(ncs, 1, true);
 
		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

Если есть вопросы по скриптам, пишите в личку

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