forum.bitel.ru http://forum.bitel.ru/ |
|
Рассылка напоминаний должникам http://forum.bitel.ru/viewtopic.php?f=22&t=9193 |
Страница 1 из 1 |
Автор: | grifin [ 28 апр 2014, 09:21 ] |
Заголовок сообщения: | Рассылка напоминаний должникам |
Необходимо тем, у кого задолженность больше заданной раз в неделю капать на мозги, что мол у вас задолженность, срочно оплатите. Тыкните пожалуйста носом в документацию, где написано как это сделать... Спасибо. |
Автор: | Yarlan Zey [ 28 апр 2014, 09:57 ] |
Заголовок сообщения: | Re: Рассылка напоминаний должникам |
у нас есть глобальный скрипт, который посылает смс абонентам за 1,2,5 дней до отключения. надо? |
Автор: | grifin [ 28 апр 2014, 10:25 ] |
Заголовок сообщения: | Re: Рассылка напоминаний должникам |
У нас нет "отключения", специфика бизнеса. У нас есть абоненты, которые пока им лично не позвонишь - присланные счета игнорируют. Нужно для тех, кто задолжал определенную сумму - навязчиво слать уведомления. Предел суммы вполне можно брать из лимита договора. PS. А почему "скрипт" ? штатных средств нету что-ли ? |
Автор: | Yarlan Zey [ 28 апр 2014, 10:30 ] |
Заголовок сообщения: | Re: Рассылка напоминаний должникам |
скрипт и есть штатное средство. в этом вся прелесть бг |
Автор: | Phricker [ 28 апр 2014, 11:39 ] |
Заголовок сообщения: | Re: Рассылка напоминаний должникам |
Модуль dispatch. Штатное средство на скриптах ) |
Автор: | grifin [ 28 апр 2014, 11:47 ] |
Заголовок сообщения: | Re: Рассылка напоминаний должникам |
У меня тут приятель БМВ подержанную купил. 5ку, в последнем кузове. На форум полез, там увидел обсуждение того, что она прямо не едет, народ советовал "повернуть пружины", а если не поможет - ехать к дилеру и покупать опцию "движение прямо"... во как... Движение прямо это теперь опция ) |
Автор: | vkulakov [ 29 апр 2014, 12:43 ] |
Заголовок сообщения: | Re: Рассылка напоминаний должникам |
Я для рассылки SMS написал задачу для шедулера. Раньше тоже было через скрипты, но не глобальные. Пробовал через модуль dispatch, но там много лишнего получается и нужно менять логику работы с контактными данными абонентов. Гораздо проще и эффективнее было написать задачу для шедулера. А уже в задаче можно реализовать любую логику для рассылки уведомлений. |
Автор: | grifin [ 04 май 2014, 04:15 ] |
Заголовок сообщения: | Re: Рассылка напоминаний должникам |
поделитесь вашей задачей для образца мне ? |
Автор: | nik247 [ 06 май 2014, 15:43 ] |
Заголовок сообщения: | Re: Рассылка напоминаний должникам |
Yarlan Zey писал(а): у нас есть глобальный скрипт, который посылает смс абонентам за 1,2,5 дней до отключения. надо? Если возможно, поделитесь с нами .... |
Автор: | Yarlan Zey [ 06 май 2014, 16:31 ] |
Заголовок сообщения: | Re: Рассылка напоминаний должникам |
Код: //Отправка СМС с напоминанием о приближающейся блокировке
import bitel.billing.server.contract.bean.BalanceUtils; import bitel.billing.server.contract.bean.ContractStatus; import bitel.billing.server.contract.bean.ContractStatusManager; import java.math.*; import java.sql.PreparedStatement; import java.sql.ResultSet; import ru.bitel.bgbilling.modules.npay.server.Calculator; import java.text.SimpleDateFormat; import java.net.*; import java.io.*; public void main( setup, con, conSlave ) { //Начало функции подсчета суммы оплаты за n-количество дней public static double payday (StartDate, int days) { double result = 0;//переменная в которой будет содержаться результат payday = 0; paymonth = 0; paymonthtoday = 0; String DateQuery = StartDate; for (int i = 0; i < days; i++) { //Достаем сумму списания в день на сегодня querytoday = "SELECT ROUND(SUM(REPLACE(data,'type&1%cost&',''))/EXTRACT(DAY FROM LAST_DAY('"+DateQuery+"')),3) as ap FROM module_tariff_tree as a " + "LEFT JOIN mtree_node AS b ON (a.id=b.mtree_id) " + "LEFT JOIN contract_tariff AS c ON (c.tpid=a.tree_id) " + "WHERE (a.mid=7)AND(type LIKE \"%cos%t\")AND(c.cid="+cid+")AND(c.date1<='"+DateQuery+"')AND((c.date2 is NULL)OR(c.date2>='"+DateQuery+"'))AND(type='day_cost')"; PreparedStatement psSelect = conSlave.prepareStatement(querytoday); ResultSet rs = psSelect.executeQuery(); while (rs.next()) { payday = rs.getDouble(1); } psSelect.close(); //Достаем сумму списания в месяц на сегодня querytoday = "SELECT IFNULL(ROUND(SUM(REPLACE(data,'type&0%cost&',''))/EXTRACT(DAY FROM LAST_DAY('"+DateQuery+"')),3),0) as ap FROM module_tariff_tree as a " + "LEFT JOIN mtree_node AS b ON (a.id=b.mtree_id) " + "LEFT JOIN contract_tariff AS c ON (c.tpid=a.tree_id) " + "WHERE (a.mid=7)AND(type LIKE \"%cos%t\")AND(c.cid="+cid+")AND(c.date1<='"+DateQuery+"')AND((c.date2 is NULL)OR(c.date2>='"+DateQuery+"'))AND(type='month_cost')AND(DAYOFMONTH('"+DateQuery+"')=1)"; PreparedStatement psSelect = conSlave.prepareStatement(querytoday); ResultSet rs = psSelect.executeQuery(); while (rs.next()) { paymonth = rs.getDouble(1); } psSelect.close(); result = result + payday + paymonth; SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd"); Calendar c = Calendar.getInstance(); c.setTime(sdf.parse(DateQuery)); c.add(Calendar.DATE, 1); // +1 день DateQuery = sdf.format(c.getTime()); // Новая дата } return result;//возвращаю его } //Конец функции подсчета суммы оплаты за n-количество дней //Достаем сегодняшнюю дату Date currentDate = new Date(); //Достаем параметры договоров String query = "SELECT a.id, b.value, SUBSTRING(f.title,1,1), CONCAT(e.house, '', e.frac), d.flat " + //c.summa1+c.summa2-c.summa3-c.summa4, "FROM contract AS a " + "LEFT JOIN contract_parameter_type_phone AS b ON (b.cid=a.id) " + // "LEFT JOIN contract_balance AS c ON ((c.cid=a.id) AND (c.mm=month(now())) AND (c.yy=year(now()))) " + "LEFT JOIN contract_parameter_type_2 AS d ON (d.cid=a.id) " + "LEFT JOIN address_house AS e ON (d.hid=e.id) " + "LEFT JOIN address_street AS f ON (f.id=e.streetid) " + "LEFT JOIN contract_module AS g ON (g.cid=a.id) " + "LEFT JOIN ipn_contract_status_9 AS h ON (h.cid=a.id) " + "WHERE (a.status=0) AND NOT ISNULL(b.value) AND (g.mid=9) AND (h.status=0)"; //AND ((c.summa1+c.summa2-c.summa3-c.summa4)>0) PreparedStatement contractPs = con.prepareStatement( query ); ResultSet contractRs = contractPs.executeQuery(); int cid; String ContractPhone = ""; int ksms = 0; int ksms1 = 0; int ksms5 = 0; while ( contractRs.next() ) { cid = contractRs.getInt( 1 ); //print (cid +" start"); ContractPhone = contractRs.getString( 2 ); ContractPhone = ContractPhone.replace("; ", ",");//Заменяем точку с запятой и пробел на запятую (требование СМС пилота) //balance = contractRs.getDouble( 3 ); BalanceUtils bu = new BalanceUtils( con ); BigDecimal bal = bu.getBalance(currentDate, cid); double balance = bal.doubleValue(); if (balance > 0) { Street = contractRs.getString(3); H = contractRs.getBytes(4); String House = new String(H,"cp1251"); Flat = contractRs.getString(5); Adress = Street + ". " + House + "-" + Flat; SMSText2 = ""; //Применяем к дате маску для использования в СМС-Пилоте SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd"); senddt = sdf.format(currentDate); //Забираем суммы оплат на ближайшие дни double paytoday = payday(senddt,1);//Сумма оплаты на 1 день double payto2day = payday(senddt,2);//Сумма оплаты на 2 дня //double payto3day = payday(senddt,3);//Сумма оплаты на 3 дня double payto5day = payday(senddt,5);//Сумма оплаты на 5 дней double payto6day = payday(senddt,6);//Сумма оплаты на 6 дней if (balance < payto2day && balance >=paytoday) { SMSText2 = URLEncoder.encode(" руб. До блокировки 1 день.", "UTF-8") + "&to="; ksms1++;; } /* else if (balance < payto3day && balance >= payto2day) { SMSText2 = URLEncoder.encode(" руб. До блокировки 2 дня.", "UTF-8") + "&to="; }*/ else if (balance < payto6day && balance >= payto5day) { SMSText2 = URLEncoder.encode(" руб. До блокировки 5 дней.", "UTF-8") + "&to="; ksms5++; } if (SMSText2 != "") { balance = balance - paytoday; balance = Math.rint(100.0 * balance) / 100.0; SMSSend = ""; SMSSender = ""; APIKey = ""; SMSSend = "http://smspilot.ru/api.php?send=" + URLEncoder.encode("Баланс ", "UTF-8") + URLEncoder.encode(Adress, "UTF-8") + ": " + balance + SMSText2 + ContractPhone + "&from=" + SMSSender + "&apikey=" + APIKey + "&send_datetime=" + senddt + " 05:00:00"; print ("Абонент: " + cid + " SMS: " + balance + URLDecoder.decode (SMSText2)); // -- START SENDING SMS MESSAGE -- URL url = new URL(SMSSend); URLConnection c = url.openConnection(); BufferedReader in = new BufferedReader( new InputStreamReader( c.getInputStream())); String inputLine; while ((inputLine = in.readLine()) != null) System.out.println(inputLine); in.close(); // -- END SENDING SMS MESSAGE -- ksms++; } } } print("Кол-во отправленных СМСок " +ksms ); print("Из них:"); print("1 день " +ksms1 ); print("5 дней " +ksms5 ); contractRs.close(); contractPs.close(); } |
Автор: | nik247 [ 06 май 2014, 17:30 ] |
Заголовок сообщения: | Re: Рассылка напоминаний должникам |
to Yarlan Zey Спасибо. |
Автор: | vkulakov [ 12 май 2014, 12:17 ] |
Заголовок сообщения: | Re: Рассылка напоминаний должникам |
Не знаю, пригодиться ли... Выкладываю как есть. Код: package ru.provider.sms.tasks; import java.io.UnsupportedEncodingException; import java.math.BigDecimal; import java.net.URLEncoder; import java.sql.ResultSet; import java.sql.Statement; import java.util.Calendar; import java.util.Map; import bitel.billing.server.contract.bean.BalanceUtils; import bitel.billing.server.contract.bean.ContractManager; import bitel.billing.server.npay.Calculator; import bitel.billing.server.npay.bean.ServiceObjectManager; import ru.bitel.bgbilling.server.util.ServerUtils; /** * <p>Задача рассылки уведомлений о необходимости пополнить счёт</p> * <p>Ищет абонентов, у которых на счёте недостаточно средств для оплаты следующего месяца, и отправляет им уведомления</p> * <p>Для запуска класс необходимо скомпилировать и положить к другим библиотекам на сервере биллинга, * после чего добавить задачу по аналогии с другими задачами планировщика.</p> */ public class PeriodicSmsNotifications extends AbstractSmsTask { private int npayMid; private BalanceUtils bu; private Calendar currDate; private Calendar nextDate; private Map<Integer, BigDecimal> planAccountMap; @Override public String getDescription() { return this.defaultDescription + "Рассылка SMS уведомлений о необходимости пополнить счёт."; } @Override public boolean initTask() { boolean r = super.initTask(); this.npayMid = this.taskSetup.getInt("npay.mid", 0); if (!r || this.npayMid == 0) { this.log.error("Bad task config!"); return false; } return true; } @Override public void executeTask() { this.con = this.setup.getDBConnectionFromPool(); this.currDate = Calendar.getInstance(); this.nextDate = Calendar.getInstance(); this.nextDate.set(this.currDate.get(Calendar.YEAR), this.currDate.get(Calendar.MONTH) + 1, 1); if (this.tariffTotal.isEmpty()){ this.tariffTotal = "-1"; } String query = "" + " SELECT" + " c.id AS contractId," + " c.title AS contractTitle," + " c.closesumma AS contractLimit," + " p1.val AS mobilePhone," + " p2.val AS emailAddress," + " t.cid" + " FROM" + " contract c" + " LEFT JOIN contract_parameter_type_1 p1 ON c.id = p1.cid AND p1.pid = " + this.mobilePid + " LEFT JOIN contract_parameter_type_1 p2 ON c.id = p2.cid AND p2.pid = " + this.emailPid + " LEFT JOIN contract_tariff t ON c.id = t.cid AND t.tpid IN (" + this.tariffTotal + ") AND ((NOW() BETWEEN t.date1 AND t.date2) OR (t.date1 <= NOW() AND t.date2 IS NULL))" + " WHERE" + " c.status IN (0,3,4) AND" + " c.fc = 0 AND (" + " (p1.val IS NOT NULL AND p1.val != '') OR" + " (p2.val IS NOT NULL AND p2.val != '')" + " ) AND t.cid IS NULL"; if (!this.workGroups.isEmpty()) { query += " AND ("; for (int gid : this.workGroups) { query += " (c.gr >> " + gid + ") & 1" + " AND"; } query += " 1=1)"; } if (!this.skipGroups.isEmpty()) { query += " AND !("; for (int gid : this.skipGroups) { query += " (c.gr >> " + gid + ") & 1" + " OR"; } query += " 0=1 )"; } try { new ContractManager(con); this.bu = new BalanceUtils(con); new ServiceObjectManager(con, this.npayMid); Statement stmt = con.createStatement(); ResultSet rs = stmt.executeQuery(query); String cids = ""; while (rs.next()) { cids += rs.getInt("contractId"); cids += ","; } if (cids.length() == 0) { this.log.info("Contracts not found"); return; } Calculator calculator = new Calculator(); calculator.setExecutingTime( this.nextDate ); calculator.setPreCalc(); calculator.initTask( setup, 0, "mid=" + this.npayMid ); calculator.setCids( cids.substring(0, cids.length() - 1) ); calculator.startTask(); this.planAccountMap = calculator.getCostCache().getContractAccounts(); rs.beforeFirst(); while (rs.next()) { int contractId = rs.getInt("contractId"); String contractTitle = rs.getString("contractTitle"); BigDecimal contractLimit = rs.getBigDecimal("contractLimit"); String mobilePhone = rs.getString("mobilePhone"); String emailAddress = rs.getString("emailAddress"); sendNotification(contractId, contractTitle, contractLimit, mobilePhone, emailAddress); sleep(); } stmt.close(); } catch (Exception ex) { this.log.error(ex.getMessage(), ex); } finally { ServerUtils.closeConnection(con); } } public void sendNotification(int contractId, String contractTitle, BigDecimal contractLimit, String mobilePhone, String emailAddress) throws UnsupportedEncodingException { BigDecimal balance = this.bu.getBalance(this.currDate.getTime(), contractId); BigDecimal totalCost = this.planAccountMap.get( contractId ); if (totalCost == null) { this.log.info("Договор=" + contractTitle + "; limit=" + contractLimit + "; balance=" + balance + "; totalCost=" + totalCost + "; Result=ERROR!"); return; } if (totalCost.compareTo(BigDecimal.ZERO) <= 0 || balance.subtract(totalCost).compareTo(contractLimit) >= 0) { this.log.info("Договор=" + contractTitle + "; limit=" + contractLimit + "; balance=" + balance + "; totalCost=" + totalCost + "; Result=OK!"); } else { this.log.info("Договор=" + contractTitle + "; limit=" + contractLimit + "; balance=" + balance + "; totalCost=" + totalCost + "; Result=" + totalCost.subtract(balance)); if (emailAddress != null && !emailAddress.isEmpty()) { String msg = this.emailMsgTpl; msg = msg.replaceAll("%title%", contractTitle); msg = msg.replaceAll("%balance%", balance.toString()); msg = msg.replaceAll("%result%", totalCost.subtract(balance).toString()); sendEmailNotification(emailAddress, "Недостаточный баланс", msg); } if (mobilePhone != null && !mobilePhone.isEmpty()) { String mobilePhoneMod = mobilePhone.replaceAll("\\D+", ""); String msg = this.mobileMsgTpl; msg = msg.replaceAll("%title%", contractTitle); msg = msg.replaceAll("%balance%", balance.toString()); msg = msg.replaceAll("%result%", totalCost.subtract(balance).toString()); String url = this.urlTpl; url = url.replaceAll("%mobile%", mobilePhoneMod); url = url.replaceAll("%message%", URLEncoder.encode(msg, "UTF-8")); sendSmsNotification(mobilePhoneMod, url, msg); } } } } Код: package ru.provider.sms.tasks; import java.io.IOException; import java.io.InputStream; import java.io.InputStreamReader; import java.io.UnsupportedEncodingException; import java.net.MalformedURLException; import java.net.URL; import java.net.URLConnection; import java.sql.Connection; import java.sql.PreparedStatement; import java.sql.SQLException; import java.util.Calendar; import java.util.LinkedList; import java.util.List; import bitel.billing.server.util.MailMsg; import ru.bitel.bgbilling.kernel.task.server.TaskBase; /** * <p>Основа для задач рассылки SMS и E-Mail уведомлений абонентам.</p> */ public abstract class AbstractSmsTask extends TaskBase { /** * <p>Идентификатор приложения для сбора статистики отправленных SMS</p> */ protected String appId; protected int mode; protected long sleep; protected String tariffTotal; protected List<Integer> workGroups; protected List<Integer> skipGroups; protected int mobilePid; protected int emailPid; protected String urlTpl; protected String emailMsgTpl; protected String mobileMsgTpl; protected Connection con; @Override public String getDescription() { return this.defaultDescription; } @Override public boolean initTask() { this.appId = this.taskSetup.get("appId", ""); this.mode = this.taskSetup.getInt("mode", 0); this.sleep = this.taskSetup.getLong("sleep", 0); this.tariffTotal = this.taskSetup.get("tariff.total", ""); this.workGroups = this.taskSetup.getIntegerList("group.ids", new LinkedList<Integer>()); this.skipGroups = this.taskSetup.getIntegerList("skip.ids", new LinkedList<Integer>()); this.mobilePid = this.taskSetup.getInt("mobile.pid", 0); this.emailPid = this.taskSetup.getInt("email.pid", 0); this.urlTpl = this.taskSetup.get("url.tpl", ""); this.emailMsgTpl = this.taskSetup.get("email.msg.tpl", ""); this.mobileMsgTpl = this.taskSetup.get("mobile.msg.tpl", ""); if (this.appId.isEmpty( )|| this.workGroups.isEmpty() || this.emailMsgTpl.isEmpty() || this.mobileMsgTpl.isEmpty() || this.urlTpl.isEmpty() || this.mobilePid == 0 || this.emailPid == 0) { return false; } return true; } @Override public abstract void executeTask(); /** * <p>Отправка уведомления на электронную почту</p> * @param address Адрес электронной почты * @param title Заголовок сообщения * @param message Текст уведомления * @throws UnsupportedEncodingException */ public void sendEmailNotification(String address, String title, String message) throws UnsupportedEncodingException { if (this.mode == 0) { return; } this.log.info(" E-Mail Address: " + address); this.log.info(" E-Mail Text: " + message); MailMsg emailMsg = new MailMsg(this.setup); emailMsg.sendMessage(address, title, message); } /** * <p>Отправка уведомления через SMS</p> * <p>При успешной отправке сообщения по HTTP обновляет счётчит отправленных сообщений для статистики.</p> * @param mobile Номер телефона в формате 9XXXXXXXXX * @param url Адрес сервера для отправки запроса * @param message Текст уведомления * @throws UnsupportedEncodingException */ public void sendSmsNotification(String mobile, String url, String message) throws UnsupportedEncodingException { if (this.mode == 0) { return; } this.log.info(" SMS Phone: " + mobile); this.log.info(" SMS Text: " + message); this.log.info(" SMS URL: " + url); URLConnection conn; try { conn = new URL(url).openConnection(); } catch (MalformedURLException e) { this.log.error("Malformed URL found: url=" + url); this.log.error(e.getMessage()); return; } catch (IOException e) { this.log.error("IOException while opening the url connection found:"); this.log.error(e.getMessage()); return; } String reply; try { reply = readStreamToString(conn.getInputStream(), "UTF-8"); } catch (IOException e) { this.log.error("IOException while reading reply found:"); this.log.error(e.getMessage()); return; } this.log.info(" SMS Result: " + reply); if (reply.matches("^\\s*OK\\s*$")) { int messages = 0; if (message.length() <= 70) { messages = 1; } else { messages = (int)Math.ceil((float)message.length()/67); } try { Calendar today = Calendar.getInstance(); PreparedStatement ps = this.con.prepareStatement("" + " INSERT INTO" + " sms_stats" + " VALUES (" + " ?," + " ?," + " ?," + " ?" + " ) ON DUPLICATE KEY UPDATE messagesSend = messagesSend + ?"); ps.setInt(1, today.get(Calendar.MONTH) + 1); ps.setInt(2, today.get(Calendar.YEAR) - 2000); ps.setString(3, appId); ps.setInt(4, messages); ps.setInt(5, messages); ps.execute(); ps.close(); } catch (SQLException e) { this.log.error("Cannot update SMS stats: " + e.getMessage()); } } } /** * <p>Читает данные из потока и записывает их в выходную строку в соответствии с заданной кодировкой.</p> * @param in Входной поток * @param encoding Кодировка * @return * @throws IOException */ public String readStreamToString(InputStream in, String encoding) throws IOException { StringBuffer b = new StringBuffer(); InputStreamReader r = new InputStreamReader(in, encoding); int c; while ((c = r.read()) != -1) { b.append((char)c); } return b.toString(); } /** * <p>Приостанавливает выполнение потока на заданное количество секунд.</p> * @throws InterruptedException */ public void sleep() throws InterruptedException { if (sleep > 0) { Thread.sleep(sleep); } } } Конфиг задачи в биллинге: Код: # Режим запуска: # 0 - сообщения не рассылаются, только запись логов работы # 1 - сообщения рассылаются mode=1 # Идентификатор приложения для сбора статистики по отправленным SMS-сообщениям appId=NOTIFICATION # Время ожидания в милисекундах после обработки одного абонента. 0 - нет ожидания. sleep=1200 # Код модуля абонплат npay.mid=2 # Группы договоров, которым рассылать уведомления. Для отправки уведомления у договора должны быть установлены все перечисленные группы одновременно! group.ids=1,2 # Группы договоров, которым запрещено рассылать уведомления. Для исключения из рассылки у договора должна быть установлена хотя бы одна из перечисленных групп. skip.ids=3 # Тарифные планы с подневной абонплатой tariff.total=1,3,4 # Параметр договора, в котором содержится адрес электронной почты email.pid=1 # Шаблон для отправки электронной почты email.msg.tpl=Напоминаем пополнить счет на сумму %result% руб. по договору %title%. # Параметр договора, в котором содержится номер мобильного телефона mobile.pid=2 # Шаблон SMS сообщения mobile.msg.tpl=Пополните счет на сумму %result% руб. по договору %title%. # Шаблон адреса для отправки SMS url.tpl=http://www.xxx.ru/send.php?phone=%mobile%&message=%message% В базе нужно добавить: Код: SELECT * FROM bgbilling.scheduled_class; 1 Рассылка SMS уведомлений ru.provider.sms.tasks.PeriodicSmsNotifications Небольшие пояснения. Задача проверяет, хватает ли денег на следующий месяц, сравнивая планируемую наработку из Calculator с балансом. Если не хватает, то оправляется SMS и Email сообщения, если указаны номер телефона и/или адрес почты. Задержка sleep нужна, чтобы звонки абонентов размазать по времени на весь рабочий день. Для запуска классы нужно скомпилировать, собрать из них jar, положить рядом с kernel.jar. Перезапустить биллинг и планировщик. Добавить в базу одну строчку с новой задачей. Добавить через стандартный интерфейс новую задачу. Не забываем обращать внимание на версию биллинга. Из дополнительного здесь ведётся статистика отправленных сообщения для сверки счетов. |
Автор: | skyb [ 12 май 2014, 17:20 ] |
Заголовок сообщения: | Re: Рассылка напоминаний должникам |
ох ёжик ))))))))) чета много букофф, а он для любой абонки подходит? что будет после обновления? |
Автор: | vkulakov [ 12 май 2014, 17:40 ] |
Заголовок сообщения: | Re: Рассылка напоминаний должникам |
Что значит "для любой абонки"? Точно работает с ежемесячной абонплатой, учитывает опции. Абоненты с подневной абонплатой исключаются из рассылки. Для нас, собственно, имеет значение только ежемесячная абонплата. После обновления, возможно, работать не будет. Но это всё я привёл для примера, как и просили, под себя всё-равно допиливать. |
Автор: | skyb [ 12 май 2014, 18:00 ] |
Заголовок сообщения: | Re: Рассылка напоминаний должникам |
Ну да, я имел ввиду что лучше выложить в вику и с коментами как работает, чтоб по сообщениям не собирать |
Автор: | vkulakov [ 12 май 2014, 19:36 ] |
Заголовок сообщения: | Re: Рассылка напоминаний должникам |
Попробую на досуге заняться. |
Страница 1 из 1 | Часовой пояс: UTC + 5 часов [ Летнее время ] |
Powered by phpBB® Forum Software © phpBB Group http://www.phpbb.com/ |