Синхронизация услуг договора в соответствии с тарифными планами
Материал из 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" ;//не синхронизировать услуги. }