<?php
$moduleVersion  = "3.36";
$moduleRevision = "159";

// ========================================================================
// Custom configuration
// ========================================================================

$isDebugMode = 1; //Debug Mode [0/1]

// ========================================================================
$additionalDataMerge = array();
require_once(realpath(dirname(__FILE__)) . '/usm_abills.conf.php');

$billingName   = 'abills_db';
$moduleName    = "usm_abills";
$isAddOnModule = 1;

$billing = new Billing();

class Billing {

    public function verifyBillingApi() {
        global $billingDBHost, $billingDBUser, $billingDBPassword, $billingDBName, $moduleHelper, $isDatabaseOpen;
        global $billingSqlData;
        //Billing custom conf
        global $confAdrDistrictMode, $confDateConnectSrc, $confUserImportExpr, $confAddFieldPhone, $confAddFieldCellPhone;
        global $confAddFieldEntrance, $confAddFieldFloor, $confImportTaxNumber, $confTaxNumberField, $confImportPassport;
        global $confPassportField, $confIsSavePasswordToComment, $confIsCidIp, $confUserStateSrc, $confWorkState2;
        global $confWorkState5, $confIsHideDvLogData, $confIsHideIpAdrFromDvCalls, $confIsHideCidFromDvMain;
        global $confIsIpAdrOnlyFromDhcpHosts, $confImportNasIp, $confImportNasPort, $confImportNasName, $confImportGroup;
        global $confIsIpAdrSkipFromDhcpHosts, $confUserAccountSrc, $confIsImportTags, $billingDBPort, $confPasswordSecretKey;

        if ('' == $confAddFieldPhone) {
            $confAddFieldPhone = "phone";
        }

        //Фиксируем специфические параметры
        Log::w("confAdrDistrictMode                      : " . $confAdrDistrictMode);
        Log::w("confDateConnectSrc                       : " . $confDateConnectSrc);
        Log::w("confUserImportExpr                       : " . $confUserImportExpr);
        Log::w("confAddFieldPhone                        : " . $confAddFieldPhone);
        Log::w("confAddFieldCellPhone                    : " . $confAddFieldCellPhone);
        Log::w("confAddFieldEntrance                     : " . $confAddFieldEntrance);
        Log::w("confAddFieldFloor                        : " . $confAddFieldFloor);
        Log::w("confImportTaxNumber                      : " . $confImportTaxNumber);
        Log::w("confTaxNumberField                       : " . $confTaxNumberField);
        Log::w("confImportPassport                       : " . $confImportPassport);
        Log::w("confPassportField                        : " . $confPassportField);
        Log::w("confIsSavePasswordToComment              : " . $confIsSavePasswordToComment);
        Log::w("confIsCidIp                              : " . $confIsCidIp);
        Log::w("confUserStateSrc                         : " . $confUserStateSrc);
        Log::w("confWorkState2                           : " . $confWorkState2);
        Log::w("confWorkState5                           : " . $confWorkState5);
        Log::w("confIsHideDvLogData                      : " . $confIsHideDvLogData);
        Log::w("confIsHideIpAdrFromDvCalls               : " . $confIsHideIpAdrFromDvCalls);
        Log::w("confIsHideCidFromDvMain                  : " . $confIsHideCidFromDvMain);
        Log::w("confIsIpAdrOnlyFromDhcpHosts             : " . $confIsIpAdrOnlyFromDhcpHosts);
        Log::w("confImportNasIp                          : " . $confImportNasIp);
        Log::w("confImportNasPort                        : " . $confImportNasPort);
        Log::w("confImportNasName                        : " . $confImportNasName);
        Log::w("confImportGroup                          : " . $confImportGroup);
        Log::w("confIsIpAdrSkipFromDhcpHosts             : " . $confIsIpAdrSkipFromDhcpHosts);
        Log::w("confUserAccountSrc                       : " . $confUserAccountSrc);
        Log::w("confIsImportTags                         : " . $confIsImportTags);
        Log::w("billingDBPort                            : " . $billingDBPort);
        if ('' != $confPasswordSecretKey) {
            Log::w("confPasswordSecretKey                    : enabled");
        } else {
            Log::w("confPasswordSecretKey                    : not set");
        }

        //Открываем соединение с базой биллинга
        if ('' == $billingDBPort) {
            $billingDBPort = 3306;
        }
        Database::connect($billingDBHost, $billingDBUser, $billingDBPassword, $billingDBName, $billingDBPort);

        $data           = Database::query("SELECT NOW()");
        $billingSqlData = $data[0];

        $data      = Database::query("SHOW VARIABLES WHERE `Variable_name` LIKE 'version_compile%'", true);
        $billingOS = $data['version_compile_os']['Value'] . ' ' . $data['version_compile_machine']['Value'];

        $isDatabaseOpen = 1;

        $responce = array(
            'date' => $billingSqlData,
            'os' => $billingOS,
            'billing' => array(
                'name' => '-',
                'version' => '-'
            )
        );
        return $responce;
    }

    public function getTariffList() {
        //Данные по тарифам
        $query = "
            SELECT
                t.id AS 'id',
                t.name AS 'name',
                t.month_fee AS 'month_fee',
                t.day_fee AS 'day_fee',
                tt.prepaid AS 'traffic',
                tt.in_speed AS 'speed_up',
                tt.out_speed AS 'speed_down',
                d.speed AS 'speed_user'
            FROM
                tarif_plans AS t
            LEFT JOIN
                intervals AS i
            ON
                i.tp_id = t.tp_id
            LEFT JOIN
                trafic_tarifs AS tt
            ON
                tt.interval_id = i.id
            LEFT JOIN
                dv_main AS d
            ON
                d.tp_id = t.id
            AND
                d.speed > 0
            GROUP BY
                t.id";
        $data  = Database::query($query, true, 'id');
        Log::rawLog('tariff', $data);
        $responce = array();
        foreach ($data as $i => $value) {
            $payment          = $value['month_fee'];
            $payment_interval = 30;
            if (0 == $payment) {
                $payment          = $value['day_fee'];
                $payment_interval = 1;
            }
            $speedUp   = $value['speed_up'];
            $speedDown = $value['speed_down'];
            if (0 == $speedUp) {
                $speedUp   = $value['speed_user'];
                $speedDown = $value['speed_user'];
            }
            $traffic = floor($value['traffic']);
            if (0 == $traffic) {
                $traffic = -1;
            }
            $responce[$value['id']] = array(
                'id' => $value['id'],
                'name' => $value['name'],
                'payment' => $payment,
                'payment_interval' => $payment_interval,
                'traffic' => $traffic,
                'service_type' => 0,
                'speed' => array(
                    'up' => $speedUp,
                    'down' => $speedDown
                )
            );
        }
        Log::d(json_encode($responce));
        return $responce;
    }

    public function getBillingState() {
        $responce = array(
            2 => array(
                'id' => 2,
                'name' => 'Active',
                'functional' => 'work'
            ),
            1 => array(
                'id' => 1,
                'name' => 'Pause',
                'functional' => 'pause'
            ),
            0 => array(
                'id' => 0,
                'name' => 'Stop',
                'functional' => 'stop'
            )
        );
        return $responce;
    }

    public function getBillingTags() {
        global $confIsImportTags;
        $responce = array();
        if (1 == $confIsImportTags) {
            $query = "
                SELECT
                    id AS 'id',
                    name AS 'name'
                FROM
                    tags";
            $responce  = Database::query($query, true, 'id');
            Log::d(str_replace("\n", ' ', json_encode($responce)));
        }
        return $responce;
    }

    public function getBillingGroup() {
        $responce = array();
        return $responce;
    }

    public function getHouseList() {
        global $arrayBillingAddress;
        Log::d(json_encode($arrayBillingAddress['house']));
        return $arrayBillingAddress['house'];
    }

    public function getStreetList() {
        global $arrayBillingAddress;
        Log::d(json_encode($arrayBillingAddress['street']));
        return $arrayBillingAddress['street'];
    }

    public function getCityList() {
        global $arrayBillingAddress;
        //Billing custom conf
        global $confAdrDistrictMode;
        $arrayBillingAddress = array(
            'city' => array(),
            'street' => array(),
            'house' => array()
        );
        //Загружаем массив всех адресов
        $query = "
            SELECT
                b.id AS 'id',
                UPPER(b.number) AS 'number',
                s.id AS 'street_id',
                s.name AS 'street',
                d.city AS 'city',
                d.name AS 'district'
            FROM
                builds AS b
            INNER JOIN
                streets AS s
            ON
                s.id = b.street_id
            LEFT JOIN
                districts AS d
            ON
                d.id = s.district_id";
        $data  = Database::query($query, true, 'id');
        Log::rawLog('address', $data);
        foreach ($data as $i => $value) {
            $street   = $value['street'];
            $streetId = $value['street_id'];
            $houseId  = $value['id'];
            $number   = floor($value['number']);
            if (strlen($value['number']) != strlen($number)) {
                $block = trim(substr($value['number'], strlen($number), strlen($value['number'])));
                $block = str_replace('/', '', $block);
            } else {
                $block = '';
            }
            $city = '';
            if ($confAdrDistrictMode == 1) {
                $street = $value['district'] . ' ' . $street;
            }
            if ($confAdrDistrictMode == 2) {
                $city = $value['district'];
            }
            if ($confAdrDistrictMode == 3) {
                $city = $value['city'];
            }
            $cityId = md5($city);

            $arrayBillingAddress['city'][$cityId]     = array(
                'id' => $cityId,
                'name' => $city
            );
            $arrayBillingAddress['street'][$streetId] = array(
                'id' => $streetId,
                'city_id' => $cityId,
                'name' => $street
            );
            $arrayBillingAddress['house'][$houseId]   = array(
                'id' => $houseId,
                'street_id' => $streetId,
                'number' => $number,
                'block' => $block
            );
        }
        Log::d(json_encode($arrayBillingAddress['city']));
        return $arrayBillingAddress['city'];
    }

    public function getCustomerList() {
        global $billingSqlData, $additionalDataMerge, $lastPaidId, $isUpdateTraffic;
        //Billing custom conf
        global $confDateConnectSrc, $confUserImportExpr, $confAddFieldPhone, $confAddFieldCellPhone, $confWorkState2;
        global $confAddFieldEntrance, $confAddFieldFloor, $confImportTaxNumber, $confTaxNumberField, $confWorkState5;
        global $confImportPassport, $confPassportField, $confIsSavePasswordToComment, $confIsCidIp, $confUserStateSrc;
        global $confIsHideDvLogData, $confIsHideIpAdrFromDvCalls, $confIsHideCidFromDvMain, $confIsIpAdrOnlyFromDhcpHosts;
        global $confImportGroup, $confIsIpAdrSkipFromDhcpHosts, $confImportNasIp, $confImportNasPort, $confImportNasName;
        global $confUserAccountSrc, $confIsImportTags, $confPasswordSecretKey;

        //Формируем массив доп.полей
        if (0 < $confImportNasIp) {
            $additionalDataMerge[$confImportNasIp] = $confImportNasIp;
        }
        if (0 < $confImportNasPort) {
            $additionalDataMerge[$confImportNasPort] = $confImportNasPort;
        }
        if (0 < $confImportNasName) {
            $additionalDataMerge[$confImportNasName] = $confImportNasName;
        }
        if (0 < $confImportTaxNumber) {
            $additionalDataMerge[$confImportTaxNumber] = $confImportTaxNumber;
        }
        if (0 < $confImportPassport) {
            $additionalDataMerge[$confImportPassport] = $confImportPassport;
        }
        if (0 < $confImportGroup) {
            $additionalDataMerge[$confImportGroup] = $confImportGroup;
        }

        //Глобальные данные по абонентам
        Log::d("Load Customer");
        //Проверяем - есть ли поля
        $data = Database::query("SHOW COLUMNS FROM users WHERE Field = 'deleted'", true, 'Field');
        if (isset($data['deleted'])) {
            $isFieldDeleted = 1;
        } else {
            $isFieldDeleted = 0;
        }
        if (1 == $confDateConnectSrc) {
            $fieldDateConnect = "u.registration";
        } else {
            $fieldDateConnect = "MIN(dl.start)";
        }
        if ('' != $confAddFieldCellPhone) {
            $fieldCellPhone = "up." . $confAddFieldCellPhone;
        } else {
            $fieldCellPhone = "''";
        }
        if ('' != $confAddFieldEntrance) {
            $fieldEntrance = "up." . $confAddFieldEntrance;
        } else {
            $fieldEntrance = "''";
        }
        if ('' != $confAddFieldFloor) {
            $fieldFloor = "up." . $confAddFieldFloor;
        } else {
            $fieldFloor = "''";
        }
        if (0 < $confImportTaxNumber) {
            $fieldTaxNumber = "up." . $confTaxNumberField;
        } else {
            $fieldTaxNumber = "''";
        }
        if (0 < $confImportPassport) {
            $fieldPassportData = "up." . $confPassportField;
        } else {
            $fieldPassportData = "''";
        }
        if (1 == $confIsCidIp) {
            $fieldCid = "INET_ATON(dm.cid)";
        } else {
            $fieldCid = "REPLACE(REPLACE(UPPER(dm.cid), ':', ''), '-', '')";
        }

        $ipOwner = array();

        //Центральная выборка по абонентам
        $query = "
            SELECT
                u.uid AS 'id',
                u.id AS 'login',
                up.fio AS 'full_name',";
        if ('' != $confPasswordSecretKey) {
            $query .= "
                decode(u.password, '" . $confPasswordSecretKey. "') AS 'password',";
        } else {
            $query .= "
                u.password AS 'password',";
        }
        $query .= "
                " . $fieldDateConnect . " AS 'date_connect',
                u.credit AS 'credit',
                u.reduction AS 'discount',
                dm.tp_id AS 'tariff',
                dm.disable AS 'state_id',
                u.disable AS 'state_id2',
                up.contract_id AS 'agreement_number',
                up.contract_date AS 'agreement_date',
                IFNULL(b.deposit, 0) AS 'balance',
                up." . $confAddFieldPhone . " AS 'phone',
                " . $fieldCellPhone . " AS 'phone_cell',
                up.email AS 'email',
                IFNULL(up.comments, '') AS 'comment',
                up.location_id AS 'house_id',
                " . $fieldEntrance . " AS 'entrance',
                " . $fieldFloor . " AS 'floor',
                up.address_flat AS 'apartment',
                u.company_id AS 'company_id',
                IFNULL(b2.deposit, 0) AS 'company_balance',
                u.bill_id AS 'account_number',
                c.bill_id AS 'c_bill_id',
                c.ext_bill_id AS 'c_ext_bill_id',
                u.ext_bill_id AS 'u_ext_bill_id',
                g.name AS 'group',
                " . $confIsCidIp . " AS 'cid_ip',
                " . $fieldTaxNumber . " AS 'tax_number',
                " . $fieldPassportData . " AS 'passport_data'
            FROM
                users AS u";
        if (2 == $confDateConnectSrc) {
            $query .= "
            LEFT JOIN
                (SELECT uid, MIN(start) AS start FROM dv_log GROUP BY uid) AS dl
            ON
                u.uid = dl.uid";
        }
        $query .= "
            LEFT JOIN
                groups AS g
            ON
                g.gid = u.gid
            LEFT JOIN
                dv_main AS dm
            ON
                dm.uid = u.uid
            LEFT JOIN
                users_pi AS up
            ON
                up.uid = u.uid
            LEFT JOIN
                bills AS b
            ON
                b.uid = u.uid
            AND
                b.id = u.bill_id
            LEFT JOIN
                companies AS c
            ON
                c.id = u.company_id
            LEFT JOIN
                bills AS b2
            ON
                b2.id = c.bill_id";
        if (1 == $isFieldDeleted) {
            $query .= "
            WHERE
                IFNULL(u.deleted, 0) <> 1";
            if ('' != $confUserImportExpr) {
                $query .= "
            AND
                " . $confUserImportExpr;
            }
        } else {
            if ('' != $confUserImportExpr) {
                $query .= "
            WHERE
                " . $confUserImportExpr;
            }
        }
        $query .= "
            GROUP BY
                u.uid";
        $data = Database::query($query, true, 'id');
        Log::rawLog('customer', $data, 1);
        foreach ($data as $i => $value) {
            $customerId = $value['id'];
            $comment    = $value['comment'];
            if (1 == $confIsSavePasswordToComment) {
                $comment .= ' pass: ' . $value['password'];
            }
            $dateConnect = $value['date_connect'];
            if ('' == $dateConnect) {
                $dateConnect = '1970-01-01';
            }
            $phone     = $value['phone'];
            $phoneCell = $value['phone_cell'];
            $stateId   = 2; //Play
            if ('u.disable' == $confUserStateSrc) {
                /*
                users.disable
                0 активно
                1 отключено
                2 регистрация подтвердить (хз, нашел методом тыка)
                3 и больше , показывают статус "Отключено"
                */
                if (0 < $value['state_id2']) {
                    $stateId = 0; //stop
                }
            } else {
                /*
                dv_main.disable
                0 Активно
                1 Отключено
                2 Не активизирован
                3 Приостановление
                4 Отключено: Неуплата
                5 Cлишком маленький депозит
                6.Заблокирован из-за вирусов
                */
                switch ($value['state_id']) {
                    case '':
                    case 1:
                    case 4:
                    case 6:
                        $stateId = 0; //stop
                        break;
                    case 3:
                        $stateId = 1; //pause
                        break;
                    case 2:
                        $stateId = $confWorkState2;
                        break;
                    case 5:
                        $stateId = $confWorkState5;
                        break;
                }
            }
            if (0 < $value['company_id']) {
                $balance       = $value['company_balance'];
                if ('bill_id' == $confUserAccountSrc) {
                    $accountNumber = $value['account_number'];
                } else {
                    $accountNumber = 0;
                }
                $isCorporate   = 1;
            } else {
                $balance = $value['balance'];
                if ('' == $confUserAccountSrc || 'uid' == $confUserAccountSrc) {
                    $accountNumber = $value['id'];
                } else {
                    $accountNumber = $value['account_number'];
                }
                $isCorporate = 0;
            }
            $floor = $value['floor'];
            if (1 > $floor) {
                $floor = 1;
            }

            $responce[$customerId] = array(
                'id' => $customerId,
                'flag_corporate' => $isCorporate,
                'login' => $value['login'],
                'full_name' => $value['full_name'],
                'comment' => $comment,
                'date_connect' => $dateConnect,
                'state_id' => $stateId,
                'account_number' => $accountNumber,
                'balance' => $balance,
                'address' => array(
                    0 => array(
                        'type' => 'connect',
                        'house_id' => $value['house_id'],
                        'entrance' => $value['entrance'],
                        'floor' => $floor,
                        'apartment' => array(
                            'number' => $value['apartment']
                        )
                    )
                ),
                'email' => array(
                    0 => array(
                        'address' => $value['email']
                    )
                ),
                'agreement' => array(
                    0 => array(
                        'number' => $value['agreement_number'],
                        'date' => $value['agreement_date']
                    )
                ),
                'credit' => $value['credit'],
                'discount' => floor($value['discount']),
                'tariff' => array(
                    'current' => array(
                        $value['tariff'] => array(
                            'id' => $value['tariff']
                        )
                    )
                ),
                'phone' => array(
                    0 => array(
                        'flag_main' => 1,
                        'number' => $phoneCell
                    ),
                    1 => array(
                        'number' => $phone
                    )
                )
            );
            if (0 < $confImportTaxNumber) {
                $responce[$customerId]['additional_data'][$confImportTaxNumber] = array(
                    'id' => $confImportTaxNumber,
                    'value' => $value['tax_number']
                );
            }
            if (0 < $confImportPassport) {
                $responce[$customerId]['additional_data'][$confImportPassport] = array(
                    'id' => $confImportPassport,
                    'value' => $value['passport_data']
                );
            }
            if (0 < $confImportGroup) {
                $responce[$customerId]['additional_data'][$confImportGroup] = array(
                    'id' => $confImportGroup,
                    'value' => $value['group']
                );
            }
            if (1 != $confIsHideCidFromDvMain && 1 != $confIsIpAdrOnlyFromDhcpHosts) {
                $cid     = $value['cid_ip'];
                $array   = explode(';', $cid);
                $cidMac1 = isset($array[0]) ? $array[0] : '';
                $cidMac2 = isset($array[1]) ? $array[1] : '';
                $cidMac3 = isset($array[2]) ? $array[2] : '';
                $mac     = '';
                if (1 == $confIsCidIp) {
                    $ip = trim(sprintf("%u\n", ip2long($cid)));
                } else {
                    $array = explode('/', $cid);
                    $cidIp = isset($array[0]) ? $array[0] : '';
                    $mac   = isset($array[1]) ? $array[1] : '';
                    $ip    = trim(sprintf("%u\n", ip2long($cidIp)));
                    if (1 > $ip && 12 == strlen($cidIp)) {
                        $mac = $cidIp;
                    }
                    if ('' == $cidIp) {
                        $mac = $cidMac2;
                    }
                }
                if ('' == $mac) {
                    //001D.60C3.AF9FVLAN1235325
                    //90F6.5277.ACD5VLAN1875297
                    $array = explode('VLAN', $cid);
                    $mac   = isset($array[0]) ? $array[0] : '';
                    $mac   = str_replace('.', '', $mac);
                }
                if (12 < strlen($mac)) {
                    $array = explode('VLAN', $mac);
                    $mac   = isset($array[0]) ? $array[0] : '';
                    $mac   = str_replace('.', '', $mac);
                }
                if (0 < $ip) {
                    if ('' == $mac) {
                        $mac = $cidMac2;
                    }
                    //Убираем у прошлого владельца
                    if (isset($ipOwner[$ip])) {
                        $lastCustomerId = $ipOwner[$ip];
                        unset($responce[$lastCustomerId]['ip_mac'][$ip]);
                        if (0 == count($responce[$lastCustomerId]['ip_mac'])) {
                            unset($responce[$lastCustomerId]['ip_mac']);
                        }
                    }
                    $responce[$customerId]['ip_mac'][$ip]['ip'] = $ip;
                    $ipOwner[$ip] = $customerId;
                    if ('' != $mac || !isset($responce[$customerId]['ip_mac'][$ip]['mac'])) {
                        $responce[$customerId]['ip_mac'][$ip]['mac'] = $mac;
                    }
                }
            }
        }

        //Информация по траффику
        $customerAccountData = array();
        if (1 == $isUpdateTraffic) {
            Log::d("Load Traffic");
            $dateStart  = date('Y-m-01 00:00:00', strtotime($billingSqlData));
            $dateFinish = date('Y-m-t 23:59:59', strtotime($billingSqlData));
            $query      = "
                SELECT
                    uid,
                    SUM(recv) AS 'up',
                    SUM(sent) AS 'down'
                FROM
                    dv_log
                WHERE
                    start BETWEEN '" . $dateStart . "' AND '" . $dateFinish . "'
                GROUP BY
                    uid";
            $data       = Database::query($query, true, 'uid');
            foreach ($data as $i => $value) {
                $customerId = $value['uid'];
                if (isset($responce[$customerId])) {
                    $responce[$customerId]['traffic']['month'] = array(
                        'up' => $value['up'],
                        'down' => $value['down']
                    );
                }
            }
            Log::d("Load Traffic. Finish Stage 1");
            $query = "
                SELECT
                    uid,
                    SUM(acct_input_octets + (acct_input_gigawords * 4294967295)) AS 'up',
                    SUM(acct_output_octets + (acct_output_gigawords * 4294967295)) AS 'down'
                FROM
                    dv_calls
                WHERE
                    started >= '" . $dateStart . "'
                GROUP BY
                    uid";
            $data  = Database::query($query, true, 'uid');
            foreach ($data as $i => $value) {
                $customerId = $value['uid'];
                if (isset($responce[$customerId])) {
                    if (!isset($responce[$customerId]['traffic']['month']['up'])) {
                        $responce[$customerId]['traffic']['month']['up'] = 0;
                    }
                    $responce[$customerId]['traffic']['month']['up'] += $value['up'];

                    if (!isset($responce[$customerId]['traffic']['month']['down'])) {
                        $responce[$customerId]['traffic']['month']['down'] = 0;
                    }
                    $responce[$customerId]['traffic']['month']['down'] += $value['down'];
                }
            }
            Log::d("Load Traffic. Finish");
        }

        //Информация по активности
        if (1 != $confIsHideDvLogData) {
            Log::d("Load Activity From dv_log. Start");
            $data   = Database::query("SELECT MAX(uid) FROM dv_log", false);
            $uidMax = $data[0];
            Log::d("uidMax: " . $uidMax);
            $data     = Database::query("SELECT COUNT(uid) FROM dv_log", false);
            $uidCount = $data[0];
            Log::d("uidCount: " . $uidCount);
            $uidCount -= $uidMax;
            if (0 > $uidCount) {
                $uidCount = 0;
            }
            if (1 == $confIsCidIp) {
                $fieldCid = "INET_ATON(CID)";
            } else {
                $fieldCid = "REPLACE(REPLACE(REPLACE(UPPER(CID), ':', ''), '-', ''), '.', '')";
            }
            $query = "
                SELECT
                    uid,
                    start AS 'date_activity',
                    ip,
                    " . $fieldCid . " AS 'cid'
                FROM
                    dv_log
                LIMIT
                    " . $uidCount . ", " . $uidMax;
            $data  = Database::query($query, true, 'uid');
            foreach ($data as $i => $value) {
                $customerId = $value['uid'];
                if (isset($responce[$customerId])) {
                    $responce[$customerId]['date_activity'] = $value['date_activity'];
                    if (1 != $confIsIpAdrOnlyFromDhcpHosts) {
                        if (1 == $confIsCidIp) {
                            $ip  = $value['cid'];
                            $mac = '';
                        } else {
                            $ip  = $value['ip'];
                            $mac = $value['cid'];
                        }
                        $pos = strpos($value['cid'], '/');
                        if (0 < $pos) {
                            $mac = substr($value['cid'], ($pos + 1));
                        }
                        if (0 < $ip) {
                            //Убираем у прошлого владельца
                            if (isset($ipOwner[$ip])) {
                                $lastCustomerId = $ipOwner[$ip];
                                unset($responce[$lastCustomerId]['ip_mac'][$ip]);
                                if (0 == count($responce[$lastCustomerId]['ip_mac'])) {
                                    unset($responce[$lastCustomerId]['ip_mac']);
                                }
                            }
                            $responce[$customerId]['ip_mac'][$ip]['ip'] = $ip;
                            $ipOwner[$ip] = $customerId;
                            if ('' != $mac || !isset($responce[$customerId]['ip_mac'][$ip]['mac'])) {
                                $responce[$customerId]['ip_mac'][$ip]['mac'] = $mac;
                            }
                        }
                    }
                }
            }
            Log::d("Load Activity From dv_log. Finish");
        }
        $data = Database::query("SHOW TABLES LIKE 'ipn_log'", false);
        if (isset($data[0])) {
            Log::d("Load Activity From ipn_log. Start");
            $data     = Database::query("SELECT MAX(uid) FROM ipn_log", false);
            $uidMax   = $data[0] * 40;
            $data     = Database::query("SELECT COUNT(uid) FROM ipn_log", false);
            $uidCount = $data[0];
            $uidCount -= $uidMax;
            if (0 > $uidCount) {
                $uidCount = 0;
            }
            $query = "
                SELECT
                    uid,
                    start AS 'date_activity',
                    ip
                FROM
                    ipn_log
                WHERE
                    traffic_in <> 0
                AND
                    traffic_out <> 0
                LIMIT
                    " . $uidCount . ", " . $uidMax;
            $data  = Database::query($query, true, 'uid');
            Log::rawLog('customer_ip', $data);
            foreach ($data as $i => $value) {
                $customerId = $value['uid'];
                if (isset($responce[$customerId])) {
                    $responce[$customerId]['date_activity'] = $value['date_activity'];
                    $ip                                     = $value['ip'];
                    if (0 < $ip && 1 != $confIsIpAdrOnlyFromDhcpHosts) {
                        //Убираем у прошлого владельца
                        if (isset($ipOwner[$ip])) {
                            $lastCustomerId = $ipOwner[$ip];
                            unset($responce[$lastCustomerId]['ip_mac'][$ip]);
                            if (0 == count($responce[$lastCustomerId]['ip_mac'])) {
                                unset($responce[$lastCustomerId]['ip_mac']);
                            }
                        }
                        $responce[$customerId]['ip_mac'][$ip]['ip'] = $ip;
                        $ipOwner[$ip] = $customerId;
                    }
                }
            }
            Log::d("Load Activity From ipn_log. Finish");
        }

        Log::d("Load Activity From dv_calls. Start");
        $nasArray = array();
        $query    = "
            SELECT
                dv.uid AS 'uid',
                NOW() AS 'date_activity',
                dv.framed_ip_address AS 'ip',
                REPLACE(REPLACE(REPLACE(UPPER(dv.CID), ':', ''), '-', ''), '.', '') AS 'cid',
                INET_NTOA(dv.nas_ip_address) AS 'nas_ip',
                dv.nas_port_id AS 'nas_port',
                n.name AS 'nas_name',
                n.id,
                n2.name AS 'nas_name2',
                n2.id
            FROM
                dv_calls AS dv
            LEFT JOIN
                nas AS n
            ON
                INET_ATON(n.ip) = dv.nas_ip_address
            LEFT JOIN
                nas AS n2
            ON
                n2.ip = dv.nas_ip_address
            ORDER BY
                dv.started";
        $data     = Database::query($query, true, 'uid');
        Log::rawLog('customer_ip', $data);
        foreach ($data as $i => $value) {
            $customerId = $value['uid'];
            if (isset($responce[$customerId])) {
                $responce[$customerId]['date_activity'] = $value['date_activity'];
                if (1 != $confIsHideIpAdrFromDvCalls && 1 != $confIsIpAdrOnlyFromDhcpHosts) {
                    $ip = $value['ip'];
                    if (0 < $ip) {
                        $mac = $value['cid'];
                        $pos = strpos($mac, '/');
                        if (0 < $pos) {
                            $mac = substr($mac, ($pos + 1));
                        }
                        //Убираем у прошлого владельца
                        if (isset($ipOwner[$ip])) {
                            $lastCustomerId = $ipOwner[$ip];
                            unset($responce[$lastCustomerId]['ip_mac'][$ip]);
                            if (0 == count($responce[$lastCustomerId]['ip_mac'])) {
                                unset($responce[$lastCustomerId]['ip_mac']);
                            }
                        }
                        $responce[$customerId]['ip_mac'][$ip]['ip'] = $ip;
                        $ipOwner[$ip] = $customerId;
                        if ('' != $mac || !isset($responce[$customerId]['ip_mac'][$ip]['mac'])) {
                            $responce[$customerId]['ip_mac'][$ip]['mac'] = $mac;
                        }
                    }
                }
                if (0 < $confImportNasIp || 0 < $confImportNasPort || 0 < $confImportNasName) {
                    $nasIp   = $value['nas_ip'];
                    $nasPort = $value['nas_port'];
                    $nasName = $value['nas_name'];
                    if ('' == $nasName) {
                        $nasName = $value['nas_name2'];
                    }
                    if (0 < $confImportNasIp) {
                        $responce[$customerId]['additional_data'][$confImportNasIp] = array(
                            'id' => $confImportNasIp,
                            'value' => $nasIp
                        );
                    }
                    if (0 < $confImportNasPort) {
                        $responce[$customerId]['additional_data'][$confImportNasPort] = array(
                            'id' => $confImportNasPort,
                            'value' => $nasPort
                        );
                    }
                    if (0 < $confImportNasName) {
                        $responce[$customerId]['additional_data'][$confImportNasName] = array(
                            'id' => $confImportNasName,
                            'value' => $nasName
                        );
                    }
                }
            }
        }
        Log::d("Load Activity From dv_calls. Finish");

        if (1 != $confIsIpAdrSkipFromDhcpHosts) {
            $data = Database::query("SHOW TABLES LIKE 'dhcphosts_hosts'", false);
            if (isset($data[0])) {
                Log::d("Load Data From 'dhcphosts_hosts'");
                //167837703 - 10.1.0.7
                //167837704 - 10.1.0.8
                //167837705 - 10.1.0.9
                $query = "
                    SELECT
                        uid,
                        ip,
                        UCASE(REPLACE(mac, ':', '')) AS 'mac'
                    FROM
                        dhcphosts_hosts
                    WHERE
                        `ip` NOT IN (167837703, 167837704, 167837705)";
                $data  = Database::query($query, true, 'uid');
                Log::rawLog('customer_ip', $data);
                foreach ($data as $i => $value) {
                    $customerId = $value['uid'];
                    if (isset($responce[$customerId])) {
                        $ip = $value['ip'];
                        if (0 < $ip) {
                            $mac = $value['mac'];
                            if ('000000000000' == $mac) {
                                $mac = '';
                            }
                        }
                        //Убираем у прошлого владельца
                        if (isset($ipOwner[$ip])) {
                            $lastCustomerId = $ipOwner[$ip];
                            unset($responce[$lastCustomerId]['ip_mac'][$ip]);
                            if (0 == count($responce[$lastCustomerId]['ip_mac'])) {
                                unset($responce[$lastCustomerId]['ip_mac']);
                            }
                        }
                        $responce[$customerId]['ip_mac'][$ip]['ip'] = $ip;
                        $ipOwner[$ip] = $customerId;
                        if ('' != $mac || !isset($responce[$customerId]['ip_mac'][$ip]['mac'])) {
                            $responce[$customerId]['ip_mac'][$ip]['mac'] = $mac;
                        }
                    }
                }
            }
        }

        //Заявки на смену тарифа
        Log::d("Load Tariff Change Requests. Start");
        $query = "
            SELECT
                uid,
                action AS 'tariff'
            FROM
                shedule
            WHERE
                type = 'tp'";
        $data  = Database::query($query, true, 'uid');
        foreach ($data as $i => $value) {
            $customerId = $value['uid'];
            if (isset($responce[$customerId])) {
                $responce[$customerId]['tariff']['new'][$value['tariff']] = array(
                    'id' => $value['tariff']
                );
            }
        }
        Log::d("Load Tariff Change Requests. Finish");

        //dv_main
        Log::d("Load dv_main. Start");
        $query = "
            SELECT
                uid,
                tp_id AS 'tariff',
                disable AS 'state_id'
            FROM
                dv_main";
        $data  = Database::query($query, true, 'uid');
        Log::rawLog('customer', $data, 1);
        foreach ($data as $i => $value) {
            $customerId = $value['uid'];
            if (isset($responce[$customerId])) {
                $responce[$customerId]['tariff']['current'][$value['tariff']] = array(
                    'id' => $value['tariff']
                );
                if ('u.disable' != $confUserStateSrc) {
                    $stateId   = 2; //Play
                    /*
                    dv_main.disable
                    0 Активно
                    1 Отключено
                    2 Не активизирован
                    3 Приостановление
                    4 Отключено: Неуплата
                    5 Cлишком маленький депозит
                    6.Заблокирован из-за вирусов
                    */
                    switch ($value['state_id']) {
                        case '':
                        case 1:
                        case 4:
                        case 6:
                            $stateId = 0; //stop
                            break;
                        case 3:
                            $stateId = 1; //pause
                            break;
                        case 2:
                            $stateId = $confWorkState2;
                            break;
                        case 5:
                            $stateId = $confWorkState5;
                            break;
                    }
                    $responce[$customerId]['state_id'] = $stateId;
                }
            }
        }
        Log::d("Load dv_main. Finish");

        if (1 == $confIsImportTags) {
            Log::d("Load Tags. Start");
            $query = "
                SELECT
                    CONCAT(uid, '_', tag_id) AS id,
                    uid,
                    tag_id,
                    `date` AS 'date_add'
                FROM
                    tags_users";
            $data  = Database::query($query, true, 'id');
            Log::rawLog('tags', $data);
            foreach ($data as $i => $value) {
                $customerId = $value['uid'];
                if (isset($responce[$customerId])) {
                    $responce[$customerId]['tag'][$value['tag_id']] = array(
                        'id' => $value['tag_id'],
                        'date_add' => $value['date_add']
                    );
                }
            }
            Log::d("Load Tags. Finish");
        }

        Log::d(json_encode($responce));
        return $responce;
    }

    function getDataFromBilling($additionalUrl) {
        global $billingUrl, $moduleHelper;
        $api = new Api();
        ++$this->counterApiBilling;
        $this->billingApiPath = $billingUrl;
        $url                  = $this->billingApiPath . $additionalUrl;
        $result               = $api->readFromUrl($url);
        //echo '|'.$result.'|';
        if ('' == $result) {
            Log::c('Error: Empty Responce From Billing API');
            $moduleHelper->finishModule();
        }
        return json_decode($result, true);
    }

}

// ---- 1.1.36

$moduleHelper = new ModuleHelper();
$api          = new Api();

//Запуск модуля. Начальные операции
$moduleHelper->initializeModule();

//Обработка тарифных планов
Log::w("Tariff. Start");
$tariff        = new Tariff();
$tarifBilling  = $tariff->loadFromBilling();
$tarifUserside = $tariff->loadFromUserside();
$tariff->compareTariff($tarifBilling, $tarifUserside);
Log::w("Tariff. Finish");

//Обработка абонентов
Log::w("Customer. Start");
$customer    = new Customer();
$userBilling = $customer->loadFromBilling();

$userUserside = $customer->loadFromUserside();
$customer->compareCustomer($userBilling, $userUserside);
Log::w("Customer. Finish");

Log::w("System Operation");
$moduleHelper->systemOperation();

// Завершение работы модуля
$moduleHelper->finishModule();

class Address {

    static function addCityToUserside($array) {
        global $api;
        $name = $array['name'];
        Log::d("Add City To UserSide. Name: '" . $name . "'");
        $json = $api->sendDataToUserside("&cat=address&action=add_city&name=" . $name);
        if (isset($json['Id'])) {
            Log::d("Adding OK. Id: " . $json['Id']);
        } else {
            Log::d("Adding ERROR");
        }
        return $json['Id'];
    }

    static function addStreetToUserside($array) {
        global $api;
        $name   = $array['name'];
        $cityId = $array['city_id'];
        Log::d("Add Street To UserSide. CityId: '" . $cityId . "' Name: '" . $name . "'");
        $bundle = array(
            'cat' => 'address',
            'action' => 'add_street',
            'city_id' => $cityId,
            'name' => $name
        );
        $json   = $api->sendDataToUserside("&cat=address&action=add_street&city_id=" . $cityId . "&name=" . $name, $bundle);
        if (isset($json['Id'])) {
            Log::d("Adding OK. Id: " . $json['Id']);
        } else {
            Log::d("Adding ERROR");
        }
        return $json['Id'];
    }

    static function addHouseToUserside($array) {
        global $api;
        $cityId   = $array['city_id'];
        $streetId = $array['street_id'];
        $number   = $array['number'];
        $block    = $array['block'];
        $entrance = isset($array['entrance']) ? $array['entrance'] : 0;
        $floor    = isset($array['floor']) ? $array['floor'] : 0;
        Log::d("Add House To UserSide. CityId: '" . $cityId . "' StreetId: '" . $streetId . "' Number: '" . $number . "' Block: '" . $block . "'");

        $url = "&cat=address&action=add_house&city_id=" . $cityId . "&street_id=" . $streetId . "&number=" . $number . "&floor=" . $floor . "&entrance=" . $entrance . "&block=" . $block;

        //echo $url . "\n";

        $json = $api->sendDataToUserside($url);
        if (isset($json['Id'])) {
            Log::d("Adding OK. Id: " . $json['Id']);
        } else {
            Log::d("Adding ERROR");
        }
        return $json['Id'];
    }

    static function editHouseInUserside($houseId, $array) {
        global $api;
        Log::d("Edit House In UserSide. HouseId: " . $houseId);
        $url  = "&cat=address&action=edit_house&id=" . $houseId . "&data=" . json_encode($array);
        $json = $api->sendDataToUserside($url);
        if (isset($json['Id'])) {
            Log::d("Editing OK");
        } else {
            Log::d("Editing ERROR");
        }
    }

}

class Customer {
    private $arrayCustomerToAdd  = array();
    private $arrayCustomerToEdit = array();
    private $currentUserSideData = array();
    private $currentBillingData  = array();
    private $arrayBillingState   = array();
    private $arrayBillingHouse   = array();
    private $arrayBillingTags    = array();
    private $arrayBillingGroup    = array();

    private function houseProcessing() {
        global $api, $moduleHelper, $billingName, $billing;
        //Обработка домов из биллинга и сравнение с UserSide
        Log::w("House Processing");
        Log::w("Load City From Billing");
        if ('standart' == $billingName) {
            $billingCity = $api->getDataFromBilling("&request=get_city_list");
        } elseif ('carbon5' == $billingName) {
            $billingCity = $api->getDataFromBilling("&method1=userside_manager.get_city_list");
        } else {
            $billingCity = $billing->getCityList();
        }
        Log::w("Count: " . count($billingCity));
        Log::w("Load Street From Billing");
        if ('standart' == $billingName) {
            $billingStreet = $api->getDataFromBilling("&request=get_street_list");
        } elseif ('carbon5' == $billingName) {
            $billingStreet = $api->getDataFromBilling("&method1=userside_manager.get_street_list");
        } else {
            $billingStreet = $billing->getStreetList();
        }
        Log::w("Count: " . count($billingStreet));
        Log::w("Load House From Billing");
        if ('standart' == $billingName) {
            $billingHouse = $api->getDataFromBilling("&request=get_house_list");
        } elseif ('carbon5' == $billingName) {
            $billingHouse = $api->getDataFromBilling("&method1=userside_manager.get_house_list");
        } else {
            $billingHouse = $billing->getHouseList();
        }
        Log::w("Count: " . count($billingHouse));
        //print_r($billingCity);
        //print_r($billingStreet);
        //print_r($billingHouse);
        Log::w("Load House From UserSide");
        $usersideHouse = $api->getDataFromUserside("&request=get_house_list&is_id_address=3");
        if (!is_array($usersideHouse) || 1 > count($usersideHouse)) {
            Log::i("Error Load House From UserSide");
            $moduleHelper->finishModule();
        } else {
            Log::w("Count: " . count($usersideHouse));
        }

        $isLoadAddressDataFromUserside = 0;
        if (is_array($billingHouse) && 0 < count($billingHouse)) {
            foreach ($billingHouse as $id => $value) {
                $streetId   = $value['street_id'];
                $street     = $billingStreet[$value['street_id']];
                $cityId     = $street['city_id'];
                $cityName   = isset($billingCity[$cityId]['name']) ? $billingCity[$cityId]['name'] : '';
                $postCode   = isset($value['postcode']) ? floor($value['postcode']) : '';
                $floor      = isset($value['floor']) ? floor($value['floor']) : 0;
                $entrance   = isset($value['entrance']) ? floor($value['entrance']) : 0;
                $address    = $cityName;
                $streetName = isset($street['name']) ? trim($street['name']) : '';
                if ('' == $streetName) {
                    $streetName = isset($street['full_name']) ? trim($street['full_name']) : '';
                }
                if ('' != $streetName) {
                    $streetName = str_replace("'", '&#039;', $streetName);
                    if ('' != $address) {
                        $address .= ', ' . $streetName;
                    }
                }
                $houseNumber = $value['number'];
                $houseBlock  = isset($value['block']) ? trim($value['block']) : '';
                if ('' != $houseBlock) {
                    $houseNumber .= '-' . $houseBlock;
                }
                if ('' != $houseNumber) {
                    if ('' != $address) {
                        $address .= ', ' . $houseNumber;
                    }
                }
                $billingHouse[$id]['_address'] = $address;

                $usHouseId = 0;
                if ('' != $cityName) {
                    //Проверяем - есть ли такой дом в UserSide
                    $address             = str_replace("'", '&#039;', $address);
                    $address             = str_replace("/", '&#047;', $address);
                    $address             = str_replace("&#047;", '-', $address);
                    $addressUpperConvert = strtoupper(ModuleHelper::replaceNonUtfSymbol($address));
                    if (extension_loaded('mbstring')) {
                        $addressUpperConvert2 = mb_strtoupper(ModuleHelper::replaceNonUtfSymbol($address));
                    } else {
                        $addressUpperConvert2 = $addressUpperConvert;
                    }
                    $addressRaw   = md5($addressUpperConvert);
                    $addressRaw2  = md5($addressUpperConvert . ', ');
                    $addressRaw3  = md5($addressUpperConvert2);
                    $addressRaw4  = md5($addressUpperConvert2 . ', ');
                    $houseUSIndex = '';
                    if (isset($usersideHouse[$addressRaw])) {
                        $usHouseId    = $usersideHouse[$addressRaw]['id'];
                        $houseUSIndex = $addressRaw;
                    } elseif (isset($usersideHouse[$addressRaw2])) {
                        $usHouseId    = $usersideHouse[$addressRaw2]['id'];
                        $houseUSIndex = $addressRaw2;
                    } elseif (isset($usersideHouse[$addressRaw3])) {
                        $usHouseId    = $usersideHouse[$addressRaw3]['id'];
                        $houseUSIndex = $addressRaw3;
                    } elseif (isset($usersideHouse[$addressRaw4])) {
                        $usHouseId    = $usersideHouse[$addressRaw4]['id'];
                        $houseUSIndex = $addressRaw4;
                    } else {
                        //Нет дома
                        Log::d("Not find House '" . $address . "|" . $addressUpperConvert . "|" . $addressUpperConvert2 . "' - SearchId: '" . $addressRaw . "|" . $addressRaw2 . "|" . $addressRaw3 . "|" . $addressRaw4 . "' Billing id: '" . $id . "' street_id: '" . $streetId . "' - '" . $streetName . "' city_id: '" . $cityId . "' - '" . $cityName . "'");
                        //Загружаем массивы с адресами из UserSide
                        if (1 != $isLoadAddressDataFromUserside) {
                            Log::w("Load City From UserSide");
                            $usersideCity = $api->getDataFromUserside("&request=get_city_list&is_id_address=1");
                            if (!is_array($usersideCity) || 1 > count($usersideCity)) {
                                Log::i("Error Load City From UserSide");
                                $moduleHelper->finishModule();
                            } else {
                                Log::w("Count: " . count($usersideCity));
                            }
                            Log::w("Load Street From UserSide");
                            $usersideStreet = $api->getDataFromUserside("&request=get_street_list&is_id_address=2");
                            if (!is_array($usersideStreet) || 1 > count($usersideStreet)) {
                                Log::i("Error Load Street From UserSide");
                                $moduleHelper->finishModule();
                            } else {
                                Log::w("Count: " . count($usersideStreet));
                            }
                            $isLoadAddressDataFromUserside = 1;
                        }

                        //Ищем - есть ли город нужный
                        if (isset($usersideCity[$cityName])) {
                            $usCityId = $usersideCity[$cityName]['id'];
                        } else {
                            //Создаём новый населённый пункт
                            $usCityId = Address::addCityToUserside(array('name' => $cityName));
                            if (0 < $usCityId) {
                                $usersideCity[$cityName] = array(
                                    'id' => $usCityId
                                );
                            }
                        }

                        //Ищем - есть ли улица нужная
                        $streetNameRaw = ModuleHelper::replaceNonUtfSymbol($streetName);
                        if (isset($usersideStreet[$usCityId . '_' . $streetNameRaw])) {
                            $usStreetId = $usersideStreet[$usCityId . '_' . $streetNameRaw]['id'];
                        } else {
                            //Создаём новую улицу
                            Log::d("Not find Street '" . $streetName . "' us_city_id: '" . $usCityId . "' - '" . $cityName . "'");
                            $usStreetId = Address::addStreetToUserside(
                                array(
                                    'city_id' => $usCityId,
                                    'name' => $streetName
                                )
                            );
                            if (0 < $usStreetId) {
                                $usersideStreet[$usCityId . '_' . $streetName] = array(
                                    'id' => $usStreetId
                                );
                            }
                        }
                        //Добавляем дом
                        if (0 < $usStreetId) {
                            $usHouseId = Address::addHouseToUserside(
                                array(
                                    'city_id' => $usCityId,
                                    'street_id' => $usStreetId,
                                    'number' => $value['number'],
                                    'block' => $houseBlock,
                                    'postcode' => $postCode
                                )
                            );
                        }

                        if (0 < $usHouseId) {
                            $usersideHouse[md5($address)] = array(
                                'id' => $usHouseId,
                                'postcode' => $postCode
                            );
                            $houseUSIndex                 = md5($address);
                        }
                    }
                    if (0 < $usHouseId) {
                        $houseUsArray = $usersideHouse[$houseUSIndex];
                        //Проверяем правильность данных
                        $houseUsArray['postcode'] = isset($houseUsArray['postcode']) ? $houseUsArray['postcode'] : '';
                        $houseUsArray['entrance'] = isset($houseUsArray['entrance']) ? $houseUsArray['entrance'] : 0;
                        $houseUsArray['floor']    = isset($houseUsArray['floor']) ? $houseUsArray['floor'] : 0;
                        $isChange                 = 0;
                        $array                    = array();
                        if ($postCode != $houseUsArray['postcode'] && '' != $postCode) {
                            $isChange                                 = 1;
                            $array['postcode']                        = $postCode;
                            $usersideHouse[$houseUSIndex]['postcode'] = $postCode;
                        }
                        if ($entrance != $houseUsArray['entrance'] && 0 < $entrance) {
                            $isChange                                 = 1;
                            $array['entrance']                        = $entrance;
                            $usersideHouse[$houseUSIndex]['entrance'] = $entrance;
                        }
                        if ($floor != $houseUsArray['floor'] && 0 < $floor) {
                            $isChange                              = 1;
                            $array['floor']                        = $floor;
                            $usersideHouse[$houseUSIndex]['floor'] = $floor;
                        }
                        if (1 == $isChange) {
                            //Изменяем
                            $usHouseId = Address::editHouseInUserside($usHouseId, $array);
                        }
                    }
                }
                $billingHouse[$id]['_userside_id'] = $usHouseId;
            }
        }
        $this->arrayBillingHouse = $billingHouse;
    }

    public function loadFromBilling() {
        global $api, $isUpdateAddress, $billingName, $billing;
        Log::w("Load User State From Billing");
        if ('standart' == $billingName) {
            $this->arrayBillingState = $api->getDataFromBilling("&request=get_user_state_list");
        } elseif ('carbon5' == $billingName) {
            $this->arrayBillingState = $api->getDataFromBilling("&method1=userside_manager.get_user_state_list");
        } else {
            $this->arrayBillingState = $billing->getBillingState();
        }
        if (1 == $isUpdateAddress) {
            $this->houseProcessing();
        }

        Log::w("Load User From Billing");
        if ('standart' == $billingName) {
            $json = $api->getDataFromBilling("&request=get_user_list");
        } elseif ('carbon5' == $billingName) {
            $json = $api->getDataFromBilling("&method1=userside_manager.get_user_list");
        } else {
            $json = $billing->getCustomerList();
        }
        if (2 > count($json)) {
            $json = array();
        }
        Log::w("Count: " . count($json));

        Log::w("Load User Tags From Billing");
        if ('standart' == $billingName) {
            $this->arrayBillingTags = $api->getDataFromBilling("&request=get_user_tags");
        } elseif ('carbon5' == $billingName) {
            $this->arrayBillingTags = array();
        } else {
            $this->arrayBillingTags = $billing->getBillingTags();
        }

        Log::w("Load User Group From Billing");
        if ('standart' == $billingName) {
            $this->arrayBillingGroup = $api->getDataFromBilling("&request=get_user_group_list");
        } elseif ('carbon5' == $billingName) {
            $this->arrayBillingGroup = array();
        } else {
            $this->arrayBillingGroup = $billing->getBillingGroup();
        }
        return $json;
    }

    public function loadFromUserside() {
        global $api, $billingId, $moduleHelper;
        Log::w("Load User From UserSide");
        $json = $api->getDataFromUserside("&request=get_user_list&is_id_billing_user_id=1&billing_id=" . $billingId);
        if (!is_array($json) || 1 > count($json)) {
            Log::i("Error Load User From UserSide");
            $moduleHelper->finishModule();
        }
        Log::w("Count: " . count($json));
        return $json;
    }

    public function loadTagsFromUserside() {
        global $api;
        Log::w("Load User Tags From UserSide");
        $json = $api->getDataFromUserside("&request=get_user_tags");
        Log::w("Count: " . count($json));
        return $json;
    }

    public function loadGroupFromUserside() {
        global $api;
        Log::w("Load User Groups From UserSide");
        $json = $api->getDataFromUserside("&request=get_user_group_list");
        Log::w("Count: " . count($json));
        return $json;
    }

    private function addTagToUserside($array) {
        global $api;
        $name = $array['name'];
        Log::d("Add Tag To UserSide. Name: '" . $name . "'");
        $json = $api->sendDataToUserside("&cat=customer&action=add_tag&name=" . $name);
        if (isset($json['id'])) {
            Log::d("Adding OK. Id: " . $json['id']);
        } else {
            $json['id'] = 0;
            Log::d("Adding ERROR");
        }
        return $json['id'];
    }

    private function addGroupToUserside($array) {
        global $api;
        $name = $array['name'];
        Log::d("Add Group To UserSide. Name: '" . $name . "'");
        $json = $api->sendDataToUserside("&cat=customer&action=add_group&name=" . $name);
        if (isset($json['id'])) {
            Log::d("Adding OK. Id: " . $json['id']);
        } else {
            $json['id'] = 0;
            Log::d("Adding ERROR");
        }
        return $json['id'];
    }

    private function prepareToAddCustomerToUserside($array) {
        $this->arrayCustomerToAdd[] = array(
            'billing_customer_id' => $array['id'],
            //'name' => urlencode($array['name'])
            'name' => '_'
        );
    }

    private function addCustomerToUserside() {
        global $api, $billingId;
        Log::w("Add Customer To UserSide. Count: " . count($this->arrayCustomerToAdd));
        $count    = 0;
        $bufer    = array();
        $addCount = 0;
        foreach ($this->arrayCustomerToAdd as $i => $value) {
            ++$count;
            $bufer[] = $value;
            if (0 == fmod($count, 100)) {
                $json  = $api->sendDataToUserside("&cat=customer&action=group_add&billing_id=" . $billingId . "&data=" . str_replace('#', '_', str_replace('&', '_', json_encode($bufer))));
                $bufer = array();
                if (isset($json['Count'])) {
                    $addCount += $json['Count'];
                }
            }
        }
        if (0 < count($bufer)) {
            $json = $api->sendDataToUserside("&cat=customer&action=group_add&billing_id=" . $billingId . "&data=" . json_encode($bufer));
            if (isset($json['Count'])) {
                $addCount += $json['Count'];
            }
        }
        if (isset($json['Count'])) {
            Log::d("Adding OK. Count: " . $addCount);
        } else {
            Log::d("Adding ERROR");
        }
        return $json;
    }

    private function editCustomerInUserside() {
        global $api, $apiGroupCounter;
        Log::w("Edit Customer In UserSide. Count: " . count($this->arrayCustomerToEdit));

        $n       = 300; //В запросах подавать по n-абонентов
        $nCustom = 4000; //Для типовых данных - подавать блоками по $nCustom
        if (0 < $apiGroupCounter) {
            $nCustom = $apiGroupCounter;
        }

        //Прогоняем типовые данные
        $arrayGroupList = array(
            'balance' => 1,
            'traffic_up' => 1,
            'traffic_down' => 1,
            'date_activity_inet' => 1
        );

        $arrayGroupData = array();
        foreach ($this->arrayCustomerToEdit as $customerId => $value) {
            foreach ($arrayGroupList as $j => $value2) {
                if (isset($value[$j])) {
                    $arrayGroupData[$j][$customerId] = $value[$j];
                    unset($this->arrayCustomerToEdit[$customerId][$j]);
                }
            }
            if (1 > count($this->arrayCustomerToEdit[$customerId])) {
                unset($this->arrayCustomerToEdit[$customerId]);
            }
        }

        //Посылаем групповые данные
        foreach ($arrayGroupList as $i => $value) {
            if (isset($arrayGroupData[$i])) {
                $j         = 0;
                $jAll      = 0;
                $tempArray = array();
                foreach ($arrayGroupData[$i] as $customerId => $value2) {
                    ++$j;
                    ++$jAll;
                    if ($nCustom >= $j) {
                        $tempArray[$customerId][$i] = $value2;
                    } else {
                        Log::d("Edit '" . $i . "' - " . ($jAll - $nCustom) . '-' . $jAll . " / " . count($arrayGroupData[$i]));
                        $post = array(
                            'cat' => 'customer',
                            'action' => 'group_edit',
                            'src' => 'billing',
                            'data' => json_encode($tempArray)
                        );
                        $json = $api->sendDataToUserside(implode('&', $post), $post);
                        if (isset($json['Count'])) {
                            Log::d("Editing OK. Count: " . $json['Count']);
                        } else {
                            Log::d("Editing ERROR");
                        }
                        $j         = 0;
                        $tempArray = array();
                    }
                }
                //Если ещё что осталось
                if (0 < $j) {
                    $post = array(
                        'cat' => 'customer',
                        'action' => 'group_edit',
                        'src' => 'billing',
                        'data' => json_encode($tempArray)
                    );
                    $json = $api->sendDataToUserside(implode('&', $post), $post);
                    if (isset($json['Count'])) {
                        Log::d("Editing OK. Count: " . $json['Count']);
                    } else {
                        Log::d("Editing ERROR");
                    }
                }
            }
        }

        //Обрабатываем оставшиеся данные
        //Разбиваем по $n абонентов
        $j         = 0;
        $jAll      = 0;
        $tempArray = array();
        foreach ($this->arrayCustomerToEdit as $customerId => $value) {
            ++$j;
            ++$jAll;
            if ($n >= $j) {
                $tempArray[$customerId] = $value;
            } else {
                Log::d("Edit " . ($jAll - $n) . '-' . $jAll . " / " . count($this->arrayCustomerToEdit));
                $post = array(
                    'cat' => 'customer',
                    'action' => 'group_edit',
                    'data' => json_encode($tempArray)
                );
                $json = $api->sendDataToUserside(implode('&', $post), $post);
                if (isset($json['Count'])) {
                    Log::d("Editing OK. Count: " . $json['Count']);
                } else {
                    Log::d("Editing ERROR");
                }
                $j         = 0;
                $tempArray = array();
            }
        }

        //Если ещё что осталось
        if (0 < $j) {
            $post = array(
                'cat' => 'customer',
                'action' => 'group_edit',
                'data' => json_encode($tempArray)
            );
            $json = $api->sendDataToUserside(implode('&', $post), $post);
            if (isset($json['Count'])) {
                Log::d("Editing OK. Count: " . $json['Count']);
            } else {
                Log::d("Editing ERROR");
            }
        }
    }

    private function importPaidHistory($customerPaidHistory) {
        global $api, $billingId;
        Log::w("Import Paid History In UserSide. Count: " . count($customerPaidHistory));
        $post = array(
            'cat' => 'customer',
            'action' => 'import_paid',
            'billing_id' => $billingId,
            'data' => json_encode($customerPaidHistory)
        );
        $json = $api->sendDataToUserside(implode('&', $post), $post);
        if (isset($json['Count'])) {
            Log::d("Import OK. Count: " . $json['Count']);
        } else {
            Log::d("Import ERROR");
        }
    }

    private function compareData($field, $isSpecialValue = 0) {
        $usersideId = $this->currentUserSideData['userside_id'];
        if (isset($this->currentBillingData[$field])) {
            $billingValue = str_replace("'", '&#039;', $this->currentBillingData[$field]);
        } else {
            if (1 == $isSpecialValue) {
                $billingValue = 0;
            } elseif (2 == $isSpecialValue || 3 == $isSpecialValue) {
                $billingValue = '1970-01-01';
            } else {
                $billingValue = '';
            }
        }
        if (3 == $isSpecialValue) {
            $billingValue = date('Y-m-d', strtotime($billingValue));
        }
        if (isset($this->currentUserSideData[$field])) {
            $usersideValue = $this->currentUserSideData[$field];
        } else {
            if (1 == $isSpecialValue) {
                $usersideValue = 0;
            } elseif (2 == $isSpecialValue || 3 == $isSpecialValue) {
                $usersideValue = '1970-01-01 00:00:00';
            } else {
                $usersideValue = '';
            }
        }
        if ($usersideValue != $billingValue) {
            if (2 != $isSpecialValue || ('1970-01-01 00:00:00' != $billingValue && '1970-01-01' != $billingValue && '' != $billingValue)) {
                $this->arrayCustomerToEdit[$usersideId][$field] = str_replace("'", '&#039;', $billingValue);
            }
        }
    }

    public function compareCustomer($billing, $userside) {
        global $additionalDataMerge, $isUpdateAddress, $customerPaidHistory, $customer, $confIpGrayNet, $confIpWhiteNet;

        if ('' != $confIpGrayNet && '0' != $confIpGrayNet) {
            $isAddWhiteIp = 1;
            $grayNetIpMin = trim(sprintf("%u\n", ip2long($confIpGrayNet))) + 1;
            $grayNetIpMax = $grayNetIpMin + 65025;
            $whiteNetIp = trim(sprintf("%u\n", ip2long($confIpWhiteNet)));
            $whiteNetIp = $grayNetIpMin - $whiteNetIp - 1;
        } else {
            $isAddWhiteIp = 0;
        }

        //Сверяем метки (только наименования меток)
        if (0 < count($this->arrayBillingTags)) {
            Log::w("Compare Tag");
            $tagsUserside = $customer->loadTagsFromUserside();
            foreach($this->arrayBillingTags as $i => $value) {
                $isFind = 0;
                if (isset($value['name'])) {
                    $tagBillingName = $value['name'];
                    $this->arrayBillingTags[$i]['us_id'] = 0;
                    foreach($tagsUserside as $j => $value2) {
                        if ($tagBillingName == $value2['name']) {
                            $isFind = 1;
                            $this->arrayBillingTags[$i]['us_id'] = $value2['id'];
                            $tagsUserside[$j]['billing_id'] = $i;
                            break;
                        }
                    }
                    if (1 != $isFind) {
                        $bundle = array(
                            'name' => $tagBillingName
                        );
                        $usersideId = $this->addTagToUserside($bundle);
                        if (0 < $usersideId) {
                            $this->arrayBillingTags[$i]['us_id'] = $usersideId;
                        }
                    }
                }
            }
        }

        //Сверяем группы (только наименования групп)
        if (0 < count($this->arrayBillingGroup)) {
            Log::w("Compare Group");
            $groupUserside = $customer->loadGroupFromUserside();
            foreach($this->arrayBillingGroup as $i => $value) {
                $isFind = 0;
                if (isset($value['name'])) {
                    $groupBillingName = $value['name'];
                    $this->arrayBillingGroup[$i]['us_id'] = 0;
                    foreach($groupUserside as $j => $value2) {
                        if ($groupBillingName == $value2['name']) {
                            $isFind = 1;
                            $this->arrayBillingGroup[$i]['us_id'] = $value2['id'];
                            $groupUserside[$j]['billing_id'] = $i;
                            break;
                        }
                    }
                    if (1 != $isFind) {
                        $bundle = array(
                            'name' => $groupBillingName
                        );
                        $usersideId = $this->addGroupToUserside($bundle);
                        if (0 < $usersideId) {
                            $this->arrayBillingGroup[$i]['us_id'] = $usersideId;
                        }
                    }
                }
            }
        }

        Log::w("Compare Customer");
        //Проходим всех абонентов из биллинга
        foreach ($billing as $id => $value) {
            $name                     = isset($value['full_name']) ? $value['full_name'] : '';
            $name                     = str_replace("'", '&#039;', $name);
            $this->currentBillingData = $value;

            //Есть ли такой абонент в UserSide
            if (!isset($userside[$id])) {
                //Нужно добавить абонента в UserSide
                Log::w("Customer '" . $id . "' Not Found In UserSide. Adding...");
                $bundle = array(
                    'id' => $id,
                    'name' => $name
                );
                $this->prepareToAddCustomerToUserside($bundle);
            } else {
                //Этот абонент есть. Нужно сверить данные
                $userside[$id]['isFindInBilling'] = 1;

                $customer                  = $userside[$id];
                $usersideId                = $customer['userside_id'];
                $this->currentUserSideData = $customer;

                //Название
                if ($customer['full_name'] != $name) {
                    $this->arrayCustomerToEdit[$usersideId]['name'] = str_replace("'", '&#039;', $name);
                }

                if (isset($value['comment'])) {
                    $value['comment']         = str_replace('\\', '&#092;', $value['comment']);
                    $value['comment']         = str_replace("/", '&#047;', $value['comment']);
                    $value['comment']         = trim(str_replace("'", '&#039;', $value['comment']));
                    $this->currentBillingData = $value;
                }

                if (isset($value['date_connect'])) {
                    if ('0000-00-00' == $value['date_connect']) {
                        $value['date_connect']    = "1970-01-01";
                        $this->currentBillingData = $value;
                    }
                }

                $this->compareData('account_number'); //Лицевой счёт
                $this->compareData('login'); //Учетная запись
                $this->compareData('comment'); //Заметки
                $this->compareData('balance', 1); //Баланс
                $this->compareData('credit', 1); //Кредит
                $this->compareData('discount', 1); //Скидка
                $this->compareData('flag_corporate', 1); //Флаг - юр.лицо
                $this->compareData('date_connect', 3); //Дата подключения

                //Тарифы
                if (!isset($value['tariff']['current'][0]['id'])) {
                    $value['tariff']['current'][0]['id'] = '';
                }

                foreach ($value['tariff']['current'] as $i => $array) {
                    $tariffId = $array['id'];
                    if (!isset($customer['tariff']['current'][$tariffId]['id'])) {
                        $customer['tariff']['current'][$tariffId]['id'] = '';
                    }
                    $customer['tariff']['current'][$tariffId]['isUsed'] = 1;
                    if ($customer['tariff']['current'][$tariffId]['id'] != $array['id']) {
                        $this->arrayCustomerToEdit[$usersideId]['tariff_id'][$tariffId] = $array['id'];
                    }
                }

                //Откидываем неиспользованые
                foreach ($customer['tariff']['current'] as $i => $array) {
                    if ('' != $array['id'] && !isset($array['isUsed'])) {
                        $this->arrayCustomerToEdit[$usersideId]['delete_tariff_id'][] = $array['id'];
                    }
                }

                //Следующий тариф
                $billingValue  = isset($value['tariff']['new'][0]['id']) ? $value['tariff']['new'][0]['id'] : '';
                $usersideValue = isset($customer['tariff']['new'][0]['id']) ? $customer['tariff']['new'][0]['id'] : '';
                if (strtolower($billingValue) != strtolower($usersideValue)) {
                    $billingDate                                             = isset($value['tariff']['new'][0]['date_start']) ? $value['tariff']['new'][0]['date_start'] : '';
                    $this->arrayCustomerToEdit[$usersideId]['tariff_new_id'] = $billingValue . '|' . $billingDate;
                }

                //Номер договора
                $billingValue  = isset($value['agreement'][0]['number']) ? $value['agreement'][0]['number'] : '';
                $usersideValue = isset($customer['agreement'][0]['number']) ? $customer['agreement'][0]['number'] : '';
                if ($billingValue != $usersideValue && '' != $billingValue) {
                    $this->arrayCustomerToEdit[$usersideId]['agreement_number'] = str_replace("'", '&#039;', $billingValue);
                }
                //Дата договора
                $billingValue  = isset($value['agreement'][0]['date']) ? substr($value['agreement'][0]['date'], 0, 10) : '';
                $usersideValue = isset($customer['agreement'][0]['date']) ? $customer['agreement'][0]['date'] : '';
                if ($billingValue != $usersideValue && '' != $billingValue) {
                    $this->arrayCustomerToEdit[$usersideId]['agreement_date'] = str_replace("'", '&#039;', $billingValue);
                }

                //Трафик
                $trafficUp = isset($value['traffic']['month']['up']) ? $value['traffic']['month']['up'] : 0;
                if ($customer['traffic']['month']['up'] != $trafficUp) {
                    $this->arrayCustomerToEdit[$usersideId]['traffic_up'] = $trafficUp;
                }
                $trafficDown = isset($value['traffic']['month']['down']) ? $value['traffic']['month']['down'] : 0;
                if ($customer['traffic']['month']['down'] != $trafficDown) {
                    $this->arrayCustomerToEdit[$usersideId]['traffic_down'] = $trafficDown;
                }

                //Телефоны
                $phone0    = '';
                $phone1    = '';
                $phoneTemp = '';
                if (isset($value['phone']) && is_array($value['phone'])) {
                    foreach ($value['phone'] as $i => $value2) {
                        if (isset($value2['flag_main']) && 1 == $value2['flag_main']) {
                            $phone0 = $value2['number'];
                        } else {
                            if ($value2['number'] != $phone1) {
                                $phoneTemp = $phone1;
                            }
                            $phone1 = $value2['number'];
                        }
                    }
                }
                if ('' == $phone0) {
                    $phone0 = $phoneTemp;
                }
                $usersideValue = isset($customer['phone'][0]['number']) ? $customer['phone'][0]['number'] : '';
                if ($usersideValue != $phone0 && '' != $phone0) {
                    $this->arrayCustomerToEdit[$usersideId]['phone0'] = str_replace("'", '&#039;', $phone0);
                }
                $usersideValue = isset($customer['phone'][1]['number']) ? $customer['phone'][1]['number'] : '';
                if ($usersideValue != $phone1 && '' != $phone1) {
                    //echo '|' . $phone1 . '|' . $usersideValue . "|\n";
                    $this->arrayCustomerToEdit[$usersideId]['phone1'] = str_replace("'", '&#039;', $phone1);
                }
                //E-mail
                $billingValue = isset($value['email'][0]['address']) ? $value['email'][0]['address'] : '';
                if ('' != $billingValue) {
                    $billingValue = str_replace("'", '&#039;', $billingValue);
                    $billingValue = str_replace("\n", ' ', $billingValue);
                    $billingValue = str_replace(",", ' ', $billingValue);
                    $arrayEmail   = explode('@', $billingValue);
                    $billingValue = '';
                    if (isset($arrayEmail[1])) {
                        $email1       = explode(' ', $arrayEmail[0]);
                        $email1       = $email1[count($email1) - 1];
                        $email2       = explode(' ', $arrayEmail[1]);
                        $email2       = $email2[0];
                        $billingValue = $email1 . '@' . $email2;
                    }
                }
                $usersideValue = isset($customer['email'][0]['address']) ? $customer['email'][0]['address'] : '';
                if ($billingValue != $usersideValue && '' != $billingValue) {
                    $this->arrayCustomerToEdit[$usersideId]['email'] = str_replace("'", '&#039;', $billingValue);
                }

                //Адрес
                if (1 == $isUpdateAddress) {

                    //echo $name . "\n";
                    //print_r($value['address'][0]);

                    //Дом
                    $billingValue = isset($value['address'][0]['house_id']) ? $value['address'][0]['house_id'] : 0;
                    if (0 < $billingValue || 1 < strlen($billingValue)) {
                        $billingValue  = isset($this->arrayBillingHouse[$billingValue]['_userside_id']) ? $this->arrayBillingHouse[$billingValue]['_userside_id'] : 0;
                        $usersideValue = isset($customer['address'][0]['house_id']) ? $customer['address'][0]['house_id'] : 0;
                        if ($billingValue != $usersideValue && 0 < $billingValue) {
                            $this->arrayCustomerToEdit[$usersideId]['house_id'] = $billingValue;
                        }
                    }
                    //Квартира
                    $billingValue  = isset($value['address'][0]['apartment']['number']) ? $value['address'][0]['apartment']['number'] : 0;
                    $usersideValue = isset($customer['address'][0]['apartment']['number']) ? $customer['address'][0]['apartment']['number'] : '';
                    if ($billingValue != $usersideValue && '' != $billingValue) {
                        $this->arrayCustomerToEdit[$usersideId]['apartment_number'] = $billingValue;
                    }
                    //Этаж
                    $billingValue  = isset($value['address'][0]['floor']) ? $value['address'][0]['floor'] : 0;
                    $billingValue  = trim($billingValue);
                    $usersideValue = isset($customer['address'][0]['floor']) ? $customer['address'][0]['floor'] : '';
                    if ($billingValue != $usersideValue && '' != $billingValue) {
                        $this->arrayCustomerToEdit[$usersideId]['floor'] = $billingValue;
                    }
                    //Подъезд
                    $billingValue  = isset($value['address'][0]['entrance']) ? $value['address'][0]['entrance'] : 0;
                    $usersideValue = isset($customer['address'][0]['entrance']) ? $customer['address'][0]['entrance'] : '';
                    if ($billingValue != $usersideValue && 0 < $billingValue) {
                        $this->arrayCustomerToEdit[$usersideId]['entrance'] = $billingValue;
                    }
                }

                //IP-MAC
                $usersideIp  = isset($customer['ip_mac']) ? $customer['ip_mac'] : array();
                $ipToAdd     = 0;
                $ipChangeMac = 0;
                $ipToDelete  = 0;
                if (isset($value['ip_mac']) && is_array($value['ip_mac'])) {
                    //Добавляем белые IP-адреса для серых
                    if (1 == $isAddWhiteIp) {
                        foreach ($value['ip_mac'] as $i => $value2) {
                            $ip  = trim($value2['ip']);
                            if ('' != $ip && 0 < $ip) {
                                if ($ip >= $grayNetIpMin && $ip <= $grayNetIpMax) {
                                    $mac = isset($value2['mac']) ? strtolower($value2['mac']) : '';
                                    $ipNew = $ip - $whiteNetIp;
                                    $value['ip_mac'][$ipNew] = array(
                                        'ip' => $ipNew,
                                        'mac' => $mac
                                    );
                                    Log::w("Add White Ip " . $ipNew . " For Gray Ip " . $ip . " (customer: " . $id . ")");
                                }
                            }
                        }
                    }

                    foreach ($value['ip_mac'] as $i => $value2) {
                        $ip  = trim($value2['ip']);
                        $mac = isset($value2['mac']) ? strtolower($value2['mac']) : '';
                        if (12 != strlen($mac)) {
                            $mac = '';
                        }
                        if ('' != $ip && 0 < $ip) {
                            if (!isset($usersideIp[$ip])) {
                                //Нет такого IP
                                ++$ipToAdd;
                                $this->arrayCustomerToEdit[$usersideId]['add_ip' . $ipToAdd] = $ip . ',' . $mac;
                            } else {
                                $usersideIp[$ip]['_is_find'] = 1;
                                //Проверяем - такой ли MAC
                                if ($mac != $usersideIp[$ip]['mac'] && '' != $mac) {
                                    ++$ipChangeMac;
                                    //Меняем MAC
                                    $this->arrayCustomerToEdit[$usersideId]['ip_change_mac' . $ipChangeMac] = $ip . ',' . $mac;
                                }
                            }
                        }

                        if (isset($value2['local_ip'])) {
                            $ip = trim($value2['local_ip']);
                            if ('' != $ip && 0 < $ip && $ip != trim($value2['ip'])) {
                                if (!isset($usersideIp[$ip])) {
                                    //Нет такого IP
                                    ++$ipToAdd;
                                    $this->arrayCustomerToEdit[$usersideId]['add_ip' . $ipToAdd] = $ip . ',' . $mac;
                                } else {
                                    $usersideIp[$ip]['_is_find'] = 1;
                                    //Проверяем - такой ли MAC
                                    if ($mac != $usersideIp[$ip]['mac'] && '' != $mac) {
                                        ++$ipChangeMac;
                                        //Меняем MAC
                                        $this->arrayCustomerToEdit[$usersideId]['ip_change_mac' . $ipChangeMac] = $ip . ',' . $mac;
                                    }
                                }
                            }
                        }

                    }
                }
                //Убираем ненайдённые IP
                foreach ($usersideIp as $ip => $value2) {
                    if (!isset($value2['_is_find'])) {
                        ++$ipToDelete;
                        $this->arrayCustomerToEdit[$usersideId]['delete_ip' . $ipToDelete] = $ip;
                    }
                }

                //Статус
                $billingValue      = isset($value['state_id']) ? $value['state_id'] : 0;
                $stateFunctionalId = isset($this->arrayBillingState[$billingValue]['functional']) ? $this->arrayBillingState[$billingValue]['functional'] : 0;
                switch ($stateFunctionalId) {
                    case 'nomoney':
                    case 'new':
                    case 'disable':
                    case 'stop':
                        $billingValue = 0; //Stop
                        break;
                    case 'pause':
                        $billingValue = 1; //Pause
                        break;
                    case 'work':
                        $billingValue = 2; //Play
                        break;
                    default:
                        $billingValue = 0; //Stop
                        break;
                }
                $usersideValue = isset($customer['state_id']) ? $customer['state_id'] : 0;
                if ($billingValue != $usersideValue && 0 < $billingValue) {
                    $this->arrayCustomerToEdit[$usersideId]['state_id'] = $billingValue;
                }

                //Дата активности
                $billingValue  = isset($value['date_activity']) ? $value['date_activity'] : '1970-01-01';
                $usersideValue = isset($customer['date_activity_inet']) ? $customer['date_activity_inet'] : '1970-01-01';
                if ($billingValue != $usersideValue && '' != $billingValue && '1970-01-01' != date('Y-m-d', strtotime($billingValue))) {
                    $this->arrayCustomerToEdit[$usersideId]['date_activity_inet'] = $billingValue;
                }

                //Доп.данные
                if (0 < count($additionalDataMerge)) {
                    $additionalData = isset($value['additional_data']) ? $value['additional_data'] : array();
                    foreach ($additionalData as $dataId => $array) {
                        if (isset($additionalDataMerge[$dataId])) {
                            $billingValue = $array['value'];
                            //Меняем ID поля биллинга на ID поля userside
                            $dataUsId      = $additionalDataMerge[$dataId];
                            $usersideValue = isset($customer['additional_data'][$dataUsId]['value']) ? $customer['additional_data'][$dataUsId]['value'] : '';
                            if ($billingValue != $usersideValue) {
                                //Log::w($usersideId . "|Billing: " . $billingValue. " (" . $dataId . ")| US: " . $usersideValue . " (" . $dataUsId . ")");
                                $this->arrayCustomerToEdit[$usersideId]['additional_data'][$dataUsId] = $billingValue;
                            }
                        }
                    }
                }

                //Метки
                if (0 < count($this->arrayBillingTags)) {
                    $tag = isset($value['tag']) ? $value['tag'] : array();
                    foreach ($tag as $tagId => $array) {
                        //Получаем ID метки в UserSide
                        if (0 < $tagId) {
                            $tagUsId = $this->arrayBillingTags[$tagId]['us_id'];
                            if (0 < $tagUsId) {
                                if (!isset($customer['tag'][$tagUsId])) {
                                    //echo 'add tag: ' . $usersideId . "|" . $tagId . "=" . $tagUsId . "|\n";
                                    $date_add                                                = isset($array['date_add']) ? $array['date_add'] : 1;
                                    $this->arrayCustomerToEdit[$usersideId]['tag'][$tagUsId] = $date_add;
                                } else {
                                    $customer['tag'][$tagUsId]['is_find'] = 1;
                                }
                            }
                        }
                    }
                    //Проверяем метки, что нужно удалить
                    if (isset($customer['tag'])) {
                        foreach ($customer['tag'] as $tagUsId => $array) {
                            if (!isset($array['is_find'])) {
                                $billingTagId = isset($tagsUserside[$tagUsId]['billing_id']) ? $tagsUserside[$tagUsId]['billing_id'] : 0;
                                if (0 < $billingTagId) {
                                    //Значит метки в биллинге уже нет - надо снимать и в userside
                                    //echo 'delete tag: ' . $usersideId . "|" . $billingTagId . "=" . $tagUsId . "|\n";
                                    $this->arrayCustomerToEdit[$usersideId]['tag'][$tagUsId] = -1;
                                }
                            }
                        }
                    }
                }

                //Группы
                if (0 < count($this->arrayBillingGroup)) {
                    $group = isset($value['group']) ? $value['group'] : array();
                    foreach ($group as $groupId => $array) {
                        //Получаем ID метки в UserSide
                        if (0 < $groupId) {
                            $groupUsId = $this->arrayBillingGroup[$groupId]['us_id'];
                            if (0 < $groupUsId) {
                                if (!isset($customer['group'][$groupUsId])) {
                                    //echo 'add group: ' . $usersideId . "|" . $groupId . "=" . $groupUsId . "|\n";
                                    $this->arrayCustomerToEdit[$usersideId]['group'][$groupUsId] = 1;
                                } else {
                                    $customer['group'][$groupUsId]['is_find'] = 1;
                                }
                            }
                        }
                    }
                    //Проверяем группы, что нужно удалить
                    if (isset($customer['group'])) {
                        foreach ($customer['group'] as $groupUsId => $array) {
                            if (!isset($array['is_find'])) {
                                $billingGroupId = isset($groupUserside[$groupUsId]['billing_id']) ? $groupUserside[$groupUsId]['billing_id'] : 0;
                                if (0 < $billingGroupId) {
                                    //Значит метки в биллинге уже нет - надо снимать и в userside
                                    //echo 'delete group: ' . $usersideId . "|" . $billingGroupId . "=" . $groupUsId . "|\n";
                                    $this->arrayCustomerToEdit[$usersideId]['group'][$groupUsId] = -1;
                                }
                            }
                        }
                    }
                }

                //Если стоит отметка, что нет в биллинге - убираем
                if (1 != $customer['is_in_billing']) {
                    $this->arrayCustomerToEdit[$usersideId]['is_in_billing'] = 1;
                }
            }
        }

        //Если каких-то абонентов нет в UserSide - то нужно добавить
        if (0 < count($this->arrayCustomerToAdd)) {
            $this->addCustomerToUserside();
        }

        //Абонентов, что не найдены в биллинге - помечаем
        if (is_array($userside)) {
            foreach ($userside as $id => $value) {
                if (!isset($value['isFindInBilling']) && 1 == $value['is_in_billing']) {
                    //Не найден в биллинге
                    $usersideId                                              = $value['userside_id'];
                    $this->arrayCustomerToEdit[$usersideId]['is_in_billing'] = 0;
                }
            }
        }

        //Если есть изменения по абонентам - обновляем
        if (0 < count($this->arrayCustomerToEdit)) {
            $this->editCustomerInUserside();
        }

        //Импорт истории платежей
        if (is_array($customerPaidHistory) && 0 < count($customerPaidHistory)) {
            foreach ($customerPaidHistory as $i => $value) {
                $customerId = $value['user_id'];
                if (isset($userside[$customerId])) {
                    $usersideId                             = $userside[$customerId]['userside_id'];
                    $customerPaidHistory[$i]['userside_id'] = $usersideId;
                    unset($customerPaidHistory[$i]['user_id']);
                } else {
                    unset($customerPaidHistory[$i]);
                }
            }
            $this->importPaidHistory($customerPaidHistory);
        }
    }

}

class Tariff {

    public function loadFromBilling() {
        global $api, $billing, $billingName;
        Log::w("Load Tariff From Billing");
        if ('standart' == $billingName) {
            $json = $api->getDataFromBilling("&request=get_tariff_list");
        } elseif ('carbon5' == $billingName) {
            $json = $api->getDataFromBilling("&method1=userside_manager.get_tariff_list");
        } else {
            $json = $billing->getTariffList();
        }
        Log::w("Count: " . count($json));
        return $json;
    }

    public function loadFromUserside() {
        global $api, $billingId, $moduleHelper;
        Log::w("Load Tariff From UserSide");
        $json = $api->getDataFromUserside("&request=get_tariff_list&billing_id=" . $billingId);
        if (!is_array($json) || 1 > count($json)) {
            Log::i("Error Load Tarif From UserSide");
            $moduleHelper->finishModule();
        }
        Log::w("Count: " . count($json));
        return $json;
    }

    private function addTariffToUserside($array) {
        global $api, $billingId;
        $id   = $array['id'];
        $name = $array['name'];
        Log::d("Add Tariff To UserSide. Id: '" . $id . "' Name: '" . $name . "'");
        $json = $api->sendDataToUserside("&cat=tariff&action=add&billing_id=" . $billingId . "&billing_tariff_id=" . $id . "&name=" . $name);
        if (isset($json['Id'])) {
            Log::d("Adding OK. Id: " . $json['Id']);
        } else {
            Log::d("Adding ERROR");
        }
        return $json;
    }

    private function editTariffInUserside($id, $bundle) {
        global $api;
        Log::d("Edit Tariff In UserSide. UserSide Id: '" . $id . "'");
        $post = array(
            'cat' => 'tariff',
            'action' => 'edit',
            'id' => $id
        );
        foreach ($bundle as $i => $value) {
            $post[$i] = $value;
        };
        $additionalString = '';
        foreach ($post as $i => $value) {
            $additionalString .= '&' . $i . '=' . $value;
        };
        $json = $api->sendDataToUserside($additionalString, $post);
        if (isset($json['Result']) && 'OK' == $json['Result']) {
            Log::d("Editing OK");
        } else {
            Log::d("Editing ERROR");
        }
    }

    public function compareTariff($billing, $userside) {
        Log::w("Compare Tariff");
        //Проходим все тарифы из биллинга
        foreach ($billing as $id => $value) {
            $name = $value['name'];
            $name = str_replace('/', '&#047;', $name);

            //Есть ли такой тариф в UserSide
            $id = strtoupper($id);
            if (!isset($userside[$id])) {
                //Нужно добавить тариф в UserSide
                Log::w("Tariff '" . $id . "' Not Found In UserSide. Adding...");
                $bundle = array(
                    'id' => $id,
                    'name' => $name
                );
                $this->addTariffToUserside($bundle);
            } else {
                //Этот тариф есть. Нужно сверить данные
                $userside[$id]['isFindInBilling'] = 1;
                $arrayChange                      = array();

                $tariff = $userside[$id];
                if (!isset($tariff['service_type'])) {
                    $tariff['service_type'] = 0;
                }

                //Название
                if ($tariff['name'] != $name) {
                    $arrayChange['name'] = $name;
                    Log::d("Tariff Data. Not Right 'name' US: '" . $tariff['name'] . "' - Billing: '" . $name . "'");
                }

                //Абонплата
                $tariff['payment'] = ceil(round($tariff['payment'], 2) * 100) * 0.01;
                if (isset($value['payment'])) {
                    $value['payment'] = ceil(round($value['payment'], 2) * 100) * 0.01;
                    if (0 == $value['payment'] && isset($value['payment_interval'])) {
                        $value['payment_interval'] = 30;
                    }
                    if ($value['payment'] != $tariff['payment']) {
                        $arrayChange['payment'] = $value['payment'];
                        Log::d("Tariff Data. Not Right 'payment' US: '" . $tariff['payment'] . "' - Billing: '" . $value['payment'] . "'");
                    }
                }

                //Интервал оплаты
                if (isset($value['payment_interval'])) {
                    if (1 > $value['payment_interval']) {
                        $value['payment_interval'] = 30;
                    }
                    if (29 == $value['payment_interval'] || 31 == $value['payment_interval']) {
                        $value['payment_interval'] = 30;
                    }
                    if (92 == $value['payment_interval']) {
                        $value['payment_interval'] = 90;
                    }
                    if (183 == $value['payment_interval']) {
                        $value['payment_interval'] = 180;
                    }
                    if (366 == $value['payment_interval']) {
                        $value['payment_interval'] = 360;
                    }
                    if ($value['payment_interval'] != $tariff['payment_interval']) {
                        $arrayChange['payment_interval'] = $value['payment_interval'];
                        $arrayChange['payment']          = $value['payment'];
                        Log::d("Tariff Data. Not Right 'payment_interval' US: '" . $tariff['payment_interval'] . "' (" . $tariff['payment'] . ") - Billing: '" . $value['payment_interval'] . "' (" . $value['payment'] . ")");
                    }
                }

                //Скорость
                if (isset($value['speed']['up']) && $value['speed']['up'] != $tariff['speed']['up']) {
                    $arrayChange['speed_tx'] = $value['speed']['up'];
                }
                if (isset($value['speed']['down']) && $value['speed']['down'] != $tariff['speed']['down']) {
                    $arrayChange['speed_rx'] = $value['speed']['down'];
                }

                //Трафик
                if (isset($value['traffic']) && $value['traffic'] != $tariff['traffic']) {
                    $arrayChange['traffic'] = $value['traffic'];
                }

                //Тип тарифа
                if (isset($value['service_type']) && $value['service_type'] != $tariff['service_type']) {
                    $arrayChange['service_type'] = $value['service_type'];
                }

                //Если стоит отметка, что нет в биллинге - убираем
                if (1 != $tariff['is_in_billing']) {
                    $arrayChange['is_in_billing'] = 1;
                }
                //Есть изменения
                if (0 < count($arrayChange)) {
                    $this->editTariffInUserside($tariff['userside_id'], $arrayChange);
                }

            }
        }

        //Тарифы, что не найдены в биллинге - помечаем
        foreach ($userside as $id => $value) {
            if (!isset($value['isFindInBilling']) && 1 == $value['is_in_billing']) {
                //Не найден в биллинге
                $bundle     = array(
                    'is_in_billing' => 0
                );
                $usersideId = $value['userside_id'];
                $this->editTariffInUserside($usersideId, $bundle);
            }
        }
    }

}

class ModuleHelper {
    public $lockFile; //Путь к файлу блокировки
    public $moduleApiName;

    public static function replaceNonUtfSymbol($text) {
        $unwanted_array = array('Š' => 'S', 'š' => 's', 'Ž' => 'Z', 'ž' => 'z', 'À' => 'A', 'Á' => 'A', 'Â' => 'A', 'Ã' => 'A', 'Ä' => 'A', 'Å' => 'A', 'Æ' => 'A', 'Ç' => 'C', 'È' => 'E', 'É' => 'E',
                                'Ê' => 'E', 'Ë' => 'E', 'Ì' => 'I', 'Í' => 'I', 'Î' => 'I', 'Ï' => 'I', 'Ñ' => 'N', 'Ò' => 'O', 'Ó' => 'O', 'Ô' => 'O', 'Õ' => 'O', 'Ö' => 'O', 'Ø' => 'O', 'Ù' => 'U',
                                'Ú' => 'U', 'Û' => 'U', 'Ü' => 'U', 'Ý' => 'Y', 'Þ' => 'B', 'ß' => 'Ss', 'à' => 'a', 'á' => 'a', 'â' => 'a', 'ã' => 'a', 'ä' => 'a', 'å' => 'a', 'æ' => 'a', 'ç' => 'c',
                                'è' => 'e', 'é' => 'e', 'ê' => 'e', 'ë' => 'e', 'ì' => 'i', 'í' => 'i', 'î' => 'i', 'ï' => 'i', 'ð' => 'o', 'ñ' => 'n', 'ò' => 'o', 'ó' => 'o', 'ô' => 'o', 'õ' => 'o',
                                'ö' => 'o', 'ø' => 'o', 'ù' => 'u', 'ú' => 'u', 'û' => 'u', 'ü' => 'u', 'ý' => 'y', 'þ' => 'b', 'ÿ' => 'y', 'Nº' => 'No', 'nº' => 'no', "'" => '&#039;');
        $text           = strtr($text, $unwanted_array);
        return $text;
    }

    public function systemOperation() {
        global $api, $billingId;
        //Осуществляем в UserSide все необходимые операции об окончании работы модуля
        $additionalUrl = "&cat=usm_billing&action=system_operation&billing_id=" . $billingId;
        $api->sendDataToUserside($additionalUrl);
    }

    public function initializeModule() {
        global $logPath, $logFile, $moduleInformation, $moduleName, $moduleVersion, $moduleRevision, $isSilence;
        global $usersideUrl, $billingId, $billingName, $api, $billing, $moduleHelper, $isAddOnModule, $isUpdateTraffic;
        global $isUpdateAddress, $moduleTimeStart, $lastPaidId, $confIpGrayNet, $confIpWhiteNet;

        $moduleTimeStart = microtime();

        $moduleInformation = $moduleName . " v." . $moduleVersion . '.' . $moduleRevision; // Version

        $this->moduleApiName = 'usm_billing';
        if (1 == $isAddOnModule) {
            $this->moduleApiName = $moduleName;
        }

        if ('/' != substr($logPath, -1, 1)) {
            $logPath .= '/';
        }

        Log::c($moduleInformation);
        Log::c("====================================");
        Log::c(" Start module at " . date('Y-m-d H:i:s'));

        Log::a("====================================");
        Log::a($moduleInformation . " - Module Start");

        $logFile          = $logPath . $moduleName . '.log';
        Log::$logDataBase = $logPath . $moduleName . '_db.log';
        Log::$logRaw      = $logPath . $moduleName . '_raw_';

        //Проверяем - есть ли файл блокировки и что там за время
        $this->lockFile = $logPath . $moduleName . '.lock';
        if (true == file_exists($this->lockFile)) {
            $file    = fopen($this->lockFile, "r");
            $content = fread($file, filesize($this->lockFile));
            fclose($file);
            if ('' == $content) {
                //Удаляем файл блокировки
                unlink($this->lockFile);
            } else {
                //Считаем - сколько времени было с последнего запуска и до сейчас
                $dateDiff = time() - $content;
                if ($dateDiff > 7200) {
                    unlink($this->lockFile);
                } else {
                    Log::c('Error: Find Lockfile');
                    Log::a($moduleInformation . " - Module Terminated - Find Lockfile");
                    $this->terminateModule();
                }
            }
        }
        //Создаём файл блокировки
        @$file = fopen($this->lockFile, "w");
        @fwrite($file, time());
        @fclose($file);
        if (true != file_exists($this->lockFile)) {
            Log::c('Verify Logs Path: ' . $logPath);
            $this->terminateModule();
        }

        //Удаляем старый лог
        if (true == file_exists($logFile)) {
            unlink($logFile);
        }
        if (true == file_exists(Log::$logDataBase)) {
            unlink(Log::$logDataBase);
        }
        if (true == file_exists(Log::$logRaw . 'tags.log')) {
            unlink(Log::$logRaw . 'tags.log');
        }
        if (true == file_exists(Log::$logRaw . 'tariff.log')) {
            unlink(Log::$logRaw . 'tariff.log');
        }
        if (true == file_exists(Log::$logRaw . 'tariff_2.log')) {
            unlink(Log::$logRaw . 'tariff_2.log');
        }
        if (true == file_exists(Log::$logRaw . 'tariff_period.log')) {
            unlink(Log::$logRaw . 'tariff_period.log');
        }
        if (true == file_exists(Log::$logRaw . 'customer.log')) {
            unlink(Log::$logRaw . 'customer.log');
        }
        if (true == file_exists(Log::$logRaw . 'customer_account.log')) {
            unlink(Log::$logRaw . 'customer_account.log');
        }
        if (true == file_exists(Log::$logRaw . 'customer_ip.log')) {
            unlink(Log::$logRaw . 'customer_ip.log');
        }
        if (true == file_exists(Log::$logRaw . 'address.log')) {
            unlink(Log::$logRaw . 'address.log');
        }

        //Начинаем писать в лог
        Log::w($moduleInformation . " - Module Start");
        Log::w("Module Path       : " . realpath(dirname(__FILE__))); //Путь к модулю
        Log::w("Log Path          : " . $logPath);
        Log::w("UserSide Path     : " . $usersideUrl);
        Log::w("isSilence         : " . $isSilence);
        Log::w("billingName       : " . $billingName);
        Log::w("billingId         : " . $billingId);
        Log::w("confIpGrayNet     : " . $confIpGrayNet);
        Log::w("confIpWhiteNet    : " . $confIpWhiteNet);

        $memoryLimit = strtoupper(ini_get('memory_limit'));
        Log::w("Memory Limit      : " . $memoryLimit);
        if (-1 != $memoryLimit) {
            if (0 < strpos($memoryLimit, 'G')) {
                $memoryLimit = str_replace('G', '', $memoryLimit);
                $memoryLimit *= 1024;
            }
            if (0 < strpos($memoryLimit, 'K')) {
                $memoryLimit = str_replace('K', '', $memoryLimit);
                $memoryLimit /= 1024;
            }
            $memoryLimit = str_replace('M', '', $memoryLimit);
            if (2048 > $memoryLimit) {
                ini_set('memory_limit', '2048M');
                Log::w("Memory Limit Set To 2048M");
            }
        }
        $postMaxSize = strtoupper(ini_get('post_max_size'));
        Log::w("Post Max Size     : " . $postMaxSize);

        if (1 > $billingId) {
            Log::c('Error: Setting billingNumber Is Empty');
            $moduleHelper->finishModule();
        }

        //Пытаемся подключиться к USERSIDE
        Log::w("Try UserSide API");
        $json = $api->verifyUsersideApi();
        Log::w("US - Date         : " . $json['date']);
        Log::w("US - OS           : " . $json['os']);
        Log::w("US - Version      : " . $json['erp']['version']);

        //Фиксируем в UserSide информацию о модуле
        $additionalUrl = "&cat=" . $this->moduleApiName . "&action=set_start_info&version=" . $moduleInformation . "&path=" . realpath(dirname(__FILE__)) . "&log_path=" . $logPath;
        $api->sendDataToUserside($additionalUrl);

        //Пытаемся подключиться к Биллингу
        Log::w("Try Billing API");
        if ('standart' == $billingName || 'carbon5' == $billingName) {
            $json = $api->verifyBillingApi();
        } else {
            $json = $billing->verifyBillingApi();
        }

        $json['os']                 = isset($json['os']) ? $json['os'] : '';
        $json['date']               = isset($json['date']) ? $json['date'] : '';
        $json['billing']['name']    = isset($json['billing']['name']) ? $json['billing']['name'] : '';
        $json['billing']['version'] = isset($json['billing']['version']) ? $json['billing']['version'] : '';

        Log::w("Billing - Date    : " . $json['date']);
        Log::w("Billing - OS      : " . $json['os']);
        Log::w("Billing - Name    : " . $json['billing']['name']);
        Log::w("Billing - Version : " . $json['billing']['version']);

        //Получаем параметры работы
        $isUpdateAddress = 0;
        $additionalUrl   = "&cat=usm_billing&action=get_settings&billing_id=" . $billingId;
        $json            = $api->sendDataToUserside($additionalUrl);
        $isUpdateAddress = isset($json['data']['is_update_address']) ? $json['data']['is_update_address'] : 0;
        $isUpdateTraffic = isset($json['data']['is_update_traffic']) ? $json['data']['is_update_traffic'] : 0;
        $lastPaidId      = isset($json['data']['last_paid_id']) ? $json['data']['last_paid_id'] : 0;
        Log::w("Settings");
        Log::w("isUpdateAddress   : " . $isUpdateAddress);
        Log::w("isUpdateTraffic   : " . $isUpdateTraffic);
        Log::w("lastPaidId        : " . $lastPaidId);
    }

    public function finishModule() {
        global $moduleInformation, $api, $billingName, $billing, $isDatabaseOpen;

        //Фиксируем в UserSide информацию об окончании работы модуля
        $additionalUrl = "&cat=" . $this->moduleApiName . "&action=set_stop_info";
        $api->sendDataToUserside($additionalUrl, array(), 'finish');

        if ('standart' == $billingName) {
            $counterBilling = $api->counterApiBilling;
        } else {
            $counterBilling = isset($billing->counterApiBilling) ? $billing->counterApiBilling : 0;
        }

        if (1 == $isDatabaseOpen) {
            //Закрываем соединение с базой биллинга
            Database::close();
        }

        Log::w("API Request Counter: UserSide: " . $api->counterApiUserside . " / Billing: " . $counterBilling);

        Log::a($moduleInformation . " - Module Finish");
        Log::w($moduleInformation . " - Module Finish");

        Log::c("Finish module at " . date('Y-m-d H:i:s'));
        Log::c("====================================");

        //Удаляем файл с блокировкой
        if (true == file_exists($this->lockFile)) {
            unlink($this->lockFile);
        }

        exit;
    }

    public function terminateModule() {
        Log::c("  Stop module at " . date('Y-m-d H:i:s'));
        Log::c("====================================");
        exit;
    }

}

class Log {
    static $logDataBase;
    static $logRaw;

    static function i($text) {
        self::c($text);
        self::w($text);
    }

    static function c($text) {
        global $isSilence;
        //Фиксация в консоль
        if (1 != $isSilence) {
            echo $text . "\n";
        }
    }

    static function w($text) {
        global $logFile;
        //Фиксация в лог
        @$file = fopen($logFile, "a");
        @fwrite($file, date('Y-m-d H:i:s') . ' - ' . $text . "\n");
        @fclose($file);
    }

    static function d($text) {
        global $logFile, $isDebugMode;
        //Фиксация в лог только дебага
        if (1 == $isDebugMode || 2 == $isDebugMode) {
            @$file = fopen($logFile, "a");
            @fwrite($file, date('Y-m-d H:i:s') . ' - ' . $text . "\n");
            @fclose($file);
        }
    }

    static function a($text) {
        global $logPath, $moduleName, $moduleHelper;

        //Фиксация в лог запуска
        $fileName = $logPath . $moduleName . '_run.log';
        //Удаляем большой файл
        if (true == file_exists($fileName)) {
            if (100000 < filesize($fileName)) {
                unlink($fileName);
            }
        }
        @$file = fopen($fileName, "a");
        @fwrite($file, date('Y-m-d H:i:s') . ' - ' . $text . "\n");
        @fclose($file);
        if (true != file_exists($fileName)) {
            Log::c('Error: Verify Logs Path: ' . $logPath);
            $moduleHelper->terminateModule();
        }
    }

    static function dBLog($text) {
        @$file = fopen(self::$logDataBase, "a");
        @fwrite($file, $text . "\n");
        @fclose($file);
    }

    static function rawLog($type, $array, $isDump = 0) {
        if (1 != $isDump) {
            @$file = fopen(self::$logRaw . $type . '.log', "a");
            $json = json_encode($array);
            @fwrite($file, '<?php' . "\n" . date('Y-m-d H:i:s') . "\n" . $json);
            @fclose($file);
        } else {
            @file_put_contents(self::$logRaw . $type . '.log', var_export($array, true), FILE_APPEND);
        }
    }

}

Class Api {
    private $usApiPath; //Путь к UserSide API
    private $billingApiPath; //Путь к Billing API
    public  $counterApiUserside = 0; //Счетчик запросов к UserSide
    public  $counterApiBilling  = 0; //Счетчик запросов к биллингу

    function sendDataToUserside($additionalUrl, $post = array(), $type = '') {
        global $moduleHelper;
        Log::d("Additional URL: " . $additionalUrl);
        ++$this->counterApiUserside;
        $url    = $this->usApiPath . $additionalUrl;
        $result = $this->readFromUrl($url, 1, $post);
        if ('' == $result && 'finish' != $type) {
            Log::c('Error: Empty Responce From UserSide API');
            $moduleHelper->finishModule();
        }
        return json_decode($result, true);
    }

    function getDataFromUserside($additionalUrl) {
        global $usersideUrl, $usersideApiKey, $moduleHelper;
        ++$this->counterApiUserside;
        if ('' == $this->usApiPath) {
            if ('/' != substr($usersideUrl, -1, 1)) {
                $usersideUrl .= '/';
            }
            $this->usApiPath = $usersideUrl . 'api.php?key=' . $usersideApiKey . '&cat=module';
        }
        $url    = $this->usApiPath . $additionalUrl;
        $result = $this->readFromUrl($url);
        if ('' == $result) {
            Log::c('Error: Empty Responce From UserSide API');
            $moduleHelper->finishModule();
        }
        return json_decode($result, true);
    }

    function getDataFromBilling($additionalUrl) {
        ++$this->counterApiBilling;
        global $billingUrl, $moduleHelper;
        $this->billingApiPath = $billingUrl;
        $url                  = $this->billingApiPath . $additionalUrl;
        $result               = $this->readFromUrl($url);
        if ('' == $result) {
            Log::c('Error: Empty Responce From Billing API');
            $moduleHelper->finishModule();
        }
        return json_decode($result, true);
    }

    function verifyUsersideApi() {
        global $moduleHelper;
        $json = $this->getDataFromUserside('&request=get_system_information');
        if (!isset($json['date'])) {
            Log::c('Error: Bad Responce From UserSide API');
            $moduleHelper->finishModule();
        }
        return $json;
    }

    function verifyBillingApi() {
        global $moduleHelper, $billingName;
        if ('carbon5' == $billingName) {
            $request = '&method1=userside_manager.get_system_information';
        } else {
            $request = '&request=get_system_information';
        }
        $json = $this->getDataFromBilling($request);
        if (!isset($json['date'])) {
            Log::c('Error: Bad Responce From Billing API');
            $moduleHelper->finishModule();
        }
        return $json;
    }

    public function readFromUrl($url, $isPost = 0, $bundle = array()) {
        global $moduleHelper, $usersideApiKey, $isDebugMode;
        $result = '';
        if (function_exists("curl_init")) {
            $c = curl_init();
            if (1 == $isPost) {
                list($url, $post) = explode('?', $url, 2);
                if (0 < count($bundle)) {
                    $bundle['key'] = $usersideApiKey;
                    $post          = http_build_query($bundle);
                }
                curl_setopt($c, CURLOPT_POST, 1);
                curl_setopt($c, CURLOPT_POSTFIELDS, $post);
            } elseif (2 == $isDebugMode) {
                Log::d($url);
            }
            curl_setopt($c, CURLOPT_URL, $url);
            curl_setopt($c, CURLOPT_RETURNTRANSFER, true);
            curl_setopt($c, CURLOPT_CONNECTTIMEOUT, 600);
            curl_setopt($c, CURLOPT_TIMEOUT, 600);
            curl_setopt($c, CURLOPT_SSL_VERIFYPEER, 0);
            curl_setopt($c, CURLOPT_SSL_VERIFYHOST, false);
            $result = curl_exec($c);
            curl_close($c);
            Log::d(str_replace("\n", ' ', $result));
        } else {
            Log::c('Error: Function curl_init Not Found');
            $moduleHelper->finishModule();
        }
        return $result;
    }

}

Class Database {
    static $connection;
    static $queryCount = 0;

    static function connect($host, $user, $password, $database, $port) {
        global $moduleHelper;
        self::$connection = mysqli_connect($host, $user, $password, $database, $port);
        if (!self::$connection) {
            Log::i("Can't connect to Billing DB");
            $moduleHelper->finishModule();
        }
        Database::query('SET CHARACTER SET UTF8');
        Database::query('SET NAMES UTF8');
    }

    static function close() {
        Log::w("DataBase Query Counter: " . self::$queryCount);
        mysqli_close(self::$connection);
    }

    static function query($query, $isArray = false, $idName = '') {
        global $moduleTimeStart;
        ++self::$queryCount;
        Log::dBLog("==========================================================\n" . self::$queryCount . ". Time: " . date('Y-m-d H:i:s') . "\n\n" . $query . "\n");
        $timeStart  = microtime();
        $recordSet  = mysqli_query(Database::$connection, $query);
        $timeFinish = microtime();
        if (0 < mysqli_errno(Database::$connection)) {
            Log::dBLog("Error N: " . mysqli_errno(Database::$connection) . ': ' . mysqli_error(Database::$connection));
        }
        if (true == $isArray) {
            $count    = 0;
            $responce = array();
            if ('' == $idName) {
                while ($rs = mysqli_fetch_array($recordSet)) {
                    $responce[$rs[0]] = $rs;
                    ++$count;
                }
            } else {
                while ($rs = mysqli_fetch_assoc($recordSet)) {
                    $responce[$rs[$idName]] = $rs;
                    ++$count;
                }
            }
        } else {
            @$responce = mysqli_fetch_array($recordSet);
        }
        @mysqli_free_result($recordSet);
        $array      = explode(' ', $timeStart);
        $timeStart  = $array[1] + $array[0];
        $array      = explode(' ', $timeFinish);
        $timeFinish = $array[1] + $array[0];
        $timeUse    = round(($timeFinish - $timeStart) * 1000) * 0.001;
        $textLog    = "Finish: " . $timeUse;

        $array     = explode(' ', $moduleTimeStart);
        $timeStart = $array[1] + $array[0];
        $timeUse   = round(($timeFinish - $timeStart) * 1000) * 0.001;
        $textLog .= " / " . $timeUse;

        if (true == $isArray) {
            $textLog .= " - ResultCount: " . $count;
        }
        Log::dBLog($textLog . "\n");
        return $responce;
    }

}