5.2. Динамические программы

Динамические программы - это бонусные программы логика реализации которых основана на динамических Java классах. То есть, если вы хотите свой тип бонусной программы, то самым простым способом будет - это реализовать ее через динамические программы.

Для наглядности сейчас будет описан пример создания ДБП. Допустим нам нужна ДБП которая будет на каждый приход договора начислять бонусы, причем начисление зависит от некоторого коэффициента, который будет зависеть от кол-ва лет с момента создания договора, и начисление бонусов будем совершать только на первые два прихода сумма которых более 100р.

1) Создадим динамический класс и обязательно отнаследуем его от BonusProgramDynamicBase. Реализуем методы getTitle и accrualOfBonusImpl, первый соответственно возвращает название программы(оно будет отображаться в клиенте), а второй метод занимается начислением или/и расчетом некоторых значений(все зависет от ваших фантазий) для договоров. В нашем случае нам нужно реагировать на событие прихода платежа, потому мы еще переопределим метод onEvent.

Данный код можете использовать в качестве заготовки для вашего дин. кода( само собой изменив перед этим название класса и пакет ).

package ru.bitel.bgbilling.bonus.myPrograms;

import java.sql.Connection;
import java.sql.SQLException;

import ru.bitel.bgbilling.common.BGException;
import ru.bitel.bgbilling.kernel.event.Event;
import ru.bitel.bgbilling.plugins.bonus.common.bean.*;
import ru.bitel.bgbilling.server.util.Setup;
import ru.bitel.common.sql.ConnectionSet;

public class FirstProgram    extends BonusProgramDynamicBase
{
 @Override
 public String getTitle()
 {
  return "Первая программа";
 }

 @Override
 public void accrualOfBonusImpl( Connection con, BonusProgram program )     throws SQLException, BGException
 {
 }

 @Override
 public void onEvent( Event event, Setup setup, ConnectionSet set )     throws Exception
 {
 }

 @Override
 public List<String> getWebBonusStrings(Connection con, BonusContractProgram program )
 {
   return null;
 }

}

2) Далее заполним все методы, в результате у нас готовый дин. класс реализующий всю нашу логику. BonusProgramDynamicBase содержит основные методы для работы с бонусами, не забывайте ими пользоваться.

package ru.bitel.bgbilling.bonus.myPrograms;

import java.math.BigDecimal;
import java.sql.Connection;
import java.sql.SQLException;
import java.util.Calendar;
import java.util.Date;
import java.util.List;

import ru.bitel.bgbilling.common.BGException;
import ru.bitel.bgbilling.kernel.contract.api.common.bean.Contract;
import ru.bitel.bgbilling.kernel.contract.api.server.bean.ContractDao;
import ru.bitel.bgbilling.kernel.contract.balance.server.event.PaymentEvent;
import ru.bitel.bgbilling.kernel.event.Event;
import ru.bitel.bgbilling.kernel.module.common.bean.User;
import ru.bitel.bgbilling.plugins.bonus.common.bean.BonusContractProgram;
import ru.bitel.bgbilling.plugins.bonus.common.bean.BonusProgram;
import ru.bitel.bgbilling.plugins.bonus.common.bean.BonusProgramDynamicBase;
import ru.bitel.bgbilling.server.util.Setup;
import ru.bitel.common.Utils;
import ru.bitel.common.sql.ConnectionSet;
import bitel.billing.common.TimeUtils;

public class FirstProgram    extends BonusProgramDynamicBase
{
 @Override
 public String getTitle()
 {
  return "Первая программа";
 }

 @Override
 public void accrualOfBonusImpl( Connection con, BonusProgram program )     throws SQLException, BGException
 {
  Calendar now = Calendar.getInstance();
  // Производить расчеты будем только первого числа месяца. Это имеет смысл только если планировщик настроен на запуск каждый день.
  if( now.get( Calendar.DAY_OF_MONTH ) != 1 ) 
   return;
  
  ContractDao contractDao = new ContractDao( con, User.USER_SERVER );
  for( int contractId : bonusDao.getContractsOfThisProgram( program.getId(), new Date() ) )//пробегаемся по всем договорам с этой программой
  {
   if( !bonusDao.pluginInclude( contractId ) )// перед этим убедимся, что плагин у договора все еще включен.
   {
    continue;
   }
   map = bonusDao.getDataProgramOfContract( program.getId(), contractId );// инициализируем мап данными обрабатываемого договора.
   Contract contract = contractDao.get( contractId );
   // получим кол-во лет с момента действия договора( это не точный способ, так как високосные года не будут учтены, но думаю для примера пойдет )
   int year = TimeUtils.hourDelta( TimeUtils.convertDateToCalendar( contract.getDateFrom() ), Calendar.getInstance() ) / 24 / 365;
   int percent = 0;
   switch( year )// Тут мы выставляем процент. Более одного года = 5%, > 3 лет = 10%, > 5 лет = 15%
            {
    case 1:
     percent = 5;
     break;
    case 3:
     percent = 10;
     break;
    case 5:
     percent = 15;
     break;
   }
   if( percent > 0 )
   {// занесем посчитанный процент
    map.put( "percent", Integer.toString( percent ) );
   }
   map.put( "count", "0" );// обнулим месячный счетчик
   updateProgramData( program.getId(), contractId, map );// и сохраним изменения.
  }
 }

 @Override
 public void onEvent( Event event, Setup setup, ConnectionSet set )     throws Exception
 {
  // настоятельно рекомендую вызывать метод у родителя, в нем производятся некоторые установки.
  super.onEvent( event, setup, set );
  // Нас интересуют только события прихода платежа.
 if( event instanceof PaymentEvent && bonusDao.pluginInclude( event.getContractId() ) )
  {
   // получает все экземпляры программ
   List<BonusContractProgram> list = super.getContractPrograms( this.getClass(), new Date() );
   for( BonusContractProgram contractProgram : list )
   {
     BonusProgram program = contractProgram.getProgram();
     BigDecimal perc = Utils.parseBigDecimal( getParam( "percent", program.getId() ), BigDecimal.ZERO );
     int count = Utils.parseInt( getParam( "count", program.getId() ) );
     if( count < 3 && perc.compareTo( BigDecimal.ZERO ) == 1 )// Проверим чтобы кол-во начислений было меньше 3 раз.
     {
     // Получили сумму бонусов. Предупреждаю! Данная сумма получена без учета вашего курса бонус->валюта.
      BigDecimal sum = ((PaymentEvent)event).getPayment().getSum().multiply( perc.divide( new BigDecimal( 100 ) ) );
      updatePaymnent( sum, program );// И совершаем начисление бонусов.
      map.put( "count", Integer.toString( count + 1 ) );
      updateProgramData( program.getId() );// Увеличим счетчик кол-ва начислений и сохраним их.
     }
   }
  }
 }


  @Override
  public List<String> getWebBonusStrings(Connection con, BonusContractProgram program )
  { 
    ArrayList<String> list = new ArrayList<String>();
    contractId = program.getContractId();
    bonusDao = new BonusDao( con );
// Тут по желанию можно вывести данные, которое будут отображаться в ЛК.

    return list;
  }
}

3) Далее необходимо прописать параметр dinamicBonusPrograms( если не был прописан до этого ) в конфигураторе плагина( Плагины->Настройки ), а в качестве значения прописать полные имена ваших дин. классов используемых для дин. программ, через запятую. В нашем случае еще понадобиться наш класс повесить на событие прихода платежа( Сервис->Автоматизация->Функции скриптов поведения ).

В нашем случае оно будет имет следующий вид: dinamicBonusPrograms=ru.bitel.bgbilling.bonus.myPrograms.FirstProgram. Допустим если вы написали там же еще вторую дин. программу с названием SecondProgram, то параметр будет иметь следующий вид:dinamicBonusPrograms=ru.bitel.bgbilling.bonus.myPrograms.FirstProgram,ru.bitel.bgbilling.bonus.myPrograms.SecondProgram

4) Далее стандартным способом создаете бонусную программу, просто вместо операционного типа выберите ваш тип. Далее, так же стандартным способом, добавляете вашу программу на договора.