Синхронизация услуг договора в соответствии с тарифными планами

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

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

Скрипт добавляет/изменяет услуги и синхронизирует периоды действия услуг автоматически в соответствии с услугами используемыми в тарифных планах договора. Его можно повесить на событие Изменение/удаление тарифного плана, и не задумываться, какие услуги используется в тарифах. Алгоритм коротко таков: Скрипт находит все услуги используемые во всех тарифных планах договора, и запоминает их периоды действия, получаем наборы

sid1 date1_tariff1_with_sid1 date2_tariff_with_sid1
     ...
     date1_tariffN_with_sid1 date2_tariffN_with_sid1
...
sidN date1_tariff1_with_sidN date2_tariff_with_sidN
     ...
     date1_tariffN_with_sidN date2_tariffN_with_sidN

из этих наборов периодов отбрасываем пересекающиеся, и в соответсвии с результатом обновляем существующие услуги договора. Удобно!

upd 14.04.2010 Добавлен метод notSynchronizeServices(sids) ; в котором указывается список услуг, через запятую, которую не надо синхронизровать. Добавлено добавление записи(в случае необходимости) для договора, в таблицу contract_module для модуля NPay (если имеются услуги NPay, которые нужно синхронизировать). Добавлено по причине того что, на момент написания скрипта, при отсутствии записи в contract_module для NPay абонплата списывалась в полном объеме без учета статусов.


import java.sql.*;
import java.util.*;
import java.math.* ;
import bitel.billing.server.contract.bean.*;
import bitel.billing.server.tariff.*;
import bitel.billing.server.util.*;
import bitel.billing.common.KernelConst; 
import bitel.billing.server.npay.bean.*;
import bitel.billing.server.script.bean.event.*;
import bitel.billing.server.npay.*;
 
 
public void onEvent( event, setup, con, conSlave ) 
{
	//print( event ) ;
	int NPAY_MID = 8 ;
	SidsSynchroManager sidman = new SidsSynchroManager() ;
	int cid = event.getContractID() ;
	//sidman.setDebugMode(0) ;
	sidman.setDeleteNonNeeded(1) ;
	sidman.setNpayMid(NPAY_MID) ;
	sidman.notSynchronizeServices("11,21,41,42,44");
	sidman.synchronizeServices( cid, con ) ;
 
}
 
 
 
//Класс синхронизации услуг
public class SidsSynchroManager {
 
	public SidsSynchroManager()
	{
		vSidsData = new Vector() ;
		vNpaySidsData = new Vector() ;
		GregorianCalendar cal = new GregorianCalendar() ;
		cal.set(1970, 0, 1 ) ; 
		_DATE1_NULL = cal.getTime() ;
		cal.set(2020, 0, 1 ) ; 
		_DATE2_NULL = cal.getTime() ;
	}
 
	public void setNpayMid( int npayMid)
	{
		NPAY_MID = npayMid ;
	}
	public void setDebugMode(int debug)
	{
		_DEBUG = debug ;
	}
	//Не синхронизировать список услуг. через запятую.
	public void notSynchronizeServices( String sids ) 
	{
		noSynchroList = sids ;
	}
	public void setDeleteNonNeeded( int dnn)//0 - Не удалять не нужные сервисы, 1 - удалять
	{
		deleteNonNeeded = dnn ;
	}
	//Точка входа. Синхронизирует сервисы договора
	public void synchronizeServices( int cid, Connection con )
	{
		vSidsData.clear() ;
		vNpaySidsData.clear() ;
		//Выбираем глобальные тарифы, и запоминаем периоды действия их услуг
		String SQL = "SELECT tree_id, (SELECT t.title FROM tariff_plan AS t WHERE t.id=ct.tpid) as title," +
			"ct.date1, ct.date2 FROM contract_tariff AS ct LEFT JOIN tariff_tree_link as ttl ON ct.tpid=ttl.tpid " +
			"WHERE ct.cid=" + cid ;
		//print(SQL) ;
		PreparedStatement ps = con.prepareStatement( SQL );
		ResultSet rs = ps.executeQuery();
		while ( rs.next() )
		{
			int tree_id = rs.getInt("tree_id") ;
			String title = rs.getString("title") ;
			Date date1 = rs.getDate("ct.date1") ;
			Date date2 = rs.getDate("ct.date2") ;
			if ( _DEBUG == 1 ) {
				print ("tree_id=" + tree_id + " title=" + title + " date1=" + TimeUtils.format(date1, "yyyy-MM-dd") + " date2=" 
					+ TimeUtils.format(date2, "yyyy-MM-dd") ) ;
			}
			findServicesForTariff( tree_id, 0, date1, date2, con) ;//Сохраняем услуги в набор
		}
		//Выбираем персональные тарифы, и запоминаем периоды действия их услуг
		SQL = "SELECT tree_id, title, date1, date2 FROM contract_tree_link WHERE cid=" + cid ;
		ps = con.prepareStatement( SQL );
		rs = ps.executeQuery();
		if ( _DEBUG == 1 ){
			print("personals") ;
		}
		while ( rs.next() )
		{
			int tree_id = rs.getInt("tree_id") ;
			String title = rs.getString("title") ;
			Date date1 = rs.getDate("date1") ;
			Date date2 = rs.getDate("date2") ;
			if( _DEBUG == 1 )	{
				print ("tree_id=" + tree_id + " title=" + title + " date1=" + TimeUtils.format(date1, "yyyy-MM-dd") + " date2=" 
					+ TimeUtils.format(date2, "yyyy-MM-dd") ) ;
			}
			findServicesForTariff( tree_id, 0, date1, date2, con ) ;
		}
 
 
		//Для каждой услуги синхронизируем периоды действия, отбрасывая пересекающиеся периоды
		calcActualServicePeriods( vSidsData ) ;
		//Тоже самое для услуг Абонплат.
		calcActualServicePeriods( vNpaySidsData ) ;
 
		//Выводим в вывод, список услуг, так, как они будут заведены. Здесь не могут быть пересекающихся периодов для одной услуги.
		print( "Services must be synchronized with folowing date periods") ;
		printInternalDataForContract( cid ) ;
 
		//Обновляем услуги договора
		updateSynchronizedServices( cid, 0, con )		 ;
		//Обновляем услуги абонплат договора
		updateSynchronizedServices( cid, NPAY_MID, con )		 ;
	}
 
	public void printInternalDataForContract( int cid )
	{
		print("cid="+cid) ;
		print("SERVICES:") ;
		for( int i = 0; i<vSidsData.size(); i++)
		{
			vSidsData.get(i).printData() ;
		}
		print("NPAY SERVICES:") ;
		for( int i = 0; i<vNpaySidsData.size(); i++)
		{
			vNpaySidsData.get(i).printData() ;
		}
 
	}
 
	private void updateSynchronizedServices( int cid, int nPayMid, Connection con )
	{
		Vector vSids = vSidsData ;
		String dateNow = TimeUtils.format(new GregorianCalendar(), "yyyy-MM-dd hh:mm:ss") ;
		if ( nPayMid == 0 ){
			SQLsel = "SELECT id, date1, date2 FROM contract_service WHERE cid=" + cid + " AND sid=? ORDER BY date1, date2" ;
			SQLupd = "UPDATE contract_service SET date1=?, date2=?, comment='Service synchronize(update from " + 
				dateNow + ")' WHERE id=?" ;
			SQLins = "INSERT INTO contract_service (cid, sid, date1, date2, comment ) VALUES ( " + 
				cid + ", ? , ? , ?, 'Service 	synchronize(insert from " + dateNow + ")')" ;
			SQLdel = "DELETE FROM contract_service WHERE id=?" ;			
			String sidslist = getServiceList(vSidsData) ;
			if ( sidslist == "" ) { sidslist = "-1" ;}
			SQLdelNonNeeded = "DELETE FROM contract_service WHERE cid=" + cid + " AND sid NOT IN (" + 
				sidslist +") AND sid NOT IN (" + noSynchroList + ")" ;
		}
		else{
			vSids = vNpaySidsData ;
			SQLsel = "SELECT id, date1, date2 FROM npay_service_object_" + nPayMid + " WHERE cid=" + cid + " AND sid=? ORDER BY date1, date2" ;
			SQLupd = "UPDATE npay_service_object_" + nPayMid + " SET date1=?, date2=?, comment='Service synchronize(update from " + 
				dateNow + ")' WHERE id=?" ;
			SQLins = "INSERT INTO npay_service_object_" + nPayMid + " (cid, sid, date1, date2, comment ) VALUES ( " + 
				cid + ", ? , ? , ?, 'Service 	synchronize(insert from " + dateNow + ")')" ;
			SQLdel = "DELETE FROM npay_service_object_" + nPayMid + " WHERE id=?" ;			
			String sidslist = getServiceList(vNpaySidsData) ;
			if ( sidslist == "" ) {
				 sidslist = "-1" ;
			} else { //Список услуг абонплат для синхронизации не пуст. Добавляем в таблицу contract_module запись при необходимости
				SQLtmp = "SELECT mid FROM contract_module WHERE cid=" + cid + " AND mid=" + NPAY_MID ;
				PreparedStatement ps = con.prepareStatement( SQLtmp );
				rs = ps.executeQuery() ;
				if( !rs.next() )
				{
					SQLtmp = "INSERT INTO contract_module (cid,mid) VALUES (" + cid + "," + NPAY_MID + ")" ;
					ps = con.prepareStatement( SQLtmp );
					ps.executeUpdate() ;
				}
			}
 
			SQLdelNonNeeded = "DELETE FROM npay_service_object_" + nPayMid + " WHERE cid=" + cid + " AND sid NOT IN (" + sidslist + ") AND sid NOT IN (" + noSynchroList + ")" ;
		}
 
 
 
		if ( deleteNonNeeded == 1 )
		{
			print( SQLdelNonNeeded ) ;
			PreparedStatement psDelOther = con.prepareStatement( SQLdelNonNeeded ) ;
			psDelOther.executeUpdate( SQLdelNonNeeded ) ;
		}
		PreparedStatement ps = con.prepareStatement( SQLsel );
		PreparedStatement psUpd = con.prepareStatement( SQLupd );
		PreparedStatement psIns = con.prepareStatement( SQLins );
		PreparedStatement psDel = con.prepareStatement( SQLdel );
 
		ResultSet rs ;
		SidsData sd ;
		Date date1, date2 ;
		for( int i = 0; i<vSids.size(); i++)
		{
			sd = vSids.get(i) ;
			int sid = sd.sid ;
			ps.setInt(1, sid) ;
			rs = ps.executeQuery() ;
			j = 0 ; 
			while( rs.next() )
			{
				int id = rs.getInt("id") ;
				date1 = rs.getDate("date1") ;
				date2 = rs.getDate("date2") ;
				//Чтобы при сравнении дат equals не давал exeption, подменяем значения null
				//print( "date1=" + TimeUtils.format(date1, "yyyy-MM-dd") + " date2=" + TimeUtils.format(date2, "yyyy-MM-dd")) ;
				if ( date1 == null ){
					date1 = _DATE1_NULL.clone() ;
				}
				if ( date2 == null ){
					date2 = _DATE2_NULL.clone() ;
				}
				//Лишняя услуга, удаляем
				if ( j >= sd.vDate1.size() )
				{
					psDel.setInt( 1, id ) ;
					psDel.executeUpdate() ;
					if (_DEBUG==1){
						print( "Delete service with id=" + id + " for service="+sid + 
					" date1=" + TimeUtils.format(date1, "yyyy-MM-dd") + " date2=" + TimeUtils.format(date2, "yyyy-MM-dd")) ;
						print( "psDel=" + psDel ) ;
					}
					continue ;
				}
 
 
				if ( !date1.equals(sd.vDate1.get(j)) || !date2.equals(sd.vDate2.get(j)) )
				{
					//Возвращаем значения null при необходимости  для обновления инфы в БД
					if ( sd.vDate1.get(j).equals( _DATE1_NULL) ){
						sd.vDate1.set( j, null ) ;
					}
					if ( sd.vDate2.get(j).equals( _DATE2_NULL) ){
						sd.vDate2.set( j, null ) ;
					}
					psUpd.setInt( 3, id ) ;
					psUpd.setDate( 1, sd.vDate1.get(j) ) ;
					psUpd.setDate( 2, sd.vDate2.get(j) ) ;
					psUpd.executeUpdate() ;
					if (_DEBUG==1){
						print( "Update service with id=" + id + " for service="+sid + " previous date1=" + 
						TimeUtils.format(date1, "yyyy-MM-dd") + " date2=" + TimeUtils.format(date2, "yyyy-MM-dd")) ;
						print( "psUpd=" + psUpd ) ;
					}
				}
				j++ ;
			}//end while
 
			for( ; j < sd.vDate1.size(); j++)
			{
				if ( sd.vDate1.get(j).equals( _DATE1_NULL) ){
					sd.vDate1.set( j, null ) ;
				}
				if ( sd.vDate2.get(j).equals( _DATE2_NULL) ){
					sd.vDate2.set( j, null ) ;
				}
				psIns.setInt(1, sid ) ;
				psIns.setDate( 2, sd.vDate1.get(j) ) ;
				psIns.setDate( 3, sd.vDate2.get(j) ) ;
				if (_DEBUG==1){
					print( "Insert new service="+sid + " date1=" + 
					TimeUtils.format(date1, "yyyy-MM-dd") + " date2=" + TimeUtils.format(date2, "yyyy-MM-dd")) ;
					print( "psIns" + psIns ) ;
				}
				psIns.executeUpdate() ;
			}
 
		}
 
	}
 
	private String getServiceList(Vector vSids)
	{
		String sids = "";
		int i ;
		for( i = 0; i<vSids.size()-1; i++)
		{
			sids += "" + vSids.get(i).sid + "," ;
		}
		if ( i < vSids.size()) {
			sids += "" + vSids.get(i).sid ;
		}
		return sids ;
	}
 
	private calcActualServicePeriods( Vector vSids )
	{
		prepareInternalData() ;
		SidsData sd ;
		Vector vDate1 = new Vector() ;
		Vector vDate2 = new Vector() ;
		Date date1 ;
		Date date2 ;
		GregorianCalendar date1cal = new GregorianCalendar() ; //Для хранения date1+1 день
		k = 0 ;
		for( int i = 0; i<vSids.size(); i++)
		{
			sd = vSids.get(i) ;
			vDate1.clear() ;
			vDate2.clear() ;
			for ( j = 0 ; j < sd.vDate1.size() ;  )//Отсеиваем ненужные периоды
			{
				date1 = sd.vDate1.get(j) ;
				date2 = sd.vDate2.get(j) ;
				if ( date1.compareTo( date2) > 0 )
				{
					print("Fatal error,  date1=" + TimeUtils.format(date1, "yyyy-MM-dd") + " > date2=" 
					+ TimeUtils.format(date2, "yyyy-MM-dd") ) ;
					print("Services was not synchronized...") ;
					return ;
				}
				for( j++ ; j<sd.vDate1.size(); j++)
				{
					date1cal.setTime( sd.vDate1.get(j) ) ;
					date1cal.roll(Calendar.DAY_OF_YEAR,-1) ;
					if ( date2.compareTo(date1cal.getTime()) < 0 ) // Нашли нужный date2 для date1 ;
					{
						break ;
					}
					else
					{
						date2 = sd.vDate2.get(j) ;
					}
				}
				vDate1.add(date1) ;
				vDate2.add(date2) ;
			}
 
			sd.vDate1.clear() ;
			sd.vDate2.clear() ;
			sd.vDate1 = vDate1.clone() ;
			sd.vDate2 = vDate2.clone() ;
 
		}
	}
 
	private prepareInternalData()//Подготовливает внутренние данные для основного алгоритма. Сортирует массивы date услуг
	{
		SidsData sd ;
		for( int i = 0; i<vSidsData.size(); i++)
		{
			sd = vSidsData.get(i) ;
			Collections.sort(sd.vDate1 ) ;
			Collections.sort(sd.vDate2 ) ;
		}
		for( int i = 0; i<vNpaySidsData.size(); i++)
		{
			sd = vNpaySidsData.get(i) ;
			Collections.sort(sd.vDate1 ) ;
			Collections.sort(sd.vDate2 ) ;
 
		}
 
	}
 
	private void addSid( int sid, Date date1, Date date2, int npayService )
	{
		String[] noSids = noSynchroList.split(",") ;
		for ( int  i = 0 ; i<noSids.length; i++ )
		{
			if ( Integer.parseInt( noSids[i].trim() ) == sid ) { return ;}
		}
		//Заменяем нулевые значения дат значениями _DATE1_NULL и DATE2_NULL соответсвенно
		if ( date1 == null ) {
			date1 = _DATE1_NULL.clone() ;
		}
		if ( date2 == null ) {
			date2 = _DATE2_NULL.clone() ;
		}
 
		Vector vSids = vSidsData;
		if ( npayService != 0 )
		{
			vSids = vNpaySidsData ;
		}
		int f = 0 ;
		SidsData sd ;
		for ( int i=0; i < vSids.size(); i++ )
		{
			sd = vSids.get(i) ;
			if (sd.sid == sid)
			{
				sd.vDate1.add(date1) ;
				sd.vDate2.add(date2) ;
				f = 1 ; break ;
			}
		}
		if ( f == 0 )
		{
			sd = new SidsData( sid ) ;
			sd.vDate1.add(date1) ;
			sd.vDate2.add(date2) ;
			vSids.add( sd ) ;
		}
	}
 
	private	void findServicesForTariff( int tree_id, int mid /*если не 0, то выборка из родительского тарифа*/, 
 		Date date1, Date date2, Connection con) 
	{
		String SQL = "SELECT id, mid, parent_tree FROM module_tariff_tree WHERE tree_id=" + tree_id ;
		if ( mid != 0 )
		{
			SQL += " AND mid=" + mid ;
		}
		//print(SQL) ;
		PreparedStatement ps = con.prepareStatement( SQL );
		ResultSet rs = ps.executeQuery() ;
 
		while ( rs.next() )
		{
			int mtreeid = rs.getInt("id") ;
			int mid = rs.getInt("mid") ;
			int parent_tree = rs.getInt("parent_tree") ;
			if (parent_tree != 0 )
			{
				findServicesForTariff( parent_tree, mid, date1, date2, con ) ;
				continue ;
			}
			findServicesForTariff2( mtreeid, mid, date1, date2, con ) ;
		}
	}
 
	private void findServicesForTariff2( int mtreeid, int mid, Date date1, Date date2, Connection con )
	{
		SQL = "SELECT type, data " + 
			"FROM mtree_node " +
			"WHERE mtree_id=" + mtreeid + " AND type IN ('service', 'multi_service', 'month_mode', 'day_mode')" ;
		PreparedStatement ps = con.prepareStatement( SQL ) ;
		ResultSet rs = ps.executeQuery() ;
		int sid = 0;
		while ( rs.next() )
		{
			String type = rs.getString("type") ;
			String data = rs.getString("data") ;
			//print ( "type=" + type + " data=" + data) ;
			if (type.equals("service") )
			{
				sid = Integer.parseInt( data ) ;
				addSid( sid, date1, date2, 0 ) ; //Добавляем найденную услугу в набор найденных услуг
 
				//print ( "sid=" + sid ) ;
			}
			else if( type.equals("multi_service") )
			{
				String[] ss = data.split("&") ;
				String[] sids = ss[1].split(",") ;
				//print ( "sids=" + ss[1] ) ;
				for ( int i=0; i < sids.length; i++ )
				{
					sid=Integer.parseInt(sids[i]) ;
					addSid( sid, date1, date2, 0 ) ; //Добавляем найденную услугу в набор найденных услуг
					//print( "sid = " + sid ) ;
				}
			}
			else if( type.equals("month_mode") )
			{
				String[] ss = data.split("%") ;
				for ( int i = 0; i < ss.length ; i++ )
				{
					String[] sids=ss[i].split("&") ;
					if ( sids[0].equals("sid") )
					{
						sid = Integer.parseInt(sids[1]) ;
						addSid( sid, date1, date2, 1 ) ; //Добавляем найденную услугу в набор найденных услуг
						break ;
					}
				}
				//print( "month_mode sid = " + sid ) ;
			}
			else if( type.equals("day_mode") )
			{
				String[] ss = data.split("%") ;
				for ( int i = 0; i < ss.length ; i++ )
				{
					String[] sids=ss[i].split("&") ;
					if ( sids[0].equals("sid") )
					{
						sid = Integer.parseInt(sids[1]) ;
						addSid( sid, date1, date2, 1 ) ; //Добавляем найденную услугу в набор найденных услуг
						break ;
					}
				}
				//print( "day_mode sid = " + sid ) ;
			}
 
		}//end while
	}
 
 
	//внутренниц класс для хранения услуги и множества дат date1 и date2 
	private	class SidsData{
		public int sid ;
		public Vector vDate1 ;
		public Vector vDate2 ;
 
		public SidsData()
		{
			sid = 0 ;
			vDate1 = new Vector() ;
			vDate2 = new Vector() ;
		}
 
		public SidsData( int s )
		{
			sid=s ;
			vDate1 = new Vector() ;
			vDate2 = new Vector() ;
		}
		public printData()
		{
			print ("sid=" + sid) ;
			for( i=0; i<vDate1.size(); i++ )
			{
				Date date1 = vDate1.get(i) ;
				Date date2 = vDate2.get(i) ;
				if ( date1.equals( _DATE1_NULL) ){
					date1 = null ;
				}
				if ( date2.equals( _DATE2_NULL) ){
					date2 = null ;
				}
 
				print (" date1=" + TimeUtils.format(date1, "yyyy-MM-dd") + " date2=" 
					+ TimeUtils.format(date2, "yyyy-MM-dd") ) ;				
			}
		}
	}
 
	private int NPAY_MID=8 ;
	private int _DEBUG=0 ;
	private int deleteNonNeeded = 0 ;
	static private Date _DATE1_NULL ;
	static private Date _DATE2_NULL ;
	private Vector vSidsData ;
	private Vector vNpaySidsData ;
	private String noSynchroList = "1000000" ;//не синхронизировать услуги.
}
Личные инструменты