%PDF- %PDF-
| Direktori : /home/bitrix/www/bitrix/modules/sale/lib/location/migration/ |
| Current File : //home/bitrix/www/bitrix/modules/sale/lib/location/migration/migrate.php |
<?php
namespace Bitrix\Sale\Location\Migration;
use Bitrix\Main;
use Bitrix\Main\Config;
use Bitrix\Sale\Delivery;
use Bitrix\Sale;
use Bitrix\Main\Localization\Loc;
use Bitrix\Sale\Location;
use Bitrix\Sale\Location\DB\Helper;
use Bitrix\Sale\Location\DB\BlockInserter;
include_once($_SERVER["DOCUMENT_ROOT"].BX_ROOT."/modules/main/classes/general/update_class.php");
Loc::loadMessages(__FILE__);
class CUpdaterLocationPro extends \CUpdater implements \Serializable
{
// old tables that will be reused
const TABLE_LOCATION = 'b_sale_location';
const TABLE_LOCATION_GROUP = 'b_sale_location_group';
const TABLE_LOCATION2GROUP = 'b_sale_location2location_group';
const TABLE_DELIVERY2LOCATION = 'b_sale_delivery2location';
const TABLE_TAX2LOCATION = 'b_sale_tax2location';
const TABLE_LOCATION_GROUP_NAME = 'b_sale_location_group_lang';
// new tables to be created
const TABLE_LOCATION_NAME = 'b_sale_loc_name';
const TABLE_LOCATION_EXTERNAL = 'b_sale_loc_ext';
const TABLE_LOCATION_EXTERNAL_SERVICE = 'b_sale_loc_ext_srv';
const TABLE_LOCATION_TYPE = 'b_sale_loc_type';
const TABLE_LOCATION_TYPE_NAME = 'b_sale_loc_type_name';
const TABLE_LOCATION2SITE = 'b_sale_loc_2site'; // ?
// obsolete tables to get data from
const TABLE_LOCATION_ZIP = 'b_sale_location_zip';
const TABLE_LOCATION_COUNTRY_NAME = 'b_sale_location_country_lang';
const TABLE_LOCATION_REGION_NAME = 'b_sale_location_region_lang';
const TABLE_LOCATION_CITY_NAME = 'b_sale_location_city_lang';
// temporal tables
const TABLE_TEMP_TREE = 'b_sale_location_temp_tree';
const TABLE_LEGACY_RELATIONS = 'b_sale_loc_legacy';
const MODULE_ID = 'sale';
protected $data = array();
// to CUpdater ?
const DB_TYPE_MYSQL = 'MySQL';
const DB_TYPE_MSSQL = 'MSSQL';
const DB_TYPE_ORACLE = 'Oracle';
const DB_TYPE_MYSQL_LC = 'mysql';
const DB_TYPE_MSSQL_LC = 'mssql';
const DB_TYPE_ORACLE_LC = 'oracle';
public function __construct()
{
global $DBType;
$this->Init($curPath = "", $DBType, $updaterName = "", $curDir = "", self::MODULE_ID, "DB");
}
public function serialize()
{
return serialize($this->data);
}
public function unserialize($data)
{
global $DBType;
$this->Init($curPath = "", $DBType, $updaterName = "", $curDir = "", self::MODULE_ID, "DB");
$this->data = unserialize($data);
}
public static function updateDBSchemaRestoreLegacyIndexes()
{
$dbConnection = \Bitrix\Main\HttpApplication::getConnection();
$agentName = '\Bitrix\Sale\Location\Migration\CUpdaterLocationPro::updateDBSchemaRestoreLegacyIndexes();';
if(!Helper::checkIndexNameExists('IX_B_SALE_LOC_EXT_LID_SID', 'b_sale_loc_ext'))
{
$dbConnection->query('create index IX_B_SALE_LOC_EXT_LID_SID on b_sale_loc_ext (LOCATION_ID, SERVICE_ID)');
return $agentName;
}
if(!Helper::checkIndexNameExists('IXS_LOCATION_COUNTRY_ID', 'b_sale_location'))
{
$dbConnection->query('create index IXS_LOCATION_COUNTRY_ID on b_sale_location (COUNTRY_ID)');
return $agentName;
}
if(!Helper::checkIndexNameExists('IXS_LOCATION_REGION_ID', 'b_sale_location'))
{
$dbConnection->query('create index IXS_LOCATION_REGION_ID on b_sale_location (REGION_ID)');
return $agentName;
}
if(!Helper::checkIndexNameExists('IXS_LOCATION_CITY_ID', 'b_sale_location'))
{
$dbConnection->query('create index IXS_LOCATION_CITY_ID on b_sale_location (CITY_ID)');
}
return false;
}
// only for module_updater
public static function updateDBSchemaRenameIndexes()
{
global $DB, $DBType;
$updater = new \CUpdater();
$updater->Init($curPath = "", $DBType, $updaterName = "", $curDir = "", "sale", "DB");
$locationTableExists = $updater->TableExists("b_sale_location");
if($locationTableExists) // module might not be installed, but tables may exist
{
// b_sale_location
if(static::checkIndexExistsByName('IX_SALE_LOCATION_CODE', 'b_sale_location'))
{
static::dropIndexByName('IX_SALE_LOCATION_CODE', 'b_sale_location');
$DB->query('create unique index IX_B_SALE_LOC_CODE on b_sale_location (CODE)');
}
if(static::checkIndexExistsByName('IX_SALE_LOCATION_MARGINS', 'b_sale_location'))
{
static::dropIndexByName('IX_SALE_LOCATION_MARGINS', 'b_sale_location');
$DB->query('create index IX_B_SALE_LOC_MARGINS on b_sale_location (LEFT_MARGIN, RIGHT_MARGIN)');
}
if(static::checkIndexExistsByName('IX_SALE_LOCATION_MARGINS_REV', 'b_sale_location'))
{
static::dropIndexByName('IX_SALE_LOCATION_MARGINS_REV', 'b_sale_location');
$DB->query('create index IX_B_SALE_LOC_MARGINS_REV on b_sale_location (RIGHT_MARGIN, LEFT_MARGIN)');
}
if(static::checkIndexExistsByName('IX_SALE_LOCATION_PARENT', 'b_sale_location'))
{
static::dropIndexByName('IX_SALE_LOCATION_PARENT', 'b_sale_location');
$DB->query('create index IX_B_SALE_LOC_PARENT on b_sale_location (PARENT_ID)');
}
if(static::checkIndexExistsByName('IX_SALE_LOCATION_DL', 'b_sale_location'))
{
static::dropIndexByName('IX_SALE_LOCATION_DL', 'b_sale_location');
$DB->query('create index IX_B_SALE_LOC_DL on b_sale_location (DEPTH_LEVEL)');
}
if(static::checkIndexExistsByName('IX_SALE_LOCATION_TYPE', 'b_sale_location'))
{
static::dropIndexByName('IX_SALE_LOCATION_TYPE', 'b_sale_location');
$DB->query('create index IX_B_SALE_LOC_TYPE on b_sale_location (TYPE_ID)');
}
// b_sale_loc_name
if(static::checkIndexExistsByName('IX_SALE_L_NAME_NAME_UPPER', 'b_sale_loc_name'))
{
static::dropIndexByName('IX_SALE_L_NAME_NAME_UPPER', 'b_sale_loc_name');
$DB->query('create index IX_B_SALE_LOC_NAME_NAME_U on b_sale_loc_name (NAME_UPPER)');
}
if(static::checkIndexExistsByName('IX_SALE_L_NAME_LID_LID', 'b_sale_loc_name'))
{
static::dropIndexByName('IX_SALE_L_NAME_LID_LID', 'b_sale_loc_name');
$DB->query('create index IX_B_SALE_LOC_NAME_LI_LI on b_sale_loc_name (LOCATION_ID, LANGUAGE_ID)');
}
// b_sale_loc_type_name
if(static::checkIndexExistsByName('IX_SALE_L_TYPE_NAME_TID_LID', 'b_sale_loc_type_name'))
{
static::dropIndexByName('IX_SALE_L_TYPE_NAME_TID_LID', 'b_sale_loc_type_name');
$DB->query('create index IX_B_SALE_LOC_TYPE_NAME_TI_LI on b_sale_loc_type_name (TYPE_ID, LANGUAGE_ID)');
}
// b_sale_location_group
if(static::checkIndexExistsByName('IX_SALE_LOCATION_GROUP_CODE', 'b_sale_location_group'))
{
static::dropIndexByName('IX_SALE_LOCATION_GROUP_CODE', 'b_sale_location_group');
$DB->query('create unique index IX_B_SALE_LOC_GROUP_CODE on b_sale_location_group (CODE)');
}
}
}
protected static function dropIndexByName($indexName, $tableName)
{
$dbConnection = Main\HttpApplication::getConnection();
$dbConnType = $dbConnection->getType();
if($dbConnType == self::DB_TYPE_MYSQL_LC)
$dbConnection->query("alter table {$tableName} drop index {$indexName}");
elseif($dbConnType == self::DB_TYPE_ORACLE_LC)
$dbConnection->query("drop index {$indexName}");
elseif($dbConnType == self::DB_TYPE_MSSQL_LC)
$dbConnection->query("drop index {$indexName} on {$tableName}");
return true;
}
protected static function checkIndexExistsByName($indexName, $tableName)
{
if(!strlen($indexName) || !strlen($tableName))
return false;
$dbConnection = Main\HttpApplication::getConnection();
$dbConnType = $dbConnection->getType();
if($dbConnType == self::DB_TYPE_MYSQL_LC)
$res = $dbConnection->query("show index from ".$tableName);
elseif($dbConnType == self::DB_TYPE_ORACLE_LC)
$res = $dbConnection->query("SELECT INDEX_NAME as Key_name FROM USER_IND_COLUMNS WHERE TABLE_NAME = '".ToUpper($tableName)."'");
elseif($dbConnType == self::DB_TYPE_MSSQL_LC)
{
$res = $dbConnection->query("SELECT si.name Key_name
FROM sysindexkeys s
INNER JOIN syscolumns c ON s.id = c.id AND s.colid = c.colid
INNER JOIN sysobjects o ON s.id = o.Id AND o.xtype = 'U'
LEFT JOIN sysindexes si ON si.indid = s.indid AND si.id = s.id
WHERE o.name = '".ToUpper($tableName)."'");
}
while($item = $res->fetch())
{
if($item['Key_name'] == $indexName || $item['KEY_NAME'] == $indexName)
return true;
}
return false;
}
// function stands for the corresponding block in module_updater.php
public static function updateDBSchema()
{
global $DB, $DBType;
$updater = new \CUpdater();
$updater->Init($curPath = "", $DBType, $updaterName = "", $curDir = "", "sale", "DB");
// table existence check
$locationTableExists = $updater->TableExists("b_sale_location");
if($locationTableExists) // module might not be installed, but tables may exist
{
$locationGroupTableExists = $updater->TableExists("b_sale_location_group");
$locationGroupNameTableExists = $updater->TableExists("b_sale_location_group_lang");
$locationNameTableExists = $updater->TableExists("b_sale_loc_name");
$locationExternalServiceTableExists = $updater->TableExists("b_sale_loc_ext_srv");
$locationExternalTableExists = $updater->TableExists("b_sale_loc_ext");
$locationTypeTableExists = $updater->TableExists("b_sale_loc_type");
$locationTypeNameTableExists = $updater->TableExists("b_sale_loc_type_name");
$locationLoc2SiteTableExists = $updater->TableExists("b_sale_loc_2site");
$locationDefaul2SiteTableExists = $updater->TableExists("b_sale_loc_def2site");
$tax2LocationTableExists = $updater->TableExists("b_sale_tax2location");
$delivery2LocationTableExists = $updater->TableExists("b_sale_delivery2location");
// adding columns to B_SALE_LOCATION
// if CODE not exists, add it
if (!$DB->query("select CODE from b_sale_location WHERE 1=0", true))
{
$updater->query(array(
"MySQL" => "ALTER TABLE b_sale_location ADD CODE varchar(100) not null",
"MSSQL" => "ALTER TABLE B_SALE_LOCATION ADD CODE varchar(100) default '' NOT NULL", // OK
"Oracle" => "ALTER TABLE B_SALE_LOCATION ADD CODE VARCHAR2(100 CHAR) default '' NOT NULL", //OK // oracle allows to add not-null column only with default specified
));
}
// if CODE exists, copy values from ID and add index
if ($DB->query("select CODE from b_sale_location WHERE 1=0", true))
{
if (!$DB->IndexExists('b_sale_location', array('CODE')))
{
$DB->query("update b_sale_location set CODE = ID"); // OK: oracle, mssql
$DB->query("CREATE UNIQUE INDEX IX_B_SALE_LOC_CODE ON b_sale_location (CODE)"); // OK: oracle, mssql
}
}
// create LEFT_MARGIN
if (!$DB->query("select LEFT_MARGIN from b_sale_location WHERE 1=0", true))
{
$updater->query(array(
"MySQL" => "ALTER TABLE b_sale_location ADD LEFT_MARGIN int",
"MSSQL" => "ALTER TABLE B_SALE_LOCATION ADD LEFT_MARGIN int", // OK
"Oracle" => "ALTER TABLE B_SALE_LOCATION ADD LEFT_MARGIN NUMBER(18)", // OK
));
}
// create RIGHT_MARGIN
if (!$DB->query("select RIGHT_MARGIN from b_sale_location WHERE 1=0", true))
{
$updater->query(array(
"MySQL" => "ALTER TABLE b_sale_location ADD RIGHT_MARGIN int",
"MSSQL" => "ALTER TABLE B_SALE_LOCATION ADD RIGHT_MARGIN int", // OK
"Oracle" => "ALTER TABLE B_SALE_LOCATION ADD RIGHT_MARGIN NUMBER(18)", // OK
));
}
$lMarginExists = $DB->query("select LEFT_MARGIN from b_sale_location WHERE 1=0", true);
$rMarginExists = $DB->query("select RIGHT_MARGIN from b_sale_location WHERE 1=0", true);
// add indexes if margins exist, but indexes not
if($lMarginExists && $rMarginExists)
{
if (!$DB->IndexExists('b_sale_location', array('LEFT_MARGIN', 'RIGHT_MARGIN')))
{
$DB->query("CREATE INDEX IX_B_SALE_LOC_MARGINS ON b_sale_location (LEFT_MARGIN, RIGHT_MARGIN)"); // OK: oracle, mssql
}
if (!$DB->IndexExists('b_sale_location', array('RIGHT_MARGIN', 'LEFT_MARGIN')))
{
$DB->query("CREATE INDEX IX_B_SALE_LOC_MARGINS_REV ON b_sale_location (RIGHT_MARGIN, LEFT_MARGIN)"); // OK: oracle, mssql
}
}
// add PARENT_ID
if (!$DB->query("select PARENT_ID from b_sale_location WHERE 1=0", true))
{
$updater->query(array(
"MySQL" => "ALTER TABLE b_sale_location ADD PARENT_ID int DEFAULT '0'",
"MSSQL" => "ALTER TABLE B_SALE_LOCATION ADD PARENT_ID int DEFAULT '0'", // OK
"Oracle" => "ALTER TABLE B_SALE_LOCATION ADD PARENT_ID NUMBER(18) DEFAULT '0'", // OK
));
}
// add index, if not exist for PARENT_ID, that exists
if ($DB->query("select PARENT_ID from b_sale_location WHERE 1=0", true) && !$DB->IndexExists('b_sale_location', array('PARENT_ID')))
{
$DB->query('CREATE INDEX IX_B_SALE_LOC_PARENT ON b_sale_location (PARENT_ID)'); // OK: oracle, mssql
}
// add DEPTH_LEVEL
if (!$DB->query("select DEPTH_LEVEL from b_sale_location WHERE 1=0", true))
{
$updater->query(array(
"MySQL" => "ALTER TABLE b_sale_location ADD DEPTH_LEVEL int default '1'",
"MSSQL" => "ALTER TABLE B_SALE_LOCATION ADD DEPTH_LEVEL int DEFAULT '1'", // OK
"Oracle" => "ALTER TABLE B_SALE_LOCATION ADD DEPTH_LEVEL NUMBER(18) DEFAULT '1'", // OK
));
}
// add index, if not exist for DEPTH_LEVEL, that exists
if ($DB->query("select DEPTH_LEVEL from b_sale_location WHERE 1=0", true) && !$DB->IndexExists('b_sale_location', array('DEPTH_LEVEL')))
{
$DB->query("CREATE INDEX IX_B_SALE_LOC_DL ON b_sale_location (DEPTH_LEVEL)"); // OK: oracle, mssql
}
// add TYPE_ID
if (!$DB->query("select TYPE_ID from b_sale_location WHERE 1=0", true))
{
$updater->query(array(
"MySQL" => "ALTER TABLE b_sale_location ADD TYPE_ID int",
"MSSQL" => "ALTER TABLE B_SALE_LOCATION ADD TYPE_ID int", // OK
"Oracle" => "ALTER TABLE B_SALE_LOCATION ADD TYPE_ID NUMBER(18)", // OK
));
}
// add index, if not exist for TYPE_ID, that exists
if ($DB->query("select TYPE_ID from b_sale_location WHERE 1=0", true) && !$DB->IndexExists('b_sale_location', array('TYPE_ID')))
{
$DB->query("CREATE INDEX IX_B_SALE_LOC_TYPE ON b_sale_location (TYPE_ID)"); // OK: oracle, mssql
}
// add LATITUDE
if (!$DB->query("select LATITUDE from b_sale_location WHERE 1=0", true))
{
$updater->query(array(
"MySQL" => "ALTER TABLE b_sale_location ADD LATITUDE decimal(8,6)",
"MSSQL" => "ALTER TABLE B_SALE_LOCATION ADD LATITUDE decimal(8,6)", // OK
"Oracle" => "ALTER TABLE B_SALE_LOCATION ADD LATITUDE NUMBER(8,6)", // OK
));
}
// add LONGITUDE
if (!$DB->query("select LONGITUDE from b_sale_location WHERE 1=0", true))
{
$updater->query(array(
"MySQL" => "ALTER TABLE b_sale_location ADD LONGITUDE decimal(9,6)",
"MSSQL" => "ALTER TABLE B_SALE_LOCATION ADD LONGITUDE decimal(9,6)", // OK
"Oracle" => "ALTER TABLE B_SALE_LOCATION ADD LONGITUDE NUMBER(9,6)", // OK
));
}
// dropping not-nulls
if($DBType == 'mysql')
$DB->query("ALTER TABLE b_sale_location MODIFY COUNTRY_ID int NULL");
if($DBType == 'mssql')
$DB->query("ALTER TABLE B_SALE_LOCATION ALTER COLUMN COUNTRY_ID int NULL");
if($DBType == 'oracle')
{
// dropping not-nulls
if($DB->query("SELECT * FROM ALL_TAB_COLUMNS WHERE TABLE_NAME = 'B_SALE_LOCATION' and COLUMN_NAME = 'COUNTRY_ID' and NULLABLE = 'N'")->fetch())
{
//if ($DB->IndexExists('b_sale_location', array('COUNTRY_ID')))
// $DB->query('drop index IXS_LOCATION_COUNTRY_ID');
$DB->query("ALTER TABLE B_SALE_LOCATION MODIFY ( COUNTRY_ID NUMBER(18) NULL)");
}
//if (!$DB->IndexExists('b_sale_location', array('COUNTRY_ID')))
// $DB->query('CREATE INDEX IXS_LOCATION_COUNTRY_ID ON B_SALE_LOCATION(COUNTRY_ID)');
// altering sequences for oracle
// new sequence for b_sale_location
if (($DB->query("select * from USER_OBJECTS where OBJECT_TYPE = 'SEQUENCE' and OBJECT_NAME = 'SQ_SALE_LOCATION'", true)->fetch())) // OK
{
$DB->query("RENAME SQ_SALE_LOCATION TO SQ_B_SALE_LOCATION"); // OK
$DB->query("CREATE OR REPLACE TRIGGER B_SALE_LOCATION_INSERT
BEFORE INSERT
ON B_SALE_LOCATION
FOR EACH ROW
BEGIN
IF :NEW.ID IS NULL THEN
SELECT SQ_B_SALE_LOCATION.NEXTVAL INTO :NEW.ID FROM dual;
END IF;
END;"); // OK
}
// new sequence for b_sale_location_group
if ($locationGroupTableExists && !($DB->query("select * from USER_OBJECTS where OBJECT_TYPE = 'SEQUENCE' and OBJECT_NAME = 'SQ_B_SALE_LOCATION_GROUP'", true)->fetch())) // OK
{
$DB->query("RENAME SQ_SALE_LOCATION_GROUP TO SQ_B_SALE_LOCATION_GROUP"); // OK
$DB->query("CREATE OR REPLACE TRIGGER B_SALE_LOCATION_GROUP_INSERT
BEFORE INSERT
ON B_SALE_LOCATION_GROUP
FOR EACH ROW
BEGIN
IF :NEW.ID IS NULL THEN
SELECT SQ_B_SALE_LOCATION_GROUP.NEXTVAL INTO :NEW.ID FROM dual;
END IF;
END;"); // OK
}
// new sequence for b_sale_location_group_lang
if ($locationGroupNameTableExists && !($DB->query("select * from USER_OBJECTS where OBJECT_TYPE = 'SEQUENCE' and OBJECT_NAME = 'SQ_B_SALE_LOCATION_GROUP_LANG'", true)->fetch())) // OK
{
$DB->query("RENAME SQ_SALE_LOCATION_GROUP_LANG TO SQ_B_SALE_LOCATION_GROUP_LANG"); // OK
$DB->query("CREATE OR REPLACE TRIGGER B_SALE_LOCGR_LANG_INSERT
BEFORE INSERT
ON B_SALE_LOCATION_GROUP_LANG
FOR EACH ROW
BEGIN
IF :NEW.ID IS NULL THEN
SELECT SQ_B_SALE_LOCATION_GROUP_LANG.NEXTVAL INTO :NEW.ID FROM dual;
END IF;
END;"); // OK
}
}
// adding columns to B_SALE_LOCATION_GROUP
if($locationGroupTableExists)
{
if (!$DB->query("select CODE from b_sale_location_group WHERE 1=0", true))
{
$updater->query(array(
"MySQL" => "ALTER TABLE b_sale_location_group ADD CODE varchar(100) NOT NULL",
"MSSQL" => "ALTER TABLE B_SALE_LOCATION_GROUP ADD CODE varchar(100) default '' NOT NULL", // OK
"Oracle" => "ALTER TABLE B_SALE_LOCATION_GROUP ADD CODE VARCHAR2(100 CHAR) default '' NOT NULL", //OK // oracle allows to add not-null column only with default specified
));
}
// if CODE exists, copy values from ID and add index
if ($DB->query("select CODE from b_sale_location_group WHERE 1=0", true))
{
if (!$DB->IndexExists('b_sale_location_group', array('CODE')))
{
$DB->query("update b_sale_location_group set CODE = ID"); // OK: oracle, mssql
$DB->query("CREATE UNIQUE INDEX IX_B_SALE_LOC_GROUP_CODE ON b_sale_location_group (CODE)"); // OK: oracle, mssql
}
}
}
if (!$locationNameTableExists)
{
$updater->query(array(
"MySQL" => "create table b_sale_loc_name (
ID int not null auto_increment,
LANGUAGE_ID char(2) not null,
LOCATION_ID int not null,
NAME varchar(100) not null,
NAME_UPPER varchar(100) not null,
SHORT_NAME varchar(100),
primary key (ID)
)",
"MSSQL" => "CREATE TABLE B_SALE_LOC_NAME (
ID int NOT NULL IDENTITY (1, 1),
LANGUAGE_ID char(2) NOT NULL,
LOCATION_ID int NOT NULL,
NAME varchar(100) NOT NULL,
NAME_UPPER varchar(100) NOT NULL,
SHORT_NAME varchar(100)
CONSTRAINT PK_B_SALE_LOC_NAME PRIMARY KEY (ID)
)", // OK
"Oracle" => "CREATE TABLE B_SALE_LOC_NAME(
ID NUMBER(18) NOT NULL,
LANGUAGE_ID CHAR(2 CHAR) NOT NULL,
LOCATION_ID NUMBER(18) NOT NULL,
NAME VARCHAR2(100 CHAR) NOT NULL,
NAME_UPPER VARCHAR2(100 CHAR) NOT NULL,
SHORT_NAME VARCHAR2(100 CHAR),
PRIMARY KEY (ID)
)", // OK
));
$locationNameTableExists = true;
}
if($DBType == 'oracle' && $locationNameTableExists && !($DB->query("select * from USER_OBJECTS where OBJECT_TYPE = 'SEQUENCE' and OBJECT_NAME = 'SQ_B_SALE_LOC_NAME'", true)->fetch()))
{
$DB->query("CREATE SEQUENCE SQ_B_SALE_LOC_NAME INCREMENT BY 1 NOMAXVALUE NOCYCLE NOCACHE NOORDER"); // OK
$DB->query("CREATE OR REPLACE TRIGGER B_SALE_LOC_NAME_INSERT
BEFORE INSERT
ON B_SALE_LOC_NAME
FOR EACH ROW
BEGIN
IF :NEW.ID IS NULL THEN
SELECT SQ_B_SALE_LOC_NAME.NEXTVAL INTO :NEW.ID FROM dual;
END IF;
END;"); // OK
}
if ($locationNameTableExists)
{
if (!$DB->IndexExists('b_sale_loc_name', array('NAME_UPPER')))
{
$DB->query("CREATE INDEX IX_B_SALE_LOC_NAME_NAME_U ON b_sale_loc_name (NAME_UPPER)"); // OK: oracle, mssql
}
if (!$DB->IndexExists('b_sale_loc_name', array('LOCATION_ID', 'LANGUAGE_ID')))
{
$DB->query("CREATE INDEX IX_B_SALE_LOC_NAME_LI_LI ON b_sale_loc_name (LOCATION_ID, LANGUAGE_ID)"); // OK: oracle, mssql
}
}
if (!$locationExternalServiceTableExists)
{
$updater->query(array(
"MySQL" => "create table b_sale_loc_ext_srv(
ID int not null auto_increment,
CODE varchar(100) not null,
primary key (ID)
)",
"MSSQL" => "CREATE TABLE B_SALE_LOC_EXT_SRV(
ID int NOT NULL IDENTITY (1, 1),
CODE varchar(100) NOT NULL
CONSTRAINT PK_B_SALE_LOC_EXT_SRV PRIMARY KEY (ID)
)", // OK
"Oracle" => "CREATE TABLE B_SALE_LOC_EXT_SRV(
ID NUMBER(18) NOT NULL,
CODE VARCHAR2(100 CHAR) NOT NULL,
PRIMARY KEY (ID)
)", // OK
));
$locationExternalServiceTableExists = true;
}
if($DBType == 'oracle' && $locationExternalServiceTableExists && !($DB->query("select * from USER_OBJECTS where OBJECT_TYPE = 'SEQUENCE' and OBJECT_NAME = 'SQ_B_SALE_LOC_EXT_SRV'", true)->fetch()))
{
$DB->query("CREATE SEQUENCE SQ_B_SALE_LOC_EXT_SRV INCREMENT BY 1 NOMAXVALUE NOCYCLE NOCACHE NOORDER"); // OK
$DB->query("CREATE OR REPLACE TRIGGER B_SALE_LOC_EXT_SRV_INSERT
BEFORE INSERT
ON B_SALE_LOC_EXT_SRV
FOR EACH ROW
BEGIN
IF :NEW.ID IS NULL THEN
SELECT SQ_B_SALE_LOC_EXT_SRV.NEXTVAL INTO :NEW.ID FROM dual;
END IF;
END;"); // OK
}
if (!$locationExternalTableExists)
{
$updater->query(array(
"MySQL" => "create table b_sale_loc_ext(
ID int not null auto_increment,
SERVICE_ID int not null,
LOCATION_ID int not null,
XML_ID varchar(100) not null,
primary key (ID)
)",
"MSSQL" => "CREATE TABLE B_SALE_LOC_EXT(
ID int NOT NULL IDENTITY (1, 1),
SERVICE_ID int NOT NULL,
LOCATION_ID int NOT NULL,
XML_ID varchar(100) NOT NULL
CONSTRAINT PK_B_SALE_LOC_EXT PRIMARY KEY (ID)
)", // OK
"Oracle" => "CREATE TABLE B_SALE_LOC_EXT(
ID NUMBER(18) NOT NULL,
SERVICE_ID NUMBER(18) NOT NULL,
LOCATION_ID NUMBER(18) NOT NULL,
XML_ID VARCHAR2(100 CHAR) NOT NULL,
PRIMARY KEY (ID)
)", // OK
));
$locationExternalTableExists = true;
}
if($DBType == 'oracle' && $locationExternalTableExists && !($DB->query("select * from USER_OBJECTS where OBJECT_TYPE = 'SEQUENCE' and OBJECT_NAME = 'SQ_B_SALE_LOC_EXT'", true)->fetch()))
{
$DB->query("CREATE SEQUENCE SQ_B_SALE_LOC_EXT INCREMENT BY 1 NOMAXVALUE NOCYCLE NOCACHE NOORDER"); // OK
$DB->query("CREATE OR REPLACE TRIGGER B_SALE_LOC_EXT_INSERT
BEFORE INSERT
ON B_SALE_LOC_EXT
FOR EACH ROW
BEGIN
IF :NEW.ID IS NULL THEN
SELECT SQ_B_SALE_LOC_EXT.NEXTVAL INTO :NEW.ID FROM dual;
END IF;
END;");// OK
}
if ($locationExternalTableExists && !$DB->IndexExists('b_sale_loc_ext', array('LOCATION_ID', 'SERVICE_ID')))
{
$DB->query("CREATE INDEX IX_B_SALE_LOC_EXT_LID_SID ON b_sale_loc_ext (LOCATION_ID, SERVICE_ID)"); // OK: oracle, mssql
}
if (!$locationTypeTableExists)
{
$updater->query(array(
"MySQL" => "create table b_sale_loc_type(
ID int not null auto_increment,
CODE varchar(30) not null,
SORT int default '100',
primary key (ID)
)",
"MSSQL" => "CREATE TABLE B_SALE_LOC_TYPE(
ID int NOT NULL IDENTITY (1, 1),
CODE varchar(30) NOT NULL,
SORT int
CONSTRAINT PK_B_SALE_LOC_TYPE PRIMARY KEY (ID)
)", // OK
"Oracle" => "CREATE TABLE B_SALE_LOC_TYPE(
ID NUMBER(18) NOT NULL,
CODE VARCHAR2(30 CHAR) NOT NULL,
SORT NUMBER(18) DEFAULT '100',
PRIMARY KEY (ID)
)", // OK
));
$updater->query(array(
"MSSQL" => "ALTER TABLE B_SALE_LOC_TYPE ADD CONSTRAINT DF_B_SALE_LOC_TYPE_SORT DEFAULT '100' FOR SORT", // OK
));
$locationTypeTableExists = true;
}
if($DBType == 'oracle' && $locationTypeTableExists && !($DB->query("select * from USER_OBJECTS where OBJECT_TYPE = 'SEQUENCE' and OBJECT_NAME = 'SQ_B_SALE_LOC_TYPE'", true)->fetch()))
{
$DB->query("CREATE SEQUENCE SQ_B_SALE_LOC_TYPE INCREMENT BY 1 NOMAXVALUE NOCYCLE NOCACHE NOORDER"); // OK
$DB->query("CREATE OR REPLACE TRIGGER B_SALE_LOC_TYPE_INSERT
BEFORE INSERT
ON B_SALE_LOC_TYPE
FOR EACH ROW
BEGIN
IF :NEW.ID IS NULL THEN
SELECT SQ_B_SALE_LOC_TYPE.NEXTVAL INTO :NEW.ID FROM dual;
END IF;
END;"); // OK
}
if(!$locationTypeNameTableExists)
{
$updater->query(array(
"MySQL" => "create table b_sale_loc_type_name(
ID int not null auto_increment,
LANGUAGE_ID char(2) not null,
NAME varchar(100) not null,
TYPE_ID int not null,
primary key (ID)
)",
"MSSQL" => "CREATE TABLE B_SALE_LOC_TYPE_NAME(
ID int NOT NULL IDENTITY (1, 1),
LANGUAGE_ID char(2) NOT NULL,
NAME varchar(100) NOT NULL,
TYPE_ID int NOT NULL
CONSTRAINT PK_B_SALE_LOC_TYPE_NAME PRIMARY KEY (ID)
)", // OK
"Oracle" => "CREATE TABLE B_SALE_LOC_TYPE_NAME(
ID NUMBER(18) NOT NULL,
LANGUAGE_ID CHAR(2 CHAR) NOT NULL,
NAME VARCHAR2(100 CHAR) NOT NULL,
TYPE_ID NUMBER(18) NOT NULL,
PRIMARY KEY (ID)
)", // OK
));
$locationTypeNameTableExists = true;
}
if($DBType == 'oracle' && $locationTypeNameTableExists && !($DB->query("select * from USER_OBJECTS where OBJECT_TYPE = 'SEQUENCE' and OBJECT_NAME = 'SQ_B_SALE_LOC_TYPE_NAME'", true)->fetch()))
{
$DB->query("CREATE SEQUENCE SQ_B_SALE_LOC_TYPE_NAME INCREMENT BY 1 NOMAXVALUE NOCYCLE NOCACHE NOORDER"); // OK
$DB->query("CREATE OR REPLACE TRIGGER B_SALE_LOC_TYPE_NAME_INSERT
BEFORE INSERT
ON B_SALE_LOC_TYPE_NAME
FOR EACH ROW
BEGIN
IF :NEW.ID IS NULL THEN
SELECT SQ_B_SALE_LOC_TYPE_NAME.NEXTVAL INTO :NEW.ID FROM dual;
END IF;
END;"); // OK
}
if ($locationTypeNameTableExists)
{
if (!$DB->IndexExists('b_sale_loc_type_name', array('TYPE_ID', 'LANGUAGE_ID')))
{
$DB->query('CREATE INDEX IX_B_SALE_LOC_TYPE_NAME_TI_LI ON b_sale_loc_type_name (TYPE_ID, LANGUAGE_ID)'); // OK: oracle, mssql
}
}
if (!$locationLoc2SiteTableExists)
{
$updater->query(array(
"MySQL" => "create table b_sale_loc_2site(
LOCATION_ID int not null,
SITE_ID char(2) not null,
LOCATION_TYPE char(1) not null default 'L',
primary key (SITE_ID, LOCATION_ID, LOCATION_TYPE)
)",
"MSSQL" => "CREATE TABLE B_SALE_LOC_2SITE(
LOCATION_ID int NOT NULL,
SITE_ID char(2) NOT NULL,
LOCATION_TYPE char(1) NOT NULL
CONSTRAINT PK_B_SALE_LOC_2SITE PRIMARY KEY (SITE_ID, LOCATION_ID, LOCATION_TYPE)
)", // OK
"Oracle" => "CREATE TABLE B_SALE_LOC_2SITE(
LOCATION_ID NUMBER(18) NOT NULL,
SITE_ID CHAR(2 CHAR) NOT NULL,
LOCATION_TYPE CHAR(1 CHAR) DEFAULT 'L' NOT NULL,
PRIMARY KEY (SITE_ID, LOCATION_ID, LOCATION_TYPE)
)", // OK
));
$updater->query(array(
"MSSQL" => "ALTER TABLE B_SALE_LOC_2SITE ADD CONSTRAINT DF_B_SALE_LOC_2SITE DEFAULT 'L' FOR LOCATION_TYPE", // OK
));
}
if (!$locationDefaul2SiteTableExists)
{
$updater->query(array(
"MySQL" => "create table b_sale_loc_def2site(
LOCATION_CODE varchar(100) not null,
SITE_ID char(2) not null,
SORT int default '100',
primary key (LOCATION_CODE, SITE_ID)
)",
"MSSQL" => "CREATE TABLE B_SALE_LOC_DEF2SITE(
LOCATION_CODE varchar(100) NOT NULL,
SITE_ID char(2) NOT NULL,
SORT int
CONSTRAINT PK_B_SALE_LOC_DEF2SITE PRIMARY KEY (LOCATION_CODE, SITE_ID)
)", // OK
"Oracle" => "CREATE TABLE B_SALE_LOC_DEF2SITE(
LOCATION_CODE VARCHAR2(100 CHAR) NOT NULL,
SITE_ID CHAR(2 CHAR) NOT NULL,
SORT NUMBER(18) DEFAULT '100',
PRIMARY KEY (LOCATION_CODE, SITE_ID)
)", // OK
));
$updater->query(array(
"MSSQL" => "ALTER TABLE B_SALE_LOC_DEF2SITE ADD CONSTRAINT DF_B_SALE_LOC_DEF2SITE_SORT DEFAULT '100' FOR SORT",
));
}
// move tax and delivery to the new relation field: code
if ($tax2LocationTableExists && $DB->query("select LOCATION_ID from b_sale_tax2location WHERE 1=0", true)) // OK: oracle, mssql
{
$DB->query('delete from b_sale_tax2location where LOCATION_ID is null'); // OK: oracle, mssql // useless records to be deleted
if (!$DB->query("select LOCATION_CODE from b_sale_tax2location WHERE 1=0", true))
{
$updater->query(array(
"MySQL" => "ALTER TABLE b_sale_tax2location ADD LOCATION_CODE varchar(100) NOT NULL",
"MSSQL" => "ALTER TABLE B_SALE_TAX2LOCATION ADD LOCATION_CODE varchar(100) default '' NOT NULL",
"Oracle" => "ALTER TABLE B_SALE_TAX2LOCATION ADD LOCATION_CODE VARCHAR2(100 CHAR) default '' NOT NULL", // OK // oracle allows to add not-null column only with default specified
));
}
$DB->query('update b_sale_tax2location set LOCATION_CODE = LOCATION_ID'); // OK: oracle, mssql
if($DBType == 'mssql')
$DB->query('ALTER TABLE b_sale_tax2location DROP CONSTRAINT PK_B_SALE_TAX2LOCATION'); // OK
else
$DB->query('ALTER TABLE b_sale_tax2location DROP PRIMARY KEY'); // OK: oracle
$DB->query('ALTER TABLE b_sale_tax2location DROP COLUMN LOCATION_ID'); // OK: oracle, mssql
$DB->query('ALTER TABLE b_sale_tax2location ADD CONSTRAINT PK_B_SALE_TAX2LOCATION PRIMARY KEY (TAX_RATE_ID, LOCATION_CODE, LOCATION_TYPE)'); // OK: oracle, mssql
}
if ($delivery2LocationTableExists && $DB->query("select LOCATION_ID from b_sale_delivery2location WHERE 1=0", true)) // OK: oracle
{
$DB->query('delete from b_sale_delivery2location where LOCATION_ID is null'); // OK: oracle, mssql // useless records to be deleted
if (!$DB->query("select LOCATION_CODE from b_sale_delivery2location WHERE 1=0", true))
{
$updater->query(array(
"MySQL" => "ALTER TABLE b_sale_delivery2location ADD LOCATION_CODE varchar(100) NOT NULL",
"MSSQL" => "ALTER TABLE B_SALE_DELIVERY2LOCATION ADD LOCATION_CODE varchar(100) default '' NOT NULL", // OK
"Oracle" => "ALTER TABLE B_SALE_DELIVERY2LOCATION ADD LOCATION_CODE VARCHAR2(100 CHAR) default '' NOT NULL", // OK // oracle allows to add not-null column only with default specified
));
}
$DB->query('update b_sale_delivery2location set LOCATION_CODE = LOCATION_ID'); // OK: oracle, mssql
if($DBType == 'mssql')
$DB->query('ALTER TABLE b_sale_delivery2location DROP CONSTRAINT PK_B_SALE_DELIVERY2LOCATION'); // OK
else
$DB->query('ALTER TABLE b_sale_delivery2location DROP PRIMARY KEY'); // OK: oracle
$DB->query('ALTER TABLE b_sale_delivery2location DROP COLUMN LOCATION_ID'); // OK: oracle, mssql
$DB->query('ALTER TABLE b_sale_delivery2location ADD CONSTRAINT PK_B_SALE_DELIVERY2LOCATION PRIMARY KEY (DELIVERY_ID, LOCATION_CODE, LOCATION_TYPE)'); // OK: oracle, mssql
}
if(\COption::GetOptionString('sale', 'sale_locationpro_migrated', '') != 'Y') // CSaleLocation::isLocationProMigrated()
{
\CAdminNotify::Add(
array(
"MESSAGE" => Loc::getMessage('SALE_LOCATION_MIGRATION_PLZ_MIGRATE_NOTIFIER', array(
'#ANCHOR_MIGRATE#' => '<a href="/bitrix/admin/sale_location_migration.php">',
'#ANCHOR_END#' => '</a>'
)),
"TAG" => "SALE_LOCATIONPRO_PLZ_MIGRATE",
"MODULE_ID" => "SALE",
"ENABLE_CLOSE" => "Y"
)
);
}
}
}
////////////////////////////////////////////////////////
//// Migration-specific
////////////////////////////////////////////////////////
public function copyId2Code()
{
// in locations
$this->Query(array(
self::DB_TYPE_MYSQL => 'update '.self::TABLE_LOCATION.' set CODE = ID;',
self::DB_TYPE_MSSQL => 'update '.strtoupper(self::TABLE_LOCATION).' set CODE = ID;',
self::DB_TYPE_ORACLE => 'update '.strtoupper(self::TABLE_LOCATION).' set CODE = ID;'
));
// in groups
$this->Query(array(
self::DB_TYPE_MYSQL => 'update '.self::TABLE_LOCATION_GROUP.' set CODE = ID;',
self::DB_TYPE_MSSQL => 'update '.strtoupper(self::TABLE_LOCATION_GROUP).' set CODE = ID;',
self::DB_TYPE_ORACLE => 'update '.strtoupper(self::TABLE_LOCATION_GROUP).' set CODE = ID;'
));
}
public function copyZipCodes()
{
global $DB;
Helper::truncateTable(self::TABLE_LOCATION_EXTERNAL);
$zipServiceId = false;
$zip = Location\ExternalServiceTable::getList(array('filter' => array('=CODE' => 'ZIP')))->fetch();
if(intval($zip['ID']))
$zipServiceId = intval($zip['ID']);
if($zipServiceId === false)
{
$res = Location\ExternalServiceTable::add(array('CODE' => 'ZIP'));
if(!$res->isSuccess())
throw new Main\SystemException('Cannot add external system: '.implode(', ', $res->getErrors()), 0, __FILE__, __LINE__);
$zipServiceId = $res->getId();
}
if($this->TableExists(self::TABLE_LOCATION_ZIP))
{
$loc2External = new BlockInserter(array(
'entityName' => '\Bitrix\Sale\Location\ExternalTable',
'exactFields' => array('LOCATION_ID', 'XML_ID', 'SERVICE_ID'),
'parameters' => array(
//'autoIncrementFld' => 'ID',
'mtu' => 9999
)
));
$res = $DB->query('select * from '.self::TABLE_LOCATION_ZIP);
while($item = $res->fetch())
{
$item['LOCATION_ID'] = trim($item['LOCATION_ID']);
$item['ZIP'] = trim($item['ZIP']);
if(strlen($item['LOCATION_ID']) && strlen($item['ZIP']))
{
$loc2External->insert(array(
'LOCATION_ID' => $item['LOCATION_ID'],
'XML_ID' => $item['ZIP'],
'SERVICE_ID' => $zipServiceId
));
}
}
$loc2External->flush();
}
}
private function convertEntityLocationLinks($entityName)
{
/** @var \Bitrix\Sale\Location\Connector $class */
$class = $entityName.'Table';
$typeField = $class::getTypeField();
$locationLinkField = $class::getLocationLinkField();
$linkField = $class::getLinkField();
$useGroups = $class::getUseGroups();
$res = $class::getList();
$links = array();
while($item = $res->fetch())
{
if($useGroups)
$links[$item[$linkField]][$item[$typeField]][] = $item[$locationLinkField];
else
$links[$item[$linkField]][$class::DB_LOCATION_FLAG][] = $item[$locationLinkField];
}
foreach($links as $entityId => $rels)
{
if(is_array($rels[$class::DB_LOCATION_FLAG]))
$rels[$class::DB_LOCATION_FLAG] = $class::normalizeLocationList($rels[$class::DB_LOCATION_FLAG]);
if(isset($rels[$class::DB_LOCATION_FLAG]) && (!is_array($rels[$class::DB_LOCATION_FLAG]) || empty($rels[$class::DB_LOCATION_FLAG])))
unset($rels[$class::DB_LOCATION_FLAG]);
if(isset($rels[$class::DB_GROUP_FLAG]) && (!is_array($rels[$class::DB_GROUP_FLAG]) || empty($rels[$class::DB_GROUP_FLAG])))
unset($rels[$class::DB_GROUP_FLAG]);
$class::resetMultipleForOwner($entityId, $rels);
}
}
public function convertGroupLocationLinks()
{
$this->convertEntityLocationLinks('\Bitrix\Sale\Location\GroupLocation');
}
public function convertDeliveryLocationLinks()
{
$this->convertEntityLocationLinks('\Bitrix\Sale\Delivery\DeliveryLocation');
}
public function convertTaxRateLocationLinks()
{
$this->convertEntityLocationLinks('\Bitrix\Sale\Tax\RateLocation');
}
public function convertSalesZones()
{
$siteList = \CSaleLocation::getSites();
$siteList[] = ''; // 'empty site' too
foreach($siteList as $siteId)
{
$countries = Sale\SalesZone::getCountriesIds($siteId);
$regions = Sale\SalesZone::getRegionsIds($siteId);
$cities = Sale\SalesZone::getCitiesIds($siteId);
if(empty($countries) && empty($regions) && empty($cities))
continue;
Sale\SalesZone::saveSelectedTypes(array(
'COUNTRY' => $countries,
'REGION' => $regions,
'CITY' => $cities
), $siteId);
}
}
public function copyDefaultLocations()
{
$sRes = Main\SiteTable::getList();
$sites = array();
while($site = $sRes->fetch())
$sites[] = $site['LID'];
$existed = array();
$res = Location\DefaultSiteTable::getList();
while($item = $res->fetch())
$existed[$item['SITE_ID']][$item['LOCATION_CODE']] = true;
$res = \CSaleLocation::GetList(array(), array(
'LID' => 'en',
'LOC_DEFAULT' => 'Y'
), false, false, array('ID'));
while($item = $res->fetch())
{
foreach($sites as $site)
{
if(isset($existed[$site][$item['ID']]))
continue;
$opRes = Location\DefaultSiteTable::add(array(
'SITE_ID' => $site,
'LOCATION_CODE' => $item['ID']
));
if(!$opRes->isSuccess())
throw new Main\SystemException('Cannot add default location');
}
}
}
public static function createBaseTypes()
{
$types = array(
'COUNTRY' => array(
'CODE' => 'COUNTRY',
'SORT' => 100,
'DISPLAY_SORT' => 700,
'NAME' => array()
),
'REGION' => array(
'CODE' => 'REGION',
'SORT' => 300,
'DISPLAY_SORT' => 500,
'NAME' => array()
),
'CITY' => array(
'CODE' => 'CITY',
'SORT' => 600,
'DISPLAY_SORT' => 100,
'NAME' => array()
),
);
$langs = array();
$res = \Bitrix\Main\Localization\LanguageTable::getList();
while($item = $res->Fetch())
{
$MESS = array();
@include($_SERVER['DOCUMENT_ROOT'].'/bitrix/modules/sale/lang/'.$item['LID'].'/lib/location/migration/migrate.php');
if(!empty($MESS))
{
$types['COUNTRY']['NAME'][$item['LID']]['NAME'] = $MESS['SALE_LOCATION_TYPE_COUNTRY'];
$types['REGION']['NAME'][$item['LID']]['NAME'] = $MESS['SALE_LOCATION_TYPE_REGION'];
$types['CITY']['NAME'][$item['LID']]['NAME'] = $MESS['SALE_LOCATION_TYPE_CITY'];
}
$langs[$item['LID']] = true;
}
$typeCode2Id = array();
$res = Location\TypeTable::getList(array('select' => array('ID', 'CODE')));
while($item = $res->Fetch())
$typeCode2Id[$item['CODE']] = $item['ID'];
foreach($types as $code => &$type)
{
foreach($langs as $lid => $f)
{
$type['NAME'][$lid] = \Bitrix\Sale\Location\Admin\NameHelper::getTranslatedName($type['NAME'], $lid);
}
if(!isset($typeCode2Id[$type['CODE']]))
{
$typeCode2Id[$type['CODE']] = Location\TypeTable::add($type);
}
else
{
// ensure it has all appropriate translations
// we can not use ::updateMultipleForOwner() here, because user may rename types manually
Location\Name\TypeTable::addAbsentForOwner($typeCode2Id[$type['CODE']], $type['NAME']);
}
}
return $typeCode2Id;
}
public function createTypes()
{
$this->data['TYPE'] = self::createBaseTypes();
}
public function convertTree()
{
$res = Location\Name\LocationTable::getList(array('select' => array('ID'), 'limit' => 1))->fetch();
if(!$res['ID']) // if we got smth in name table - this means we already have done this conversion in the past
{
$this->grabTree();
$this->convertCountries();
$this->convertRegions();
$this->convertCities();
$this->resort();
$this->insertTreeInfo();
$this->insertNames();
}
}
public function resetLegacyPath()
{
Helper::dropTable(self::TABLE_LEGACY_RELATIONS);
$dbConnection = \Bitrix\Main\HttpApplication::getConnection();
$dbConnection->query("create table ".self::TABLE_LEGACY_RELATIONS." (
ID ".Helper::getSqlForDataType('int').",
COUNTRY_ID ".Helper::getSqlForDataType('int').",
REGION_ID ".Helper::getSqlForDataType('int').",
CITY_ID ".Helper::getSqlForDataType('int')."
)");
$dbConnection->query("insert into ".self::TABLE_LEGACY_RELATIONS." (ID, COUNTRY_ID, REGION_ID, CITY_ID) select ID, COUNTRY_ID, REGION_ID, CITY_ID from b_sale_location");
Location\LocationTable::resetLegacyPath();
}
public function rollBack()
{
if(Helper::checkTableExists(self::TABLE_LEGACY_RELATIONS))
{
Helper::mergeTables(
'b_sale_location',
self::TABLE_LEGACY_RELATIONS,
array(
'COUNTRY_ID' => 'COUNTRY_ID',
'REGION_ID' => 'REGION_ID',
'CITY_ID' => 'CITY_ID',
),
array('ID' => 'ID')
);
}
Helper::truncateTable(self::TABLE_LOCATION_NAME);
Helper::truncateTable(self::TABLE_LOCATION_EXTERNAL);
\CSaleLocation::locationProSetRolledBack();
}
// in this function we track dependences between countries, regions and cities
private function grabTree()
{
$this->data['LOC'] = array();
$auxIndex = array(
'COUNTRY' => array(),
'REGION' => array(),
'CITY' => array()
);
$this->data['LOC'] = array(
'COUNTRY' => array(),
'REGION' => array(),
'CITY' => array()
);
// level 1: country
$res = \CSaleLocation::GetList(array(), array(
'!COUNTRY_ID' => false,
'REGION_ID' => false,
'CITY_ID' => false,
'LID' => 'en'
));
while($item = $res->Fetch())
{
if(!isset($this->data['LOC']['COUNTRY'][$item['ID']]))
{
$this->data['LOC']['COUNTRY'][$item['ID']] = array(
'SUBJ_ID' => $item['COUNTRY_ID'],
'PARENT_ID' => false,
'PARENT_TYPE' => false
);
$auxIndex['COUNTRY'][$item['COUNTRY_ID']] = $item['ID'];
}
}
// level 2: country - region
$res = \CSaleLocation::GetList(array(), array(
//'!COUNTRY_ID' => false,
'!REGION_ID' => false,
'CITY_ID' => false,
'LID' => 'en'
));
while($item = $res->Fetch())
{
if(!isset($this->data['LOC']['REGION'][$item['ID']]))
{
$this->data['LOC']['REGION'][$item['ID']] = array(
'SUBJ_ID' => $item['REGION_ID'],
'PARENT_ID' => $auxIndex['COUNTRY'][$item['COUNTRY_ID']],
'PARENT_TYPE' => 'COUNTRY'
);
$auxIndex['REGION'][$item['REGION_ID']] = $item['ID'];
}
}
// level 2: country - city
$res = \CSaleLocation::GetList(array(), array(
//'!COUNTRY_ID' => false,
'REGION_ID' => false,
'!CITY_ID' => false,
'LID' => 'en'
));
while($item = $res->Fetch())
{
if(!isset($this->data['LOC']['CITY'][$item['ID']]))
$this->data['LOC']['CITY'][$item['ID']] = array(
'SUBJ_ID' => $item['CITY_ID'],
'PARENT_ID' => $auxIndex['COUNTRY'][$item['COUNTRY_ID']],
'PARENT_TYPE' => 'COUNTRY'
);
}
// level 3: country - region - city
$res = \CSaleLocation::GetList(array(), array(
//'!COUNTRY_ID' => false,
'!REGION_ID' => false,
'!CITY_ID' => false,
'LID' => 'en'
));
while($item = $res->Fetch())
{
if(!isset($this->data['LOC']['CITY'][$item['ID']]))
$this->data['LOC']['CITY'][$item['ID']] = array(
'SUBJ_ID' => $item['CITY_ID'],
'PARENT_ID' => $auxIndex['REGION'][$item['REGION_ID']],
'PARENT_TYPE' => 'REGION'
);
}
// language list
$a = false;
$b = false;
$lang = new \CLanguage();
$res = $lang->GetList($a, $b);
$this->data['LANG'] = array();
while($item = $res->Fetch())
$this->data['LANG'][] = $item['LID'];
// type list
$res = Location\TypeTable::getList();
while($item = $res->Fetch())
$this->data['TYPE'][$item['CODE']] = $item['ID'];
}
private function convertCountries()
{
global $DB;
// fetch name referece, separated with lang
$langIndex = array();
$res = $DB->query('select * from '.self::TABLE_LOCATION_COUNTRY_NAME);
while($item = $res->Fetch())
{
$langIndex[$item['COUNTRY_ID']][$item['LID']] = array(
'NAME' => $item['NAME'],
'SHORT_NAME' => $item['SHORT_NAME']
);
}
if(is_array($this->data['LOC']['COUNTRY']))
{
foreach($this->data['LOC']['COUNTRY'] as $id => &$item)
{
$this->data['NAME'][$id] = $langIndex[$item['SUBJ_ID']];
$this->data['TREE'][$id] = array(
'PARENT_ID' => false,
'TYPE_ID' => $this->data['TYPE']['COUNTRY'],
'DEPTH_LEVEL' => 1
);
}
}
unset($this->data['LOC']['COUNTRY']);
}
private function convertRegions()
{
global $DB;
// fetch name referece, separated with lang
$langIndex = array();
$res = $DB->query('select * from '.self::TABLE_LOCATION_REGION_NAME);
while($item = $res->Fetch())
{
$langIndex[$item['REGION_ID']][$item['LID']] = array(
'NAME' => $item['NAME'],
'SHORT_NAME' => $item['SHORT_NAME']
);
}
if(is_array($this->data['LOC']['REGION']))
{
foreach($this->data['LOC']['REGION'] as $id => &$item)
{
$this->data['NAME'][$id] = $langIndex[$item['SUBJ_ID']];
$this->data['TREE'][$id] = array(
'PARENT_ID' => $item['PARENT_ID'],
'TYPE_ID' => $this->data['TYPE']['REGION'],
'DEPTH_LEVEL' => 2
);
}
}
unset($this->data['LOC']['REGION']);
}
private function convertCities()
{
global $DB;
// fetch name referece, separated with lang
$langIndex = array();
$res = $DB->query('select * from '.self::TABLE_LOCATION_CITY_NAME);
while($item = $res->Fetch())
{
$langIndex[$item['CITY_ID']][$item['LID']] = array(
'NAME' => $item['NAME'],
'SHORT_NAME' => $item['SHORT_NAME']
);
}
if(is_array($this->data['LOC']['CITY']))
{
foreach($this->data['LOC']['CITY'] as $id => &$item)
{
$this->data['NAME'][$id] = $langIndex[$item['SUBJ_ID']];
$this->data['TREE'][$id] = array(
'PARENT_ID' => $item['PARENT_ID'],
'TYPE_ID' => $this->data['TYPE']['CITY'],
'DEPTH_LEVEL' => $item['PARENT_TYPE'] == 'REGION' ? 3 : 2
);
}
}
unset($this->data['LOC']['CITY']);
}
private function resort()
{
$edges = array();
$nodes = array();
if(is_array($this->data['TREE']))
{
foreach($this->data['TREE'] as $id => $item)
{
$nodes[$id] = array();
if(!intval($item['PARENT_ID']))
$edges['ROOT'][] = $id;
else
$edges[$item['PARENT_ID']][] = $id;
}
}
$this->walkTreeInDeep('ROOT', $edges, $nodes, 0);
}
private function walkTreeInDeep($nodeId, $edges, &$nodes, $margin, $depth = 0)
{
$lMargin = $margin;
if(empty($edges[$nodeId]))
$rMargin = $margin + 1;
else
{
$offset = $margin + 1;
foreach($edges[$nodeId] as $sNode)
$offset = $this->walkTreeInDeep($sNode, $edges, $nodes, $offset, $depth + 1);
$rMargin = $offset;
}
if($nodeId != 'ROOT')
{
// store margins
$this->data['TREE'][$nodeId]['LEFT_MARGIN'] = $lMargin;
$this->data['TREE'][$nodeId]['RIGHT_MARGIN'] = $rMargin;
$this->data['TREE'][$nodeId]['DEPTH_LEVEL'] = $depth;
}
return $rMargin + 1;
}
private function insertTreeInfo()
{
// We make temporal table, place margins, parent and lang data into it, then perform an update of the old table from the temporal one.
$this->createTemporalTable(
self::TABLE_TEMP_TREE,
array(
'ID' => array(
'TYPE' => array(
self::DB_TYPE_MYSQL => 'int',
self::DB_TYPE_MSSQL => 'int',
self::DB_TYPE_ORACLE => 'NUMBER(18)',
)
),
'PARENT_ID' => array(
'TYPE' => array(
self::DB_TYPE_MYSQL => 'int',
self::DB_TYPE_MSSQL => 'int',
self::DB_TYPE_ORACLE => 'NUMBER(18)',
)
),
'TYPE_ID' => array(
'TYPE' => array(
self::DB_TYPE_MYSQL => 'int',
self::DB_TYPE_MSSQL => 'int',
self::DB_TYPE_ORACLE => 'NUMBER(18)',
)
),
'DEPTH_LEVEL' => array(
'TYPE' => array(
self::DB_TYPE_MYSQL => 'int',
self::DB_TYPE_MSSQL => 'int',
self::DB_TYPE_ORACLE => 'NUMBER(18)',
)
),
'LEFT_MARGIN' => array(
'TYPE' => array(
self::DB_TYPE_MYSQL => 'int',
self::DB_TYPE_MSSQL => 'int',
self::DB_TYPE_ORACLE => 'NUMBER(18)',
)
),
'RIGHT_MARGIN' => array(
'TYPE' => array(
self::DB_TYPE_MYSQL => 'int',
self::DB_TYPE_MSSQL => 'int',
self::DB_TYPE_ORACLE => 'NUMBER(18)',
)
)
)
);
$handle = new BlockInserter(array(
'tableName' => self::TABLE_TEMP_TREE,
'exactFields' => array(
'ID' => array('data_type' => 'integer'),
'PARENT_ID' => array('data_type' => 'integer'),
'TYPE_ID' => array('data_type' => 'integer'),
'DEPTH_LEVEL' => array('data_type' => 'integer'),
'LEFT_MARGIN' => array('data_type' => 'integer'),
'RIGHT_MARGIN' => array('data_type' => 'integer'),
),
'parameters' => array(
'mtu' => 9999
)
));
// fill temporal table
if(is_array($this->data['TREE']))
{
foreach($this->data['TREE'] as $id => $node)
{
$handle->insert(array(
'ID' => $id,
'PARENT_ID' => $node['PARENT_ID'],
'TYPE_ID' => $node['TYPE_ID'],
'DEPTH_LEVEL' => $node['DEPTH_LEVEL'],
'LEFT_MARGIN' => $node['LEFT_MARGIN'],
'RIGHT_MARGIN' => $node['RIGHT_MARGIN'],
));
}
}
$handle->flush();
// merge temp table with location table
Location\LocationTable::mergeRelationsFromTemporalTable(self::TABLE_TEMP_TREE, array('TYPE_ID', 'PARENT_ID'));
$this->dropTable(self::TABLE_TEMP_TREE);
}
private function insertNames()
{
$handle = new BlockInserter(array(
'entityName' => '\Bitrix\Sale\Location\Name\LocationTable',
'exactFields' => array('LOCATION_ID', 'LANGUAGE_ID', 'NAME', 'SHORT_NAME', 'NAME_UPPER'),
'parameters' => array(
//'autoIncrementFld' => 'ID',
'mtu' => 9999
)
));
if(is_array($this->data['NAME']) && !empty($this->data['NAME']))
{
foreach($this->data['NAME'] as $id => $nameLang)
{
if(is_array($nameLang))
{
foreach($nameLang as $lang => $name)
{
$handle->insert(array(
'LOCATION_ID' => $id,
'LANGUAGE_ID' => $lang,
'NAME' => $name['NAME'],
'NAME_UPPER' => ToUpper($name['NAME']),
'SHORT_NAME' => $name['SHORT_NAME']
));
}
}
}
}
$handle->flush();
}
////////////////////////////////////////////////////////
//// Common-specific logic => add to CUpdater ???
////////////////////////////////////////////////////////
protected function dropTable($tableName = '')
{
if(!strlen($tableName))
return false;
global $DB;
if($this->TableExists($tableName))
$DB->query('drop table '.$DB->ForSql($tableName));
return true;
}
protected function createTemporalTable($tableName = '', $columns = array())
{
if(!strlen($tableName))
return false;
if($this->dropTable($tableName));
return $this->createTable($tableName, $columns);
}
protected function createTable($tableName = '', $columns = array(), $constraints = array())
{
if(!strlen($tableName) || !is_array($columns) || empty($columns) || $this->TableExists($tableName))
return false;
global $DB;
$tableName = $DB->ForSql($tableName);
$tableNameUC = strtoupper($tableName);
// queries that should be called after table creation
$afterTableCreate = array();
// column sqls separated by dbtype
$columnsSql = array();
foreach($columns as $colName => $colProps)
if($col = self::prepareFieldSql($colProps, $afterTableCreate))
$columnsSql[$colName] = $col;
// constraint sqls separated by dbtype
$constSql = self::prepareConstraintSql($constraints);
$queries = array();
if($sql = self::prepareCreateTable($tableName, $columnsSql, $constSql, self::DB_TYPE_MYSQL))
$queries[self::DB_TYPE_MYSQL] = $sql;
if($sql = self::prepareCreateTable($tableNameUC, $columnsSql, $constSql, self::DB_TYPE_MSSQL))
$queries[self::DB_TYPE_MSSQL] = $sql;
if($sql = self::prepareCreateTable($tableNameUC, $columnsSql, $constSql, self::DB_TYPE_ORACLE))
$queries[self::DB_TYPE_ORACLE] = $sql;
if(!empty($queries))
$this->Query($queries);
foreach($afterTableCreate as $dbType => $queries)
{
foreach($queries as $query)
{
$this->Query(array(
$dbType => str_replace('%TABLE_NAME%', self::DB_TYPE_MYSQL == $dbType ? $tableName : $tableNameUC, $query)
));
}
}
return true;
}
protected function prepareCreateTable($tableName, $columnsSql, $constSql, $dbType)
{
$columnsSqlSpec = $this->prepareTableFields($columnsSql, $dbType);
if(!empty($columnsSqlSpec))
return 'create table '.$tableName.' ('.$columnsSqlSpec.(!empty($constSql[$dbType]) ? ', '.implode(', ', $constSql[$dbType]) : '').')';
return false;
}
// might be some overhead
protected function prepareConstraintSql($constraints)
{
global $DB;
$cSql = array();
foreach($constraints as $cCode => $cVal)
{
if($cCode == 'PRIMARY')
{
if(is_array($cVal) || !empty($cVal))
{
foreach($cVal as &$fld)
$fld = $DB->ForSql($fld);
$key = implode(', ', $cVal);
}
else
$key = $DB->ForSql($cVal);
$pk = 'PRIMARY KEY ('.$key.')';
$cSql[self::DB_TYPE_MYSQL][] = $pk;
$cSql[self::DB_TYPE_MSSQL][] = $pk;
$cSql[self::DB_TYPE_ORACLE][] = $pk;
}
}
return $cSql;
}
protected function prepareTableFields($columnsSql, $dbType)
{
$resSql = array();
foreach($columnsSql as $colName => $sqls)
if(isset($sqls[$dbType]))
$resSql[] = $colName.' '.$sqls[$dbType];
return implode(', ', $resSql);
}
protected function prepareFieldSql($field, &$afterCreate)
{
$prepared = array();
global $DB;
foreach($field['TYPE'] as $dbType => $fldType)
{
$prepared[$dbType] = $fldType;
if($field['PRIMARY'])
$prepared[$dbType] .= ' primary key';
if($field['AUTO_INCREMENT'])
{
if($dbType == self::DB_TYPE_MYSQL)
$prepared[$dbType] .= ' auto_increment';
if($dbType == self::DB_TYPE_MSSQL)
$prepared[$dbType] .= ' IDENTITY (1, 1)';
if($dbType == self::DB_TYPE_ORACLE)
{
// create a sequence
$afterCreate[self::DB_TYPE_ORACLE][] = 'CREATE SEQUENCE SQ_B_%TABLE_NAME%';
// then create a trigger that uses the sequence
$afterCreate[self::DB_TYPE_ORACLE][] = 'CREATE OR REPLACE TRIGGER %TABLE_NAME%_insert
BEFORE INSERT
ON %TABLE_NAME%
FOR EACH ROW
BEGIN
IF :NEW.ID IS NULL THEN
SELECT SQ_%TABLE_NAME%.NEXTVAL INTO :NEW.ID FROM dual;
END IF;
END;';
}
}
if(isset($field['DEFAULT']))
$prepared[$dbType] .= ' DEFAULT '.(empty($field['DEFAULT']) ? 'NULL' : "'".$DB->ForSql($field['DEFAULT'])."'");
if(isset($field['NULL']))
$prepared[$dbType] .= ' '.($field['NULL'] ? '' : 'NOT ').'NULL';
}
return $prepared;
}
public function TableExists($tableName)
{
if (!in_array("DATABASE", $this->callType))
return False;
$tableName = preg_replace("/[^A-Za-z0-9%_]+/i", "", $tableName);
$tableName = Trim($tableName);
if (strlen($tableName) <= 0)
return False;
global $DB;
if($this->UsingMysql())
{
return $DB->query('select * from '.$DB->ForSql($tableName).' where 1=0', true);
}
else
{
$strSql = '';
if($this->UsingOracle())
$strSql = "SELECT TABLE_NAME FROM USER_TABLES WHERE TABLE_NAME LIKE UPPER('".strtoupper($DB->ForSql($tableName))."')";
elseif($this->UsingMssql())
$strSql = "SELECT TABLE_NAME FROM INFORMATION_SCHEMA.TABLES WHERE TABLE_NAME LIKE '".strtoupper($DB->ForSql($tableName))."'";
return !!$DB->Query($strSql)->fetch();
}
}
protected function UsingMysql()
{
return $this->dbType == 'MYSQL';
}
protected function UsingMssql()
{
return $this->dbType == 'MSSQL';
}
protected function UsingOracle()
{
return $this->dbType == 'ORACLE';
}
}