]> git.stg.codes - stg.git/commitdiff
Merge remote-tracking branch 'other/ticket37' into ticket
authorElena Mamontova <helenh463@gmail.com>
Tue, 25 Oct 2016 15:34:40 +0000 (18:34 +0300)
committerElena Mamontova <helenh463@gmail.com>
Tue, 25 Oct 2016 15:34:40 +0000 (18:34 +0300)
25 files changed:
include/stg/tariff.h
include/stg/tariff_conf.h
projects/stargazer/inst/var/02-alter-03.postgresql.sql
projects/stargazer/inst/var/02-alter-03.sql
projects/stargazer/plugins/configuration/rpcconfig/tariff_helper.cpp
projects/stargazer/plugins/configuration/rpcconfig/user_helper.cpp
projects/stargazer/plugins/configuration/rpcconfig/users_methods.cpp
projects/stargazer/plugins/configuration/sgconfig/parser_tariffs.cpp
projects/stargazer/plugins/configuration/sgconfig/parser_users.cpp
projects/stargazer/plugins/store/files/file_store.cpp
projects/stargazer/plugins/store/firebird/firebird_store_tariffs.cpp
projects/stargazer/plugins/store/mysql/mysql_store.cpp
projects/stargazer/plugins/store/postgresql/postgresql_store.h
projects/stargazer/plugins/store/postgresql/postgresql_store_messages.cpp
projects/stargazer/plugins/store/postgresql/postgresql_store_tariffs.cpp
projects/stargazer/plugins/store/postgresql/postgresql_store_users.cpp
projects/stargazer/plugins/store/postgresql/postgresql_store_utils.cpp
projects/stargazer/tariff_impl.cpp
projects/stargazer/tariff_impl.h
projects/stargazer/user_impl.cpp
stglibs/common.lib/common.cpp
stglibs/common.lib/include/stg/common.h
stglibs/conffiles.lib/conffiles.cpp
stglibs/conffiles.lib/include/stg/conffiles.h
tests/test_tariff.cpp

index 879c4ecc9e3e7f1a5b1ddca3913ee45d9d069400..2fd797fde514fed8a8a276116dcb2fa066639946 100644 (file)
@@ -59,6 +59,7 @@ public:
     virtual double  GetFree() const = 0;
     virtual PERIOD  GetPeriod() const = 0;
     virtual CHANGE_POLICY GetChangePolicy() const = 0;
+    virtual time_t  GetChangePolicyTimeout() const = 0;
 
     virtual const   std::string & GetName() const = 0;
     virtual void    SetName(const std::string & name) = 0;
@@ -67,6 +68,7 @@ public:
     virtual int64_t GetTraffByType(uint64_t up, uint64_t down) const = 0;
     virtual int     GetThreshold(int dir) const = 0;
     virtual const TARIFF_DATA & GetTariffData() const = 0;
+    virtual std::string TariffChangeIsAllowed(const TARIFF & to) const = 0;
 };
 
 inline
index d8684bd9de352e351ea18fb0b6028e74616ee6ce..79ade2eb44bef6ddaee90bf705a8d421297b7dcf 100644 (file)
@@ -149,6 +149,7 @@ struct TARIFF_CONF
     std::string        name;
     TARIFF::PERIOD     period;
     TARIFF::CHANGE_POLICY changePolicy;
+    time_t changePolicyTimeout;
 
     TARIFF_CONF()
         : fee(0),
@@ -157,7 +158,8 @@ struct TARIFF_CONF
           passiveCost(0),
           name(),
           period(TARIFF::MONTH),
-          changePolicy(TARIFF::ALLOW)
+          changePolicy(TARIFF::ALLOW),
+          changePolicyTimeout(0)
         {}
 
     TARIFF_CONF(const std::string & n)
@@ -167,7 +169,8 @@ struct TARIFF_CONF
           passiveCost(0),
           name(n),
           period(TARIFF::MONTH),
-          changePolicy(TARIFF::ALLOW)
+          changePolicy(TARIFF::ALLOW),
+          changePolicyTimeout(0)
         {}
 };
 //-----------------------------------------------------------------------------
@@ -180,7 +183,8 @@ struct TARIFF_CONF_RES
           passiveCost(),
           name(),
           period(),
-          changePolicy()
+          changePolicy(),
+          changePolicyTimeout()
         {}
 
     TARIFF_CONF_RES & operator=(const TARIFF_CONF & tc)
@@ -192,6 +196,7 @@ struct TARIFF_CONF_RES
         name        = tc.name;
         period      = tc.period;
         changePolicy = tc.changePolicy;
+        changePolicyTimeout = tc.changePolicyTimeout;
         return *this;
         }
 
@@ -205,6 +210,7 @@ struct TARIFF_CONF_RES
         traffType.maybeSet(tc.traffType);
         period.maybeSet(tc.period);
         changePolicy.maybeSet(tc.changePolicy);
+        changePolicyTimeout.maybeSet(tc.changePolicyTimeout);
         return tc;
         }
 
@@ -215,6 +221,7 @@ struct TARIFF_CONF_RES
     RESETABLE<std::string>        name;
     RESETABLE<TARIFF::PERIOD>     period;
     RESETABLE<TARIFF::CHANGE_POLICY> changePolicy;
+    RESETABLE<time_t>             changePolicyTimeout;
 };
 //-----------------------------------------------------------------------------
 struct TARIFF_DATA
@@ -255,6 +262,14 @@ struct TARIFF_DATA_RES
           dirPrice(DIR_NUM)
         {}
 
+    TARIFF_DATA_RES & operator=(const TARIFF_DATA & td)
+        {
+        tariffConf = td.tariffConf;
+        for (size_t i = 0; i < DIR_NUM; ++i)
+            dirPrice[i] = td.dirPrice[i];
+        return *this;
+        }
+
     TARIFF_DATA GetData() const
         {
         TARIFF_DATA td;
index a686aa68b8caab1cf55ea18e3a9b521b89947abb..e8b248bf2c5104b9f79783cd4f32e0279b044036 100644 (file)
@@ -7,6 +7,9 @@ CREATE DOMAIN DM_TARIFF_CHANGE_POLICY AS TEXT NOT NULL
     CONSTRAINT valid_value CHECK (VALUE IN ('allow', 'to_cheap', 'to_expensive', 'deny'));
 
 ALTER TABLE tb_tariffs ADD change_policy DM_TARIFF_CHANGE_POLICY DEFAULT 'allow';
+ALTER TABLE tb_tariffs ADD change_policy_timeout TIMESTAMP NOT NULL DEFAULT '1970-01-01 00:00:00';
+ALTER TABLE tb_tariffs ALTER COLUMN change_policy DROP DEFAULT;
+ALTER TABLE tb_tariffs ALTER COLUMN change_policy_timeout DROP DEFAULT;
 
 UPDATE tb_info SET version = 8;
 
index a01991736b08f1ff585f89d9d90f65862b814e45..d274520348e3234743847ea3ea618a7ee53d1580 100644 (file)
@@ -6,5 +6,11 @@ CREATE DOMAIN DM_TARIFF_CHANGE_POLICY AS  VARCHAR(32) NOT NULL
     CHECK (VALUE IN ('allow', 'to_cheap', 'to_expensive', 'deny'));
 
 ALTER TABLE tb_tariffs ADD change_policy DM_TARIFF_CHANGE_POLICY DEFAULT 'allow';
+ALTER TABLE tb_tariffs ADD change_policy_timeout DM_MOMENT DEFAULT '1970-01-01 00:00:00';
+
+UPDATE tb_tariffs SET change_policy = 'allow', change_policy_timeout = '1970-01-01 00:00:00';
+
+ALTER TABLE tb_tariffs ALTER COLUMN change_policy DROP DEFAULT;
+ALTER TABLE tb_tariffs ALTER COLUMN change_policy_timeout DROP DEFAULT;
 
 UPDATE tb_info SET version = 2;
index c96b7d31cd58b025a83a52d3950f438d3389d67e..7c023e25fd37c3a4bf96abddab964e2d3b652173 100644 (file)
@@ -1,6 +1,7 @@
 #include <ostream> // xmlrpc-c devs have missed something :)
 
 #include "tariff_helper.h"
+#include "stg/common.h"
 
 void TARIFF_HELPER::GetTariffInfo(xmlrpc_c::value * info) const
 {
@@ -14,6 +15,7 @@ structVal["passivecost"] = xmlrpc_c::value_double(data.tariffConf.passiveCost);
 structVal["traffType"] = xmlrpc_c::value_int(data.tariffConf.traffType);
 structVal["period"] = xmlrpc_c::value_string(TARIFF::PeriodToString(data.tariffConf.period));
 structVal["changePolicy"] = xmlrpc_c::value_string(TARIFF::ChangePolicyToString(data.tariffConf.changePolicy));
+structVal["changePolicyTimeout"] = xmlrpc_c::value_string(formatTime(data.tariffConf.changePolicyTimeout));
 
 std::vector<xmlrpc_c::value> prices(DIR_NUM);
 
@@ -77,6 +79,11 @@ if ((it = structVal.find("changePolicy")) != structVal.end())
     data.tariffConf.changePolicy = TARIFF::StringToChangePolicy(xmlrpc_c::value_string(it->second));
     }
 
+if ((it = structVal.find("changePolicyTimeout")) != structVal.end())
+    {
+    data.tariffConf.changePolicyTimeout = readTime(xmlrpc_c::value_string(it->second));
+    }
+
 if ((it = structVal.find("dirprices")) != structVal.end())
     {
     std::vector<xmlrpc_c::value> prices(
index 89c41587f8527862de5932bbc4cbe52e63ab0c37..0e7ac9d66e13091d8ecf2091a2392400e6f317a0 100644 (file)
@@ -433,13 +433,27 @@ if ((it = structVal.find("tariff")) != structVal.end())
         tariff = tariff.substr(0, pos);
         }
 
-    if (tariffs->FindByName(tariff))
-        if (ptr->GetProperty().tariffName.Get() != tariff)
-            if (!ptr->GetProperty().tariffName.Set(tariff,
-                                               admin,
-                                               login,
-                                               &store))
-                return true;
+    const TARIFF * newTariff = tariffs->FindByName(tariff);
+    if (newTariff)
+        {
+        const TARIFF * currentTariff = ptr->GetTariff();
+        std::string message = currentTariff->TariffChangeIsAllowed(*newTariff);
+        if (message.empty())
+            {
+            if (ptr->GetProperty().tariffName.Get() != tariff)
+                {
+                if (!ptr->GetProperty().tariffName.Set(tariff,
+                                                   admin,
+                                                   login,
+                                                   &store))
+                    return true;
+                }
+            }
+        else
+            {
+            GetStgLogger()("Tariff change is prohibited for user %s. %s", ptr->GetLogin().c_str(), message.c_str());
+            }
+        }
 
     if (nextTariff != "" &&
         tariffs->FindByName(nextTariff))
index 5adbf44131905750cfa52fefbf1398b0842b2dd0..5a738d16d2b81220503c18177d63547b191a874e 100644 (file)
@@ -381,16 +381,29 @@ if (tariffs->FindByName(tariff))
         }
     else
         {
-        if (u->GetProperty().tariffName.Set(tariff,
+        const TARIFF * newTariff = tariffs->FindByName(tariff);
+        if (newTariff)
+            {
+            const TARIFF * currentTariff = u->GetTariff();
+            std::string message = currentTariff->TariffChangeIsAllowed(*newTariff);
+            if (message.empty())
+                {
+                if (u->GetProperty().tariffName.Set(tariff,
                                             admin,
                                             login,
                                             store,
                                             comment))
-            {
-            u->ResetNextTariff();
-            u->WriteConf();
-            *retvalPtr = xmlrpc_c::value_boolean(true);
-            return;
+                    {
+                    u->ResetNextTariff();
+                    u->WriteConf();
+                    *retvalPtr = xmlrpc_c::value_boolean(true);
+                    return;
+                    }
+                }
+            else
+                {
+                GetStgLogger()("Tariff change is prohibited for user %s. %s", u->GetLogin().c_str(), message.c_str());
+                }
             }
         }
     }
index b33b1305ca4e02789427882c2334f87cfb692c64..9c75267480e9591544e920ddb2b28788ff3f08ac 100644 (file)
@@ -115,6 +115,7 @@ void GET_TARIFFS::CreateAnswer()
                   "<TraffType value=\"" + TARIFF::TraffTypeToString(it->tariffConf.traffType) + "\"/>" +
                   "<Period value=\"" + TARIFF::PeriodToString(it->tariffConf.period) + "\"/>" +
                   "<ChangePolicy value=\"" + TARIFF::ChangePolicyToString(it->tariffConf.changePolicy) + "\"/>" +
+                  "<ChangePolicyTimeout value=\"" + x2str(it->tariffConf.changePolicyTimeout) + "\"/>" +
                   "</tariff>";
         }
 
@@ -171,7 +172,11 @@ int CHG_TARIFF::Start(void *, const char * el, const char ** attr)
     {
         if (strcasecmp(el, m_tag.c_str()) == 0)
         {
-            td.tariffConf.name = attr[1];
+            const TARIFF * tariff = m_tariffs.FindByName(attr[1]);
+            if (tariff != NULL)
+                td = tariff->GetTariffData();
+            else
+                return -1;
             return 0;
         }
     }
@@ -295,6 +300,14 @@ int CHG_TARIFF::Start(void *, const char * el, const char ** attr)
             td.tariffConf.changePolicy = TARIFF::StringToChangePolicy(attr[1]);
             return 0;
         }
+
+        if (strcasecmp(el, "ChangePolicyTimeout") == 0)
+        {
+            int64_t policyTime = 0;
+            if (str2x(attr[1], policyTime) == 0)
+                td.tariffConf.changePolicyTimeout = (time_t)policyTime;
+            return 0;
+        }
     }
     return -1;
 }
index 2e9d70872a82e3a7ac1ff92236c36c306060d582..8f15c491a0b3e553685bc0a80cb4bb11aee9366c 100644 (file)
@@ -594,11 +594,21 @@ int CHG_USER::ApplyChanges()
 
     if (!m_ucr.tariffName.empty())
     {
-        if (m_tariffs.FindByName(m_ucr.tariffName.const_data()))
+        const TARIFF * newTariff = m_tariffs.FindByName(m_ucr.tariffName.const_data());
+        if (newTariff)
         {
-            if (!u->GetProperty().tariffName.Set(m_ucr.tariffName.const_data(), &m_currAdmin, m_login, &m_store))
-                return -1;
-            u->ResetNextTariff();
+            const TARIFF * tariff = u->GetTariff();
+            std::string message = tariff->TariffChangeIsAllowed(*newTariff);
+            if (message.empty())
+            {
+                if (!u->GetProperty().tariffName.Set(m_ucr.tariffName.const_data(), &m_currAdmin, m_login, &m_store))
+                    return -1;
+                u->ResetNextTariff();
+            }
+            else
+            {
+                GetStgLogger()("Tariff change is prohibited for user %s. %s", u->GetLogin().c_str(), message.c_str());
+            }
         }
         else
         {
index d4b29ad9c488b939dd00526b71aa86784494f437..f63d96869552c48b8cdcdaa85502c00445bf3b43 100644 (file)
@@ -1523,6 +1523,14 @@ if (conf.ReadString("ChangePolicy", &str, "allow") < 0)
     td->tariffConf.changePolicy = TARIFF::ALLOW;
 else
     td->tariffConf.changePolicy = TARIFF::StringToChangePolicy(str);
+
+if (conf.ReadTime("ChangePolicyTimeout", &td->tariffConf.changePolicyTimeout, 0) < 0)
+    {
+    STG_LOCKER lock(&mutex);
+    errorStr = "Cannot read tariff " + tariffName + ". Parameter ChangePolicyTimeout";
+    printfd(__FILE__, "FILES_STORE::RestoreTariff - changepolicytimeout read failed for tariff '%s'\n", tariffName.c_str());
+    return -1;
+    }
 return 0;
 }
 //-----------------------------------------------------------------------------
@@ -1585,6 +1593,7 @@ std::string fileName = storeSettings.GetTariffsDir() + "/" + tariffName + ".tf";
     cf.WriteString("TraffType", TARIFF::TraffTypeToString(td.tariffConf.traffType));
     cf.WriteString("Period", TARIFF::PeriodToString(td.tariffConf.period));
     cf.WriteString("ChangePolicy", TARIFF::ChangePolicyToString(td.tariffConf.changePolicy));
+    cf.WriteTime("ChangePolicyTimeout", td.tariffConf.changePolicyTimeout);
     }
 
 return 0;
index cdee539855eec910fbfd8defd3b111929e38baed..6f8177cab11bc04445b85d1503b090a0bdfd4124 100644 (file)
@@ -150,54 +150,44 @@ try
     int32_t id;
     st->Get(1, id);
     st->Close();
-    if (schemaVersion == 1)
+
+    std::string query = "update tb_tariffs set \
+                            fee = ?, \
+                            free = ?, \
+                            passive_cost = ?, \
+                            traff_type = ?";
+
+    if (schemaVersion > 0)
+        query += ", period = ?";
+    if (schemaVersion > 1)
+        query += ", change_policy = ?, \
+                    change_policy_timeout = ?";
+
+    query += " where pk_tariff = ?";
+
+    unsigned num = 5;
+    st->Prepare(query);
+    st->Set(1, td.tariffConf.fee);
+    st->Set(2, td.tariffConf.free);
+    st->Set(3, td.tariffConf.passiveCost);
+    st->Set(4, td.tariffConf.traffType);
+
+    if (schemaVersion > 0)
         {
-        st->Prepare("update tb_tariffs set \
-                fee = ?, \
-                free = ?, \
-                passive_cost = ?, \
-                traff_type = ?, \
-                period = ? \
-                where pk_tariff = ?");
-        st->Set(1, td.tariffConf.fee);
-        st->Set(2, td.tariffConf.free);
-        st->Set(3, td.tariffConf.passiveCost);
-        st->Set(4, td.tariffConf.traffType);
         st->Set(5, TARIFF::PeriodToString(td.tariffConf.period));
-        st->Set(6, id);
+        ++num;
         }
-    else if (schemaVersion > 1)
-            {
-            st->Prepare("update tb_tariffs set \
-                    fee = ?, \
-                    free = ?, \
-                    passive_cost = ?, \
-                    traff_type = ?, \
-                    period = ?, \
-                    change_policy = ? \
-                    where pk_tariff = ?");
-            st->Set(1, td.tariffConf.fee);
-            st->Set(2, td.tariffConf.free);
-            st->Set(3, td.tariffConf.passiveCost);
-            st->Set(4, td.tariffConf.traffType);
-            st->Set(5, TARIFF::PeriodToString(td.tariffConf.period));
-            st->Set(6, TARIFF::ChangePolicyToString(td.tariffConf.changePolicy));
-            st->Set(7, id);
-            }
-    else
+
+    if (schemaVersion > 1)
         {
-        st->Prepare("update tb_tariffs set \
-                fee = ?, \
-                free = ?, \
-                passive_cost = ?, \
-                traff_type = ? \
-                where pk_tariff = ?");
-        st->Set(1, td.tariffConf.fee);
-        st->Set(2, td.tariffConf.free);
-        st->Set(3, td.tariffConf.passiveCost);
-        st->Set(4, td.tariffConf.traffType);
-        st->Set(5, id);
+        st->Set(6, TARIFF::ChangePolicyToString(td.tariffConf.changePolicy));
+        IBPP::Timestamp policyTimeout;
+        time_t2ts(td.tariffConf.changePolicyTimeout, &policyTimeout);
+        st->Set(7, policyTimeout);
+        num += 2;
         }
+
+    st->Set(num, id);
     st->Execute();
     st->Close();
 
@@ -304,7 +294,10 @@ try
     if (schemaVersion > 0)
         td->tariffConf.period = TARIFF::StringToPeriod(Get<std::string>(st, 7));
     if (schemaVersion > 1)
+        {
         td->tariffConf.changePolicy = TARIFF::StringToChangePolicy(Get<std::string>(st, 8));
+        td->tariffConf.changePolicyTimeout = ts2time_t(Get<IBPP::Timestamp>(st, 9));
+        }
     st->Close();
     st->Prepare("select * from tb_tariffs_params where fk_tariff = ?");
     st->Set(1, id);
index f3539dd6fa2f1283bd7fe91f8b49895ae9cf50f9..198b5f3b812375fcfe88ef3fa532865f45eb1cac 100644 (file)
@@ -7,6 +7,7 @@
 #include <mysql.h>
 #include <errmsg.h>
 
+#include "stg/common.h"
 #include "stg/user_ips.h"
 #include "stg/user_conf.h"
 #include "stg/user_stat.h"
@@ -369,7 +370,8 @@ if(!IsTablePresent("tariffs",sock))
     res += "PassiveCost DOUBLE DEFAULT 0.0, Fee DOUBLE DEFAULT 0.0,"
         "Free DOUBLE DEFAULT 0.0, TraffType VARCHAR(10) DEFAULT '',"
         "period VARCHAR(32) NOT NULL DEFAULT 'month',"
-        "change_policy VARCHAR(32) NOT NULL DEFAULT 'allow')";
+        "change_policy VARCHAR(32) NOT NULL DEFAULT 'allow',"
+        "change_policy_timeout TIMESTAMP NOT NULL DEFAULT 0)";
     
     if(MysqlQuery(res.c_str(),sock))
     {
@@ -424,7 +426,7 @@ if(!IsTablePresent("tariffs",sock))
     res += "PassiveCost=0.0, Fee=10.0, Free=0,"\
         "SinglePrice0=1, SinglePrice1=1,PriceDayA1=0.75,PriceDayB1=0.75,"\
         "PriceNightA0=1.0,PriceNightB0=1.0,TraffType='up+down',period='month',"\
-        "change_policy='allow'";
+        "change_policy='allow', change_policy_timeout=0";
     
     if(MysqlQuery(res.c_str(),sock))
     {
@@ -604,7 +606,8 @@ if (schemaVersion  < 1)
 
 if (schemaVersion  < 2)
     {
-    if (MysqlQuery("ALTER TABLE tariffs ADD change_policy VARCHAR(32) NOT NULL DEFAULT 'allow'", sock))
+    if (MysqlQuery("ALTER TABLE tariffs ADD change_policy VARCHAR(32) NOT NULL DEFAULT 'allow'", sock) ||
+        MysqlQuery("ALTER TABLE tariffs ADD change_policy_timeout TIMESTAMP NOT NULL DEFAULT 0", sock))
         {
         errorStr = "Couldn't update tariffs table to version 2. With error:\n";
         errorStr += mysql_error(sock);
@@ -1662,7 +1665,7 @@ else
     }
 
 if (schemaVersion > 1)
-{
+    {
     str = row[6+8*DIR_NUM];
     param = "ChangePolicy";
 
@@ -1675,10 +1678,24 @@ if (schemaVersion > 1)
         }
 
     td->tariffConf.changePolicy = TARIFF::StringToChangePolicy(str);
+
+    str = row[7+8*DIR_NUM];
+    param = "ChangePolicyTimeout";
+
+    if (str.length() == 0)
+        {
+        mysql_free_result(res);
+        errorStr = "Cannot read tariff " + tariffName + ". Parameter " + param;
+        mysql_close(sock);
+        return -1;
+        }
+
+    td->tariffConf.changePolicyTimeout = readTime(str);
     }
 else
     {
     td->tariffConf.changePolicy = TARIFF::ALLOW;
+    td->tariffConf.changePolicyTimeout = 0;
     }
 
 mysql_free_result(res);
@@ -1749,7 +1766,8 @@ if (schemaVersion > 0)
     res += ", Period='" + TARIFF::PeriodToString(td.tariffConf.period) + "'";
 
 if (schemaVersion > 1)
-    res += ", change_policy='" + TARIFF::ChangePolicyToString(td.tariffConf.changePolicy) + "'";
+    res += ", change_policy='" + TARIFF::ChangePolicyToString(td.tariffConf.changePolicy) + "'"\
+           ", change_policy_timeout='" + formatTime(td.tariffConf.changePolicy) + "'";
 
 strprintf(&param, " WHERE name='%s' LIMIT 1", tariffName.c_str());
 res += param;
index 72a2c22c01c951fbd7bed410f12189036456776b..6bcb3cd05dbe4b8b9001a6f6e8e3ea0fd73ea228 100644 (file)
@@ -128,9 +128,6 @@ private:
 
     int EscapeString(std::string & value) const;
 
-    std::string Int2TS(time_t value) const;
-    time_t TS2Int(const std::string & value) const;
-
     int SaveStat(const USER_STAT & stat, const std::string & login, int year = 0, int month = 0) const;
 
     int SaveUserServices(uint32_t uid, const std::vector<std::string> & services) const;
index 27401e268ff874bdded9fde809e1e5d025395c07..42a190732d5368b3ba32273f581818ff08a83aa8 100644 (file)
@@ -33,6 +33,7 @@
 
 #include <libpq-fe.h>
 
+#include "stg/common.h"
 #include "postgresql_store.h"
 #include "stg/locker.h"
 #include "stg/message.h"
@@ -89,8 +90,8 @@ query << "SELECT sp_add_message("
       << "'" << elogin << "', "
       << "CAST(1 AS SMALLINT), " // Here need to be a version, but, it's uninitiated actually
       << "CAST(" << msg->header.type << " AS SMALLINT), "
-      << "CAST('" << Int2TS(msg->header.lastSendTime) << "' AS TIMESTAMP), "
-      << "CAST('" << Int2TS(msg->header.creationTime) << "' AS TIMESTAMP), "
+      << "CAST('" << formatTime(msg->header.lastSendTime) << "' AS TIMESTAMP), "
+      << "CAST('" << formatTime(msg->header.creationTime) << "' AS TIMESTAMP), "
       << msg->header.showTime << ", "
       << "CAST(" << msg->header.repeat << " AS SMALLINT), "
       << msg->header.repeatPeriod << ", "
@@ -192,8 +193,8 @@ query << "UPDATE tb_messages SET "
           << "fk_user = (SELECT pk_user FROM tb_users WHERE name = '" << elogin << "'), "
           << "ver = " << msg.header.ver << ", "
           << "msg_type = " << msg.header.type << ", "
-          << "last_send_time = CAST('" << Int2TS(msg.header.lastSendTime) << "' AS TIMESTAMP), "
-          << "creation_time = CAST('" << Int2TS(msg.header.creationTime) << "' AS TIMESTAMP), "
+          << "last_send_time = CAST('" << formatTime(msg.header.lastSendTime) << "' AS TIMESTAMP), "
+          << "creation_time = CAST('" << formatTime(msg.header.creationTime) << "' AS TIMESTAMP), "
           << "show_time = " << msg.header.showTime << ", "
           << "repeat = " << msg.header.repeat << ", "
           << "repeat_period = " << msg.header.repeatPeriod << ", "
@@ -287,8 +288,8 @@ if (tuples != 1)
 
 str2x(PQgetvalue(result, 0, 0), msg->header.ver);
 str2x(PQgetvalue(result, 0, 1), msg->header.type);
-msg->header.lastSendTime = static_cast<unsigned int>(TS2Int(PQgetvalue(result, 0, 2)));
-msg->header.creationTime = static_cast<unsigned int>(TS2Int(PQgetvalue(result, 0, 3)));
+msg->header.lastSendTime = static_cast<unsigned int>(readTime(PQgetvalue(result, 0, 2)));
+msg->header.creationTime = static_cast<unsigned int>(readTime(PQgetvalue(result, 0, 3)));
 str2x(PQgetvalue(result, 0, 4), msg->header.showTime);
 str2x(PQgetvalue(result, 0, 5), msg->header.repeat);
 str2x(PQgetvalue(result, 0, 6), msg->header.repeatPeriod);
@@ -424,8 +425,8 @@ for (int i = 0; i < tuples; ++i)
     tuple << PQgetvalue(result, i, 0) << " ";
     tuple << PQgetvalue(result, i, 1) << " ";
     tuple << PQgetvalue(result, i, 2) << " ";
-    header.lastSendTime = static_cast<unsigned int>(TS2Int(PQgetvalue(result, i, 3)));
-    header.creationTime = static_cast<unsigned int>(TS2Int(PQgetvalue(result, i, 4)));
+    header.lastSendTime = static_cast<unsigned int>(readTime(PQgetvalue(result, i, 3)));
+    header.creationTime = static_cast<unsigned int>(readTime(PQgetvalue(result, i, 4)));
     tuple << PQgetvalue(result, i, 5) << " ";
     tuple << PQgetvalue(result, i, 6) << " ";
     tuple << PQgetvalue(result, i, 7) << " ";
index c509c39c1f56dbb53be3ccdb09c8acfe1906410d..fd0b38bca4f9a6056a52d9d3d9ca178290435c86 100644 (file)
@@ -33,6 +33,7 @@
 
 #include <libpq-fe.h>
 
+#include "stg/common.h"
 #include "postgresql_store.h"
 #include "stg/locker.h"
 
@@ -318,7 +319,8 @@ int32_t id;
         query << ", period = '" << TARIFF::PeriodToString(td.tariffConf.period) << "'";
 
     if (version > 7)
-        query << ", change_policy = '" << TARIFF::ChangePolicyToString(td.tariffConf.changePolicy) << "'";
+        query << ", change_policy = '" << TARIFF::ChangePolicyToString(td.tariffConf.changePolicy) << "', \
+                  change_policy_timeout = CAST('" << formatTime(td.tariffConf.changePolicyTimeout) << "' AS TIMESTAMP)";
 
     query << " WHERE pk_tariff = " << id;
 
@@ -459,7 +461,8 @@ if (version > 6)
     query << ", period";
 
 if (version > 7)
-    query << ", change_policy";
+    query << ", change_policy \
+              , change_policy_timeout";
 
 query << " FROM tb_tariffs WHERE name = '" << ename << "'";
 
@@ -512,7 +515,10 @@ if (version > 6)
     td->tariffConf.period = TARIFF::StringToPeriod(PQgetvalue(result, 0, 5));
 
 if (version > 7)
+    {
     td->tariffConf.changePolicy = TARIFF::StringToChangePolicy(PQgetvalue(result, 0, 6));
+    td->tariffConf.changePolicyTimeout = readTime(PQgetvalue(result, 0, 7));
+    }
 
 PQclear(result);
 
index db2717acb0496e8da8f7b8b8f2a9bd62e7b01c5b..1ab5d0e8c2c806e28974c30065975727efc9bfa4 100644 (file)
@@ -33,6 +33,7 @@
 
 #include <libpq-fe.h>
 
+#include "stg/common.h"
 #include "stg/const.h"
 #include "stg/locker.h"
 #include "../../../stg_timer.h"
@@ -270,9 +271,9 @@ std::ostringstream query;
 query << "UPDATE tb_users SET "
             "cash = " << stat.cash << ", "
             "free_mb = " << stat.freeMb << ", "
-            "last_activity_time = CAST('" << Int2TS(stat.lastActivityTime) << "' AS TIMESTAMP), "
+            "last_activity_time = CAST('" << formatTime(stat.lastActivityTime) << "' AS TIMESTAMP), "
             "last_cash_add = " << stat.lastCashAdd << ", "
-            "last_cash_add_time = CAST('" << Int2TS(stat.lastCashAddTime) << "' AS TIMESTAMP), "
+            "last_cash_add_time = CAST('" << formatTime(stat.lastCashAddTime) << "' AS TIMESTAMP), "
             "passive_time = " << stat.passiveTime << " "
          "WHERE name = '" << elogin << "'";
 
@@ -527,7 +528,7 @@ query << "UPDATE tb_users SET "
              "address = '" << eaddress << "', "
              "always_online = " << (conf.alwaysOnline ? "'t'" : "'f'") << ", "
              "credit = " << conf.credit << ", "
-             "credit_expire = CAST('" << Int2TS(conf.creditExpire) << "' AS TIMESTAMP), "
+             "credit_expire = CAST('" << formatTime(conf.creditExpire) << "' AS TIMESTAMP), "
              "disabled = " << (conf.disabled ? "'t'" : "'f'") << ", "
              "disabled_detail_stat = " << (conf.disabledDetailStat ? "'t'" : "'f'") << ", "
              "email = '" << eemail << "', "
@@ -681,9 +682,9 @@ if (tuples != 1)
     std::stringstream tuple;
     tuple << PQgetvalue(result, 0, 0) << " ";
     tuple << PQgetvalue(result, 0, 1) << " ";
-    stat->lastActivityTime = TS2Int(PQgetvalue(result, 0, 2));
+    stat->lastActivityTime = readTime(PQgetvalue(result, 0, 2));
     tuple << PQgetvalue(result, 0, 3) << " ";
-    stat->lastCashAddTime = TS2Int(PQgetvalue(result, 0, 4));
+    stat->lastCashAddTime = readTime(PQgetvalue(result, 0, 4));
     tuple << PQgetvalue(result, 0, 5) << " ";
 
     PQclear(result);
@@ -699,7 +700,7 @@ if (tuples != 1)
     query << "SELECT dir_num, upload, download "
              "FROM tb_stats_traffic "
              "WHERE fk_user IN (SELECT pk_user FROM tb_users WHERE name = '" << elogin << "') AND "
-                   "DATE_TRUNC('month', stats_date) = DATE_TRUNC('month', CAST('" << Int2TS(stgTime) << "' AS TIMESTAMP))";
+                   "DATE_TRUNC('month', stats_date) = DATE_TRUNC('month', CAST('" << formatTime(stgTime) << "' AS TIMESTAMP))";
 
     result = PQexec(connection, query.str().c_str());
     }
@@ -832,7 +833,7 @@ uint32_t uid;
     conf->address = PQgetvalue(result, 0, 1);               // address
     conf->alwaysOnline = !strncmp(PQgetvalue(result, 0, 2), "t", 1);
     tuple << PQgetvalue(result, 0, 3) << " ";               // credit
-    conf->creditExpire = TS2Int(PQgetvalue(result, 0, 4));  // creditExpire
+    conf->creditExpire = readTime(PQgetvalue(result, 0, 4));  // creditExpire
     conf->disabled = !strncmp(PQgetvalue(result, 0, 5), "t", 1);
     conf->disabledDetailStat = !strncmp(PQgetvalue(result, 0, 6), "t", 1);
     conf->email = PQgetvalue(result, 0, 7);                 // email
@@ -1078,7 +1079,7 @@ query << "SELECT sp_add_param_log_entry("
             "'" << eadminLogin << "', CAST('"
             << inet_ntostring(admIP) << "/32' AS INET), "
             "'" << eparam << "', "
-            "CAST('" << Int2TS(stgTime) << "' AS TIMESTAMP), "
+            "CAST('" << formatTime(stgTime) << "' AS TIMESTAMP), "
             "'" << eold << "', "
             "'" << enew << "', "
             "'" << emessage << "')";
@@ -1149,7 +1150,7 @@ if (version < 6)
     {
     query << "SELECT sp_add_session_log_entry("
                  "'" << elogin << "', "
-                 "CAST('" << Int2TS(stgTime) << "' AS TIMESTAMP), "
+                 "CAST('" << formatTime(stgTime) << "' AS TIMESTAMP), "
                  "'c', CAST('"
                  << inet_ntostring(ip) << "/32' AS INET), 0)";
     }
@@ -1157,7 +1158,7 @@ else
     {
     query << "SELECT sp_add_session_log_entry("
                  "'" << elogin << "', "
-                 "CAST('" << Int2TS(stgTime) << "' AS TIMESTAMP), "
+                 "CAST('" << formatTime(stgTime) << "' AS TIMESTAMP), "
                  "'c', CAST('"
                  << inet_ntostring(ip) << "/32' AS INET), 0, 0, '')";
     }
@@ -1249,7 +1250,7 @@ if (EscapeString(ereason))
         // Old database version - no freeMb logging support
         query << "SELECT sp_add_session_log_entry("
                     "'" << elogin << "', "
-                    "CAST('" << Int2TS(stgTime) << "' AS TIMESTAMP), "
+                    "CAST('" << formatTime(stgTime) << "' AS TIMESTAMP), "
                     "'d', CAST('0.0.0.0/0' AS INET), "
                     << cash << ")";
         }
@@ -1257,7 +1258,7 @@ if (EscapeString(ereason))
         {
         query << "SELECT sp_add_session_log_entry("
                     "'" << elogin << "', "
-                    "CAST('" << Int2TS(stgTime) << "' AS TIMESTAMP), "
+                    "CAST('" << formatTime(stgTime) << "' AS TIMESTAMP), "
                     "'d', CAST('0.0.0.0/0' AS INET), "
                     << cash << ", " << freeMb << ", '" << ereason << "')";
         }
@@ -1397,8 +1398,8 @@ for (it = statTree.begin(); it != statTree.end(); ++it)
                 "(till_time, from_time, fk_user, "
                  "dir_num, ip, download, upload, cost) "
              "VALUES ("
-                "CAST('" << Int2TS(currTime) << "' AS TIMESTAMP), "
-                "CAST('" << Int2TS(lastStat) << "' AS TIMESTAMP), "
+                "CAST('" << formatTime(currTime) << "' AS TIMESTAMP), "
+                "CAST('" << formatTime(lastStat) << "' AS TIMESTAMP), "
                 "(SELECT pk_user FROM tb_users WHERE name = '" << elogin << "'), "
                 << it->first.dir << ", "
                 << "CAST('" << inet_ntostring(it->first.ip) << "' AS INET), "
index ef70505389fc372eb2425398903256a805e4882f..9a5101e121c04a605e1698f08305445e31687818 100644 (file)
@@ -113,35 +113,6 @@ delete[] buf;
 return 0;
 }
 
-std::string POSTGRESQL_STORE::Int2TS(time_t ts) const
-{
-struct tm brokenTime;
-
-brokenTime.tm_wday = 0;
-brokenTime.tm_yday = 0;
-brokenTime.tm_isdst = 0;
-
-gmtime_r(&ts, &brokenTime);
-
-char buf[32];
-strftime(buf, 32, "%Y-%m-%d %H:%M:%S", &brokenTime);
-
-return buf;
-}
-
-time_t POSTGRESQL_STORE::TS2Int(const std::string & ts) const
-{
-struct tm brokenTime;
-
-brokenTime.tm_wday = 0;
-brokenTime.tm_yday = 0;
-brokenTime.tm_isdst = 0;
-
-stg_strptime(ts.c_str(), "%Y-%m-%d %H:%M:%S", &brokenTime);
-
-return stg_timegm(&brokenTime);
-}
-
 void POSTGRESQL_STORE::MakeDate(std::string & date, int year, int month) const
 {
 struct tm brokenTime;
index c6785cead85c26f71ea5e273014f0d71428a8d5f..a3084fbcd810c781fdd895be95c6148b797400f1 100644 (file)
@@ -145,3 +145,24 @@ else
     return tariffData.dirPrice[dir].priceDayA;
 }
 //-----------------------------------------------------------------------------
+std::string TARIFF_IMPL::TariffChangeIsAllowed(const TARIFF & to) const
+{
+switch (GetChangePolicy())
+    {
+    case TARIFF::ALLOW:
+        return "";
+    case TARIFF::TO_CHEAP:
+        if (to.GetFee() < GetFee())
+            return "";
+        else
+            return "New tariff '" + to.GetName() + "' is more expensive than current tariff '" + GetName() + "'. The policy is '" + TARIFF::ChangePolicyToString(GetChangePolicy()) + "'.";
+    case TARIFF::TO_EXPENSIVE:
+        if (to.GetFee() >= GetFee())
+            return "";
+        else
+            return "New tariff '" + to.GetName() + "' is more cheap than current tariff '" + GetName() + "'. The policy is '" + TARIFF::ChangePolicyToString(GetChangePolicy()) + "'.";
+    case TARIFF::DENY:
+        return "Current tariff '" + GetName() + "', new tariff '" + to.GetName() + "'. The policy is '" + TARIFF::ChangePolicyToString(GetChangePolicy()) + "'.";
+    }
+}
+//-----------------------------------------------------------------------------
index 324be186aa9a57a95e1830017e55c4aa1884b198..4948b3f81bed00dd77a0247ab1d05b8e72596280 100644 (file)
@@ -73,6 +73,7 @@ public:
     double  GetFree() const { return tariffData.tariffConf.free; }
     PERIOD  GetPeriod() const { return tariffData.tariffConf.period; }
     CHANGE_POLICY GetChangePolicy() const { return tariffData.tariffConf.changePolicy; }
+    time_t GetChangePolicyTimeout() const { return tariffData.tariffConf.changePolicyTimeout; }
 
     void    Print() const;
 
@@ -88,6 +89,7 @@ public:
     TARIFF_IMPL & operator=(const TARIFF_IMPL & t);
     bool     operator==(const TARIFF_IMPL & rhs) const { return GetName() == rhs.GetName(); }
     bool     operator!=(const TARIFF_IMPL & rhs) const { return GetName() != rhs.GetName(); }
+    std::string TariffChangeIsAllowed(const TARIFF & to) const;
 
 private:
     TARIFF_DATA     tariffData;
index c160db75cb8a39ec8fd402a076fefe41d504780c..f3410368a179e097477f5e1303a3b6d7dafc416e 100644 (file)
@@ -1178,10 +1178,24 @@ if (nextTariff.ConstData() != "")
     {
     const TARIFF * nt = tariffs->FindByName(nextTariff);
     if (nt == NULL)
+        {
         WriteServLog("Cannot change tariff for user %s. Tariff %s not exist.",
                      login.c_str(), property.tariffName.Get().c_str());
+        }
     else
-        property.tariffName.Set(nextTariff, sysAdmin, login, store);
+        {
+        std::string message = tariff->TariffChangeIsAllowed(*nt);
+        if (message.empty())
+            {
+            property.tariffName.Set(nextTariff, sysAdmin, login, store);
+            }
+        else
+            {
+            WriteServLog("Tariff change is prohibited for user %s. %s",
+                         login.c_str(),
+                         message.c_str());
+            }
+        }
     ResetNextTariff();
     WriteConf();
     }
index 76a1b42144b38aaec9382d516c73b8d991e6a61d..a4722a00f3e301b9bc84dc6a500552d802dfefbb 100644 (file)
@@ -749,6 +749,38 @@ void SwapBytes(int64_t & value)
     value = temp;
 }
 //---------------------------------------------------------------------------
+std::string formatTime(time_t ts)
+{
+char buf[32];
+struct tm brokenTime;
+
+brokenTime.tm_wday = 0;
+brokenTime.tm_yday = 0;
+brokenTime.tm_isdst = 0;
+
+gmtime_r(&ts, &brokenTime);
+
+strftime(buf, 32, "%Y-%m-%d %H:%M:%S", &brokenTime);
+
+return buf;
+}
+//---------------------------------------------------------------------------
+time_t readTime(const std::string & ts)
+{
+if (ts == "0000-00-00 00:00:00")
+    return 0;
+
+struct tm brokenTime;
+
+brokenTime.tm_wday = 0;
+brokenTime.tm_yday = 0;
+brokenTime.tm_isdst = 0;
+
+stg_strptime(ts.c_str(), "%Y-%m-%d %H:%M:%S", &brokenTime);
+
+return stg_timegm(&brokenTime);
+}
+//---------------------------------------------------------------------------
 int str2x(const std::string & str, int32_t & x)
 {
 x = static_cast<int32_t>(strtol(str.c_str(), NULL, 10));
index 623d82d073aed1552bf531df2e40e4d86080f560..6201233d2b94941bad3fe04575925e3412800169 100644 (file)
@@ -159,6 +159,8 @@ bool WriteAll(int sd, const void * source, size_t size);
 
 std::string ToPrintable(const std::string & src);
 
+std::string formatTime(time_t value);
+time_t readTime(const std::string & value);
 //-----------------------------------------------------------------------------
 int str2x(const std::string & str, int32_t & x);
 int str2x(const std::string & str, uint32_t & x);
index ed2e4447744679b8e9bc2928018198b5f03c9ec8..a9c3e555093a87e8e60cc7c7d946cfdf347865b8 100644 (file)
@@ -35,6 +35,7 @@
 
 #include <cerrno> // E*
 #include <cstring>
+#include <sstream>
 #include <cstdlib>
 #include <cstdio>
 
@@ -345,6 +346,14 @@ param_val[param] = buf;
 changed = true;
 }
 //---------------------------------------------------------------------------
+void CONFIGFILE::WriteTime(const std::string & param, time_t val)
+{
+std::stringstream ss;
+ss<<val;
+param_val[param] = ss.str();
+changed = true;
+}
+//---------------------------------------------------------------------------
 int CONFIGFILE::ReadDouble(const std::string & param, double * val, double defaultVal) const
 {
 const std::map<std::string, std::string, StringCaseCmp_t>::const_iterator it(param_val.find(param));
index 48e07b6282bd2be594b35deee7d5f69a206d4779..49bb6ff336565ea6e47b4f2ff5ac5156df605d30 100644 (file)
@@ -67,6 +67,7 @@ public:
     void WriteString(const std::string & param, const std::string& val);
     void WriteInt(const std::string & param, int64_t val);
     void WriteDouble(const std::string & param, double val);
+    void WriteTime(const std::string & param, time_t val);
 
     int Error() const;
     int Flush() const;
index 7252a2378a13cb63e77083584363d00fe2b9f765..188aee8a1802cdad240568d766ab392ca783e7a5 100644 (file)
@@ -343,5 +343,113 @@ namespace tut
         ensure_equals("1101 == 0", tariff.GetPriceWithTraffType(0, 6 * 1024 * 1024, 0, 1286461245), 0); // Near 17:30, 6 > 4 DA (ignore night)
         ensure_equals("1110 == 0", tariff.GetPriceWithTraffType(0, 0 * 1024 * 1024, 0, 1286479245), 0); // Near 22:30, 0 < 4 DA (ignore night)
         ensure_equals("1111 == 0", tariff.GetPriceWithTraffType(0, 6 * 1024 * 1024, 0, 1286479245), 0); // Near 22:30, 6 > 4 DA (ignore night)
+   }
+
+    template<>
+    template<>
+    void testobject::test<7>()
+    {
+        set_test_name("Check changePolicy - ALLOW");
+
+        TARIFF_DATA td("test");
+        td.tariffConf.changePolicy = TARIFF::ALLOW;
+        td.tariffConf.fee = 100;
+        TARIFF_IMPL tariff(td);
+
+        td.tariffConf.fee = 50;
+        TARIFF_IMPL cheaper(td);
+
+        ensure_equals("Allow cheaper", tariff.TariffChangeIsAllowed(cheaper).empty(), true);
+
+        td.tariffConf.fee = 100;
+        TARIFF_IMPL equal(td);
+
+        ensure_equals("Allow equal", tariff.TariffChangeIsAllowed(equal).empty(), true);
+
+        td.tariffConf.fee = 150;
+        TARIFF_IMPL expensive(td);
+
+        ensure_equals("Allow expensive", tariff.TariffChangeIsAllowed(expensive).empty(), true);
+    }
+
+    template<>
+    template<>
+    void testobject::test<8>()
+    {
+        set_test_name("Check changePolicy - TO_CHEAP");
+
+        TARIFF_DATA td("test");
+        td.tariffConf.changePolicy = TARIFF::TO_CHEAP;
+        td.tariffConf.fee = 100;
+        TARIFF_IMPL tariff(td);
+
+        td.tariffConf.fee = 50;
+        TARIFF_IMPL cheaper(td);
+
+        ensure_equals("Allow cheaper", tariff.TariffChangeIsAllowed(cheaper).empty(), true);
+
+        td.tariffConf.fee = 100;
+        TARIFF_IMPL equal(td);
+
+        ensure_equals("Prohibit equal", tariff.TariffChangeIsAllowed(equal).empty(), false);
+
+        td.tariffConf.fee = 150;
+        TARIFF_IMPL expensive(td);
+
+        ensure_equals("Prohibit expensive", tariff.TariffChangeIsAllowed(expensive).empty(), false);
+    }
+
+    template<>
+    template<>
+    void testobject::test<9>()
+    {
+        set_test_name("Check changePolicy - TO_EXPENSIVE");
+
+        TARIFF_DATA td("test");
+        td.tariffConf.changePolicy = TARIFF::TO_EXPENSIVE;
+        td.tariffConf.fee = 100;
+        TARIFF_IMPL tariff(td);
+
+        td.tariffConf.fee = 50;
+        TARIFF_IMPL cheaper(td);
+
+        ensure_equals("Prohibit cheaper", tariff.TariffChangeIsAllowed(cheaper).empty(), false);
+
+        td.tariffConf.fee = 100;
+        TARIFF_IMPL equal(td);
+
+        ensure_equals("Allow equal", tariff.TariffChangeIsAllowed(equal).empty(), true);
+
+        td.tariffConf.fee = 150;
+        TARIFF_IMPL expensive(td);
+
+        ensure_equals("Allow expensive", tariff.TariffChangeIsAllowed(expensive).empty(), true);
+    }
+
+    template<>
+    template<>
+    void testobject::test<10>()
+    {
+        set_test_name("Check changePolicy - DENY");
+
+        TARIFF_DATA td("test");
+        td.tariffConf.changePolicy = TARIFF::DENY;
+        td.tariffConf.fee = 100;
+        TARIFF_IMPL tariff(td);
+
+        td.tariffConf.fee = 50;
+        TARIFF_IMPL cheaper(td);
+
+        ensure_equals("Prohibit cheaper", tariff.TariffChangeIsAllowed(cheaper).empty(), false);
+
+        td.tariffConf.fee = 100;
+        TARIFF_IMPL equal(td);
+
+        ensure_equals("Prohibit equal", tariff.TariffChangeIsAllowed(equal).empty(), false);
+
+        td.tariffConf.fee = 150;
+        TARIFF_IMPL expensive(td);
+
+        ensure_equals("Prohibit expensive", tariff.TariffChangeIsAllowed(expensive).empty(), false);
     }
 }