Перейти к содержимому

Перенос заказов и клиентов со старого сайта - порядок и сверка

Переносим клиентов и заказы со старого сайта так, чтобы покупатель нашёл свою историю, а бухгалтерия сошлась с прежними числами.

Механика

Переносят со старого сайта далеко не всё подряд. История заказов за десять лет чаще всего никому не нужна: покупателю интересны последние год-два, бухгалтерии - закрытый период, а маркетингу - активные клиенты. Решение о глубине переноса принимает магазин, и принимает его до разработки, потому что от него зависит и время работы, и объём базы.

Главный механизм всего переноса - это внешние коды старой системы. У каждого клиента и каждого заказа остаётся поле с идентификатором из старой системы. По нему связывают заказы с клиентами, повторный запуск не создаёт дублей, а спорные случаи разбираются сверкой, а не догадками.

Порядок шагов переноса жёсткий и менять его не стоит. Сначала клиенты, потом заказы, потом их состав и свойства: заказ без клиента создать можно, но связать потом сложнее, чем сделать сразу правильно. Каждый шаг должен переживать повторный запуск: перенос почти никогда не проходит с первого раза.

Часть данных не переносится вовсе по самому своему устройству. Пароли клиентов - это отпечатки, пригодные только для старой системы; история изменений заказа, чеки и платежи живут в своих подсистемах и переносятся отдельно, если переносятся вообще.

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

Перенос почти всегда делают дважды: сначала пробный на копии, чтобы измерить время и найти неожиданности, потом боевой в час, согласованный с магазином. Между ними проходит неделя, за которую в старой системе появляются новые заказы, и поэтому боевой запуск обязан уметь донести только то, чего ещё нет.

Шаги

  1. Договориться о глубине переноса и о том, что именно из данных переносится.
  2. Завести поля для внешних кодов и у клиентов, и у самих заказов.
  3. Перенести клиентов порциями, отсекая дубли по почте и телефону.
  4. Перенести заказы вместе с составом, свойствами и сопоставленными статусами.
  5. Поправить даты и номера заказов, если старые значения важны самому магазину.
  6. Сверить числа, суммы и выборочные заказы до запуска сайта.

Код

Заводим внешние коды:

// у пользователя - пользовательское поле, у заказа - штатное поле связи
$order->setField('XML_ID', 'old-' . $row['order_id']);
$user = new \CUser();
$user->Update($userId, ['UF_OLD_ID' => $row['client_id']]); // поле сущности USER
// по этим значениям повторный запуск находит уже перенесённое

У каждой записи остаётся код из старой системы. Это единственный способ повторно запустить перенос, не удваивая данные, и единственный способ потом объяснить, что откуда взялось.

Переносим клиентов порциями:

foreach (array_chunk($clients, 200) as $chunk) {
foreach ($chunk as $row) {
$exists = CUser::GetList('ID', 'ASC', ['UF_OLD_ID' => $row['id']])->Fetch();
if ($exists) { continue; } // уже перенесён
$user = new CUser();
$user->Add(['EMAIL' => $row['email'], 'LOGIN' => $row['email'],
'NAME' => $row['name'], 'UF_OLD_ID' => $row['id'],
'PASSWORD' => randString(20), 'CONFIRM_PASSWORD' => null]);
}
}

Клиентов переносят порциями и с проверкой на уже перенесённых. Пароли при этом задают случайные: старые отпечатки не годятся, а покупатель восстановит пароль обычным письмом.

Переносим заказ с составом:

$order = \Bitrix\Sale\Order::create(SITE_ID, $userId);
$basket = \Bitrix\Sale\Basket::create(SITE_ID);
foreach ($row['items'] as $line) {
$item = $basket->createItem('catalog', $line['product_id']);
$item->setFields(['QUANTITY' => $line['qty'], 'CURRENCY' => 'RUB',
'PRICE' => $line['price'], 'CUSTOM_PRICE' => 'Y']); // цена как была тогда
}
$order->setBasket($basket);
$order->setField('XML_ID', 'old-' . $row['id']);
$order->save();

Цену позиции переносят как есть, а не пересчитывают по текущему каталогу. Товар мог подорожать, скидка - уйти, а заказ обязан остаться таким, каким его видел покупатель три года назад.

Сопоставляем статусы:

$map = ['new' => 'N', 'paid' => 'P', 'sent' => 'F', 'cancelled' => 'CANCELED'];
$status = $map[$row['status']] ?? 'N';
if (!isset($map[$row['status']])) {
AddMessage2Log('неизвестный статус: ' . $row['status'], 'vendor.shop');
}
// неизвестный статус не молчит, а попадает в журнал и в отчёт о переносе

Статусы старой системы почти никогда не совпадают с новыми. Таблицу соответствия согласуют с магазином, а всё, что в неё не попало, отправляют в журнал: молчаливое приведение к «новому» портит отчёты.

Правим даты после сохранения:

$c = \Bitrix\Main\Application::getConnection();
$c->queryExecute("UPDATE b_sale_order SET DATE_INSERT = '" . $c->getSqlHelper()->forSql($row['date'])
. "' WHERE XML_ID = '" . $c->getSqlHelper()->forSql('old-' . $row['id']) . "'");
// платформа ставит дату сама, поэтому старую возвращают отдельным шагом

Дату создания заказа платформа ставит сама, при его сохранении. Если старая дата важна для отчётов и для личного кабинета, её возвращают отдельным шагом после сохранения, а не пытаются подсунуть при создании.

Сверяем результат:

printf("клиентов: было %d, стало %d\n", $oldClients, $newClients);
printf("заказов: было %d, стало %d, сумма %s против %s\n",
$oldOrders, $newOrders, $oldSum, $newSum);
// расхождение в одну запись разбирают так же внимательно, как в тысячу

Числа и суммы сверяют до запуска, а не после жалоб. Расхождение в сумме почти всегда означает потерянные позиции или неверно перенесённую цену, и найти это позже будет некому.

Ограничения

Пароли клиентов со старого сайта не переносятся вовсе. Отпечатки старой системы платформе не подходят, поэтому всем перенесённым клиентам пароль восстанавливают письмом, и об этом покупателей предупреждают заранее, а не постфактум.

История изменений заказа так и остаётся в старой системе магазина целиком. Платформа умеет вести свою историю, но заполнить её задним числом нельзя, и вопрос «кто менял статус в прошлом году» отвечается только выгрузкой из старой базы.

Платежи и фискальные чеки живут при этом своей совсем отдельной жизнью. Перенесённый заказ можно пометить оплаченным, но фискальные документы за прошлые периоды переносить нельзя и не нужно: за них отвечает прежняя система.

Отдельного внимания требуют заказы, оформленные без регистрации. У них нет клиента, к которому можно привязать историю, и решение об этом принимают заранее: либо заводить учётные записи по контактам, либо переносить такие заказы без владельца и мириться с тем, что в личном кабинете они не появятся.

Персональные данные переносят ровно в том объёме, на который есть согласие. Полная выгрузка клиентской базы «на всякий случай» - это не техническое решение, и согласовывать его нужно не с разработчиком.

Типичные проблемы

После повторного запуска заказы задвоились.

Внешний код старой системы нигде не сохраняется и не проверяется. Именно по нему повторный запуск переноса и находит уже перенесённые записи.

Суммы заказов не сошлись со старой системой.

Цены пересчитаны по текущему каталогу вместо переноса как есть. В перенесённом заказе цену позиции фиксируют явным признаком.

Все заказы получили сегодняшнюю дату.

Дату создания ставит платформа, а старая нигде не восстановлена. Её возвращают отдельным шагом уже после сохранения заказа.

Часть заказов оказалась в статусе «новый».

Статусы старой системы не были сопоставлены и приведены к значению по умолчанию. Все несопоставленные значения статусов обязательно пишут в журнал переноса.

Клиенты не могут войти на новый сайт.

Ожидалось, что пароли переедут вместе с самими учётными записями этих клиентов. Пароли восстанавливают обычным письмом, и об этом предупреждают самих клиентов заранее.

Частые вопросы

Насколько глубоко переносить историю?

Обычно два-три года: дальше данными почти не пользуются. Решает это магазин, а не разработчик.

Переносить ли брошенные корзины?

Нет: они устареют раньше, чем понадобятся. Переносят заказы и клиентов.

Как быть с товарами, которых больше нет?

Заказ переносят с названием и ценой позиции, даже если товара в каталоге нет. Иначе история теряет смысл.

Сколько времени занимает перенос?

Считается на копии: сто тысяч заказов идут часами. Это выясняют до назначения даты запуска.

Что делать с расхождением в пару заказов?

Разбирать: обычно это потерянные позиции или неизвестный статус. Пара заказов сегодня - это сотня при повторе.

Смежное

Первоисточник