Інтеграція Ultima 2C і Ebay. Особистий досвід

Розробник партнера погодився поділитися особистим досвідом інтеграції з EBay.
Ми постараємося докладно розібрати весь процес, зазначимо, на які деталі підключення варто звернути особливо ретельне увагу; яку базову логіку знадобиться реалізувати в обробниках і сервісах вашої конфігурації для регулярного взаємодії з EBAY. Ну а також, висвітлимо типові труднощі і незручності, з якими абсолютно неминуче зіткнетеся при даній інтеграції (і ви, і замовники).

1.Особливості Ebay
У першу чергу, звичайно, неприємні особливості роботи Ebay, з якими доведеться мати справу не тільки розробнику:
  1. Ebay не любить РФ. Технічна підтримка – урізана, деякі функції в самому рідному кабінеті продавця Ebay можуть періодично відвалюватися. Підключають API неохоче.
  2. Звичайно, не проблема торгувати в якості неросійського продавця. І вказати в налаштуваннях іншу country of origin. Але знадобиться реальне юрособа (в моєму випадку спеціально реєстрували китайське, наприклад), телефон з кодом зазначеної країни для підтвердження. PayPal, як основна система оплати дуже ретельно перевіряє дані, особливо на юрособи і продавців. Billing address та інші дані повинні бути реальним
  3. EBay вводить все більше лімітів на товари, які можна постити безкоштовно. Весь асортимент вже не постимо, вибираємо що потрібніше.
2.Merchant Integration Platform (MIP)
Що це таке можна почитати тут.
Але тільки вступну частину. Це талмудическое твір не варто докладного ознайомлення. Знадобиться технічно дуже мало. А допомоги при баги і невиразних помилки інтеграції – ніякої.
Коротко – це api –надбудова уад API Ebay. В останнього є своє Trading Api і Shopping Api, але занадто громіздко, і сама підтримка EBAY завертає на MIP кажучи що щоб уникнути будь-якої вашої конкретної проблеми треба використовувати MIP.
MIP дозволяє відправляти всі дані на вхід ибея (товари, наявність, ціни, правила продавця) і отримувати все важливе на виході (власне, замовлення) через систему xml каналів.
2.1 Адміністративні завдання
Оформити за всіма правилами акаунт селлера з юрособою, прив'язати paypal, написати в підтримку MIP від свого аккаунта з проханням MIP підключити до магазину.
2.2 Завдання розробника
  • Налаштувати тих дані в кабінеті EBAY після підключення MIP.
  • Зберегти дані доступу до константи (або довідник, якщо планується використання кількох аккаунтів EBAY).
  • Зробити клас –генерилку вхідних каналів з нашими даними.
  • Зробити синтаксичний аналізатор вхідних даних.
3. Етапи підключення:
3.1 Платформа інтеграції продавця
Після того як MIP повідомить, що ваш магазин підключили, на сайті ebay в Мій Ebay -> Короткий огляд -> Обліковий запис з'явиться пункт меню «Платформа інтеграції продавця».
3.2 Ще трохи параметрів:
  • зайти в Міжнародні налаштування,
  • заповнити адресу та інше
  • Створити Правила
правила
Мова йде про правила Оплати, Доставки, Повернення.
При натисканні на них клієнт відправляється у відповідний пункт кабінету, де створює політики для кожного з цих розділів.
Важливо!
  1. Кабінет дає створити скільки завгодно політик. АЛЕ MIP приймає тільки 1 політику оплати, 1 доставки, 1 повернення. Тому завдання в налаштуваннях в рамках одного запису задати всі можливі правила: Для Будь-якої ваги, Регіону і так далі.
  2. Політиці пропонується дати ім'я, обмежень на введення немає. Але для MIP необхідно, щоб політика називалася латиницею, без пропусків і підкреслень.
політика
3.3 Схема Стрічки
Далі заходите в «Схема Стрічки» і вибираєте формат (рекомендується Ebay) і формат файлу (ми воліли XML). Там же можна завантажити архів з прикладами каналів ( та структури файлів на фтп сервері).
схема
3.4 Налаштування FTP
Там ми отримуємо\генера дані для доступу до FTP серверу MIP, куди ми будемо вантажити канали з товарами і звідки будемо забирати канали з резервами.(Сервер, порт, юзернейм, пароль.)
встановлення завершено, далі – до логіки
3.5 Готуємо метадані для зберігання налаштувань у нас
Для всіх отриманих вище налаштувань по підключенню до Мипу необхідно створити довідник.
В довідник пропонується відразу забити константи, що вказують посилання на дані, які будемо посилати в MIP і використовувати при вкачке резервів.
Довідник EbayAccountSetting
ID Long Not NULL,
Name (string(256) Long Not NULL,
MipServerAddress string(256) Long Not NULL,
MipServerPort Ineteger Not NULL,
MipServerLogin string(256) Long Not NULL,
MipServerPassword string(2048) Long Not NULL,
NotifyEmails string(256) Long Not NULL ), - можна вказати імейл розробників або відповідальних, які будуть слідкувати за помилками
ShippingPolicy string(256) Long Not NULL,
ReturnPolicy string(256) Long Not NULL,
PaymentPolicy string(256) Long Not NULL,
IsSuborder Boolean Not NULL default true,
LangID Long Not NULL , Довідник Dictionary Language - мова, для створення резерву
FirmID Long Not NULL , Довідник Dictionary Firm - фірма, для створення резерву
OfficeReserveID Long Not NULL , Довідник Dictionary Store, - офіс резерву
StoreReserveID Long Not NULL , Довідник Dictionary Store, - склад резерву
CurrencyID Long Not NULL , Довідник Dictionary Currency, валюта резерву
PriceTypeID Long Not NULL , Довідник Dictionary PriceType, - цінова категорія резерву
PriceZoneID Long Not NULL , Довідник Dictionary PriceZone, - цінова зона резерву
AgentGroupID Long Not NULL , Довідник Dictionary AgentGroup, - папка КА
PaymentTypeID Long Not NULL , Довідник Dictionary PaymentType - тип оплати при створенні резерву

Рекомендую додати інформаційні поля, начебто посилання на кабінет ebay, логін і пароль від сайту і так далі, щоб при необхідності налагодження не розкопувати ці дані за завданням.
В залежності від потреб клієнта та специфіки обліку документів продажу додаємо посилання на інші довідники, що визначають властивості створюваних резервів або товарів, що вивантажуються.
Також нам треба зробити просто таблицю в оракле, щоб стежити за тим, які товари ми завантажили в ebay.
Нам треба буде зняти ці товари з продажу, якщо в основному запиті (товари, які на сьогоднішній день потрапляють у вибірку) їх не буде (пропаде наявність або ціна), а по-іншому їх буде витягувати клопітно і незручно.
CREATE TABLE EBAY_EXPORTED_GOODS
(
EBAY_ACOUNT_ID NUMBER(18) NOT NULL, - FK на нашу табличку EBAY_ACCOUNT_SETTINGS
ARTICLE_ID NUMBER(18) NOT NULL, - FK на нашу табличку ARTICLES
UPLOAD_DT DATE
)

Також нам знадобиться відправляти для кожного товару код його категорії в системі EBAy, і щоб менеджери могли легко маппить наші товари до їх категоріями знадобиться довідник категорій Ebay.
За основу ми можемо взяти наш довідник товарних категорій ArticleGroups. Новий довідник теж буде деревовидний.
EbayCategories
ID long PK
String Name – varchar(2048)
ParentID long (referential на самого себе)
Code – string (будемо зберігати код ebay окремо від нашого PK щоб скрипт SQL з генерацією нового ID не заплутав нас і не згенерувати нові випадкові айдишники)

Довідник повинен бути деревоподібним за ParentID.
Нарешті, в нашому базовому довіднику ArticleGroups зробимо поле EbayCode string.
У Формі редагування наших рідних категорій товарів, треба зробити так, щоб менеджер міг вибрати відповідну категорію Ebay. Після вибору, при збереженні нашої категорії в поле
EbayCode нашого ArticleGroups збережемо Code обраної записи з довідника Категорії Ebay.
Далі, нам знадобиться створювати Документи продажу, Клієнтів, Адреси доставки, при імпорті замовлень.
Для цього в документ Sale додаємо EbayDocumentNo string(2048)
У CustomerAttribute додаємо EbayUserID string(256)
У DeliveryAddress EbayAddressID string(256)
4 Інтеграція
4.1 Система каналів
Якщо ми подивимося на адмінку Мипа в кабінеті продавця, зайдемо на фтп сервер з обліковими даними, отриманими в 3.4, а також погляньмо на приклад фідів, скачаний в 3.3, то побачимо однакову структуру
фиды1
фиды2
Канали розподілені по папках. В папках лежать однойменні канали (Availability.xml, Distribution.xml...)
3 фіда ми повинні віддати ebay, повідомивши про наші товари, їх наявність і ціни (Availability, Distribution, Product)
1 фід ми повинні вкачати – Order
Канали, які ми викачуємо від нас до EBAY, кладуться нами в корінь відповідної папки. Всі. Далі Ebay сам ставить в чергу, перекидає в папку обробки, потім в папку архіву, видає лог.
Фід, який ми закачуємо від EBAY до нас ми теж беремо з кореня order. Редагувати, видаляти або робити щось ще з файлом не треба.
Передбачте, який клас для запису, а головне, Парсингу XML буде максимально зручним.
Бажана максимальна гнучкість, можливість не йти по тегам, а перескакувати за елементами і вихоплювати їх за enumerаtorам, благо велика частина каналу буде шлаком, який імпортувати немає необхідності.
Мені було досить System.Xml.Linq.XDocument
За передачу даних від нас Ebay'ю відповідають три каналу:
4.1.1 Product
Описова інфа по товарах. Назва, атрибути(характеристики), категорія.
Всі обов'язкові теги дивимося в скачаному прикладі.
<productRequest> 

<product>

<SKU>369866</SKU>

<productInformation localizedFor="en_US">

<title>LCD cable for for Acer Aspire 3820, 3820T, 3820G, 3820TG, 3820TZ</title>

<description>

<productDescription>LCD cable for for Acer Aspire 3820, 3820T, 3820G, 3820TG, 3820TZ</productDescription>

</description>

<pictureURL>http://oursite.com/good_big_pics/369866.jpg</pictureURL>

<category categoryType="EBAY_LEAF_CATEGORY">168061</category> <!-- categoryType should be EBAY_LEAF_CATEGORY for the category to be applied. -->

<conditionInfo>

<condition>New</condition>

<conditionDescription>Brand new</conditionDescription>

</conditionInfo>

</productInformation>

</product>

</productRequest>

Зауваження:
<condition>New</condition>

<conditionDescription>Brand new</conditionDescription>

Можна вважати константою. Розібратися особисто нам з обмеженнями по постинг б/у товарів не вдалося — Назви станів Перечіслімого, які фіг раскопаешь в документації. Товар повинен бути new.
Всі теги з атрибутом localizedFor="en_US" передбачають, що для одного товару можна розмістити різну інформацію для різних підсайтів ebay, відповідних зазначеної локалі (наприклад, різні назви для En Ru)
4.1.2 Availability
Передає тільки наявність в штуках.
<inventoryRequest>
<inventory>
<SKU>369866</SKU>
<totalShipToHomeQuantity>5</totalShipToHomeQuantity>
</inventory>
</inventoryRequest>

4.1.3 Distribution
Ціни, ліміти покупки, ПОЛІТИКИ, про яких ми говорили 3.2
Тут як раз знадобляться англ. Назви Доставки, Повернення і Оплати.
<listingDetails>
<shippingPolicyName>BaseShipping</shippingPolicyName> <!-- Replace business policy names with your seller's business policy names. -->
<maxQuantityPerBuyer>5</maxQuantityPerBuyer>
<paymentPolicyName>Base</paymentPolicyName> <!-- Replace business policy names with your seller's business policy names. -->
<returnPolicyName>BaseReturn</returnPolicyName> <!-- Replace business policy names with your seller's business policy names. -->
<pricingDetails>
<listPrice>7.58</listPrice>
</pricingDetails>

Валюта тут не вказується, вона вказується клієнтом в особистому кабінеті.
4.1.4 Взаємозв'язок каналів
Правила постінга фідів дуже прості
  1. Ви самі задаєте періодичність, з якою вони постять Ebay, лімітів немає. Відповідно таск, який буде генерувати канали, налаштовуєте згідно графіку роботи інших тасков, оновлюють суттєві параметри товарів (наявність, ціни). Занадто часто його смикати не слід, благо у MIP своя черга, і часом об'ємний фід може стояти в черзі по кілька годин.
  2. Унікальним ідентифікатором товару в 3х каналах служить тег SKU, відповідно краще не оригінальничати, і не додавати туди складні конструкції з партномерами, а постить щось настільки ж однозначний і унікальне – наш артикул товару.
  3. Канали «сприймаються» по черзі. Спочатку повинен піти на фтп Product, потім Availabiliy, потім Distribution.
  4. Зняття товарів проводиться відсиланням наявності = 0.
    Тому заздалегідь передбачте таблицю, в якій будете зберігати товари, які вже запостили в MIP
  5. Кожен xml пакується в zip і кладеться на фтп, в однойменну папку. Передбачте бібліотеку для роботи з фтп.
    Ми брали Renci.SshNet.SftpClient
    Приклад методу завантаження файлу цією бібліотекою
    private void Upload(long accountId, string zipFileName, string innerPath)
    {
    var EbayAccount = DictionaryManager.GetRecord<EbayAccountSettings>(accountId);
    var serverAdr = EbayAccount.MipServerAddress; //mip.ebay.com;
    var port = (int)EbayAccount.MipServerPort;// 22
    var login = EbayAccount.MipServerLogin; // my acc
    var password = EbayAccount.MipServerPassword; 
    
    using(var fStream = new System.IO.FileStream(zipFileName, System.IO.FileMode.Open))
    {
    using(var c = new Renci.SshNet.SftpClient(serverAdr, port, login, password))
    {
    c.Connect();
    c.OperationTimeout = new TimeSpan(0, 15, 0);
    var fullPath = innerPath +System.IO.Path.GetFileName(zipFileName);
    /*innerPath – шлях в структурі даних на міп сервері. Наприклад, store/availability. zipFileName – ваш локальний шлях до сегенереному вами файлу з даними*/
    if (c.Exists(fullPath))
    {
    try
    {
    c.DeleteFile(fullPath);
    }
    catch (Exception e)
    {
    LogManager.GetLogger().Info(@"Експорт ebay files to mip. error delete exist file. fileName = {0}; error = {1}", fullPath, e.Message);
    }
    }
    c.UploadFile(fStream, fullPath, true);
    }
    }
    } 

  6. через деякий час (що залежить від черги ebay) результат обробки MIP кладе на фтп в ту ж папку, в підпапку
    Output/{current_date:M-дд-РРРР}/… .xml
Наприклад:
Ми завантажуємо наш файл з залишками на FTP
В папку /store/availability
Результат выплюнется в /store/availability/output/Jul-18-2016/

  1. Також можна паралельно вантажити або просто дивитися результати обробки каналів у кабінеті в Архіві стрічок
  2. В 99% випадків, якщо і була якась помилка, на етапі Product.xml і Availability.xml MIP промовчить і скаже, що все ок. Помилка буде, хіба що у вас відверто malformed xml.
Навіть якщо помилки пов'язані з цими фідами, помилки будуть видні тільки в результаті обробки останнього каналу, Distribution.xml
  1. Питання про те, чи має сенс парсити результати вивантаження до нас у ерп – ну вкрай спірне.
    Ніякої системності, сериализуемости помилки немає.
Більш того, за відсотками 70 з них доводиться звертатися письмово у підтримку MIP, що саме по собі захід з 50% результативністю. Запити часто доводиться висилати кілька разів, так як успішність обігу залежить від працівника, на якого потрапиш.
Гугл помилок допомагає мало.
Також, надзвичайно часто з'ясовується, що просто у самого Mip був якийсь issue, про тех. подробиці якого вони не поширюються, просто повідомляючи, що Користуйтеся на здоров'я, ми пофиксили.
Також, наявність у відповіді помилки не гарантує, що всі інші товари не прогрузились.
Іноді одна помилка блокує всі товари, навіть ті, до яких вона не відноситься, іноді тільки з усього каналу.
Підсумовуючи, можна сказати, що має сенс просто стежити за результатами вивантаження в кабінеті.
Заради тавтології можна зупинитися на такому рішенні:
Парсити по датах вміст теки /output/distribution. У Xml захоплювати тільки теги
І в текстово-інформативно-списковому варіанті просто дублювати їх в окремий довідник у ерп з датами, есл клієнт хоче дивитися помилки у ерп, а не в кабінеті.
Приклад дефектного відповіді
<?xml version='1.0' encoding='UTF-8'?>
<!-- RequestID: 5089523166 --><distributionResponse>
<responseMessage>
<SKU>6124</SKU>
<channelID>EBAY_RU</channelID>
<status>FAILURE</status>
<error>
<errorID>API_95</errorID>
<severity>ERROR</severity>
<category>REQUEST</category>
<message><![CDATA[Зазначена валюта аукціону не відповідає валюті аукціону для вибраного сайту.]]></message>
</error>
</responseMessage>
<status>FAILURE</status>
</distributionResponse>

Отже, спробуємо згенерувати і залити послідовно 3 фіда.
var startDate = DateTime.Now; //Поставимо дату початку вивантаження, щоб позначити їй товари, що потрапили у поточну вибірку
var accountId = 1L; //Выбирам аккаунт
var account = DictionaryManager.GetRecord<EbayAccountSetting>(accountId); //Данныепо аккаунту
var productDoc = ExportXmlProducts(account, startDate); //Сформуємо xml з товарами
ZipAndUploadFile(accountId, productDoc, "Product", "/store/product/"); //Заллємо
var availDoc = ExportXmlAvailability(account, startDate); //Сформуємо xml з залишками
ZipAndUploadFile(accountId, availDoc, "Availability", "/store/availability/"); //Заллємо
var distrDoc = ExportXmlAvailability(account, startDate); //Сформуємо xml з цінами і правилами
ZipAndUploadFile(accountId, availDoc, "Distribution", "/store/distribution/"); //Заллємо

Формуємо xml Товарів Products.xml
private XDocument ExportXmlProducts(EbayAccountSetting account, DateTime startDate)
{
var listings = new XElement("productRequest");
//Отримаємо дані по товарах, при цьому візьмемо тільки товари з ціною, залишками і категорією Ebay
var sql = @" SELECT A. ID,
NVL(PT.РЯДОК_ЗНАЧЕННЯ, A. ONLINE_NAME) AS NAME,
P. VALUE PRICE,
CASE WHEN PH.ARTICLE_ID IS NOT NULL
THEN PH.ARTICLE_ID||'_'||PH.VIEW_ID
ELSE "
END ICON_ID,
A. FEATURE_SET_ID,
B. NAME,
AG.EBAY_CODE EBAY_CODE
FROM ARTICLES A
JOIN /* Отримаємо тільки ті що на нашому складі */ VTB_STOCK VB ON VB.ARTICLE_ID=A. ID and VB.STORE_ID =:vStoreID
JOIN PRICES P ON A. ID=P. ARTICLE_ID AND P. PRICE_TYPE_ID=:vPriceTypeD AND P. PRICE_ZONE_ID=:vPriceZoneID /* Ціна за нашою категорії і зоні з облікового запису*/
JOIN BRANDS B ON A. BRAND_ID=B. ID
JOIN ARTICLE_GROUPS AG ON A. GROUP_ID=AG.ID
LEFT JOIN ARTICLE_PHOTOS PH ON A. ID=PH.ARTICLE_ID AND PH.PHOTO_ORDER=1
LEFT JOIN KERNEL.PROP_TRANSLATIONS PT ON PT.VALUE_ID = a.ID AND PT.OBJECT_ID = :vObjectID AND PT.LANG_ID = :vLangID
WHERE VB.QUANTITY>VB.RESERVE_QUANTITY /* Котрих більше нуля */
AND P. VALUE>0 /* Ціна більше нуля */
AND AG.EBAY_CODE IS NOT NULL /* Проставлена категорія Ebay */ ";
var pars = new Dictionary<string, object>{ {"vObjectID", productObjectId},
{"vLangID", account.LangID},
{"vStoreID", account.StoreReserveID},
{"vPriceTypeD", account.PriceTypeID},
{"vPriceZoneID", account.PriceZoneID}};
var goods = SqlService.Select(sql, pars);
//Формуємо xml товару однією структурою
foreach (var good in goods)
{
var propNode = GetProperties(Convert.ToInt64(good["ID"]), account.LangID);//Отримаємо окремо список хар-до
var productNode = new XElement("product",
new XElement("SKU", good["ID"].ToString()),
new XElement("productInformation", new XAttribute("localizedFor", "en_US"),
new XElement("title", good["NAME"].ToString()),
new XElement("description",
new XElement("productDescription", good["NAME"].ToString())) ,
"",//propNode,
new XElement("pictureUrl",
String.Format(@"http://mysite.com/good_pics/{0}.jpg", good["ICON_ID"].ToString())),
new XElement("category", new XAttribute("Type", "eBayLeafCategory"), good["EBAY_CODE"].ToString()),
new XElement("conditionInfo",
new XElement("Condition", "new"),
new XElement("conditionDescription", @"Brand new")))
);
listings.Add(productNode);
AddToExported(Convert.ToInt64(good["ID"]), account.ID startDate);
}
var doc = new XDocument(listings);
return doc;
}

Пара важливих зауважень:
  • Якщо розміщуєте товари не на рускоязычном сайті, не забудьте забрати потрібні переклади властивостей.
  • КОДИ ОБ'ЄКТІВ МЕТАДАНИХ, ЯКІ ПІДЛЯГАЮТЬ ПЕРЕКАЗУ, КРАЩЕ ВИНЕСТИ В КОНСТАНТИ, ВОНИ ОДНАКОВІ І ІНВАРІАНТНІ ДЛЯ ЯДРА.
    const long productObjectId = 2952;
    const long valueObjectId = 3485;
    const long featureObjectId = 3458;
    const long unitObjectId= 7680;
Нам треба відправити характеристики товару. Згадуємо структуру шаблонів опису та характеристик. Наше завдання привести всі характеристики товару до виду ХАРАКТЕРИСТИКА(string) – ЗНАЧЕННЯ(string), навіть у тих випадках, коли йде мова про декількох значеннях для однієї характеристики.
private string GetProperties(long articleId, long langId)
{
var sql = @"WITH T AS (
WITH T AS (SELECT
F. ID AS PROP_ID,
NVL(PT.РЯДОК_ЗНАЧЕННЯ, F. NAME) AS PROP_NAME,
V. SORT_ORDER,
CASE WHEN F. TYPE_ID=1 THEN TO_CHAR(D. NUMBER_VALUE)
WHEN F. TYPE_ID=2 THEN TO_CHAR(D. BOOLEAN_VALUE)
WHEN F. TYPE_ID=3 THEN (D. РЯДОК_ЗНАЧЕННЯ)
WHEN F. TYPE_ID=4 THEN TEXT_VALUE
WHEN F. TYPE_ID in (5,6,7) THEN nvl(PT1.РЯДОК_ЗНАЧЕННЯ,V. VALUE)
END VAL,
CASE WHEN UN.ID IS NOT NULL THEN
''|| NVL(PT2.РЯДОК_ЗНАЧЕННЯ, UN.NAME)
ELSE
"
END AS UNIT_NAME
FROM ARTICLE_FEATURE_VALUES D
JOIN ARTICLE_FEATURES F ON D. FEATURE_ID=F. ID
LEFT JOIN ARTICLE_FEATURE_UNITS UN ON F. UNIT_MEASUREMENT_ID=UN.ID
LEFT JOIN ARTICLE_FEATURE_VALID_VALUES V D. ON VALUE_ID=V. ID
LEFT JOIN KERNEL.PROP_TRANSLATIONS PT ON PT.VALUE_ID = F. ID AND PT.OBJECT_ID = :vPropObjectID AND PT.LANG_ID = :vLangID
LEFT JOIN KERNEL.PROP_TRANSLATIONS PT1 ON PT1.VALUE_ID = V. ID AND PT1.OBJECT_ID = :vValObjectID AND PT1.LANG_ID = :vLangID
LEFT JOIN KERNEL.PROP_TRANSLATIONS PT2 ON PT2.VALUE_ID = UN.ID AND PT2.OBJECT_ID = :vUnitObjectID AND PT2.LANG_ID = :vLangID
WHERE D. ARTICLE_ID=:vProdID
)
SELECT PROP_ID, PROP_NAME, LISTAGG(VAL||UNIT_NAME, ',')WITHIN GROUP (ORDER BY SORT_ORDER) VAL
FROM T
GROUP BY PROP_ID, PROP_NAME";

var pars = new Dictionary<string, object>{ {"vLangID", langId},
{"vProdID", articleId},
{"vValObjectID", valueObjectId},
{"vUnitObjectID", unitObjectId},
{"vPropObjectID", featureObjectId} };
var features = SqlService.Select(sql, pars);
var featuresqueue = features.Select(r => new XElement("attribute", new XAttribute("name", (string)r["PROP_NAME"]), (string)r["VAL"]).ToString())
.ToList();
return string.Join("", featuresqueue);
}

Сформуємо файл залишків Availability.xml
private XDocument ExportXmlAvailability(EbayAccountSetting account, DateTime startDate)
{
var listings = new XElement("inventoryRequest");
//Отримаємо всі збережені нами експортовані товари за цим акку. Якщо за датою не потрапили в останню вивантаження - знімаємо з продажу - відправляємо наявність = 0
var sql = @" SELECT EG.aRTICLE_ID ID, case when last_export_date < :vDat then NVL(VB.QUANTITY, 0) - NVL(VB.RESERVE_QUANTITY, 0) 0 else end QUANTITY
FROM EBAY_EXPORTED_GOODS EG
LEFT JOIN VTB_STOCK VB ON VB.ARTICLE_ID=EG.ARTICLE_ID and VB.STORE_ID =:vStoreID
WHERE EG.EBAY_ACCOUNT_ID=:vId ";
var pars = new Dictionary<string, object>{ {"vId", account.ID},
{"vStoreID", account.ReserveStoreID}};
var goods = SqlService.Select(sql, pars);
foreach (var good in goods)
{
var productNode = new XElement("inventory",
new XElement("SKU", good["ID"].ToString()),
new XElement("totalShipToHomeQuantity", good["QUANTITY"].ToString()));
listings.Add(productNode);
}
var doc = new XDocument(listings);
return doc;
}

По тому ж принципу формуємо Distribution.xml
Пакуємо наш xml в zip і аплоадим вже описаних нами методом
private void ZipAndUploadFile(long accountId, XDocument doc, string fileName, string innerPath)
{
var tempDirectory = @"C:\WINDOWS\Temp\EbayTemp";//Шлях для тимчасових ахивов на сервері. Виберіть папку, там же будемо дивитися історію
System.IO.Directory.CreateDirectory(tempDirectory);
var cFullFileName = System.IO.Path.Combine(tempDirectory, fileName);
var xmlFileName = System.IO.Path.ChangeExtension(cFullFileName, "xml");
doc.Save(xmlFileName);
var zipFullFileName = System.IO.Path.ChangeExtension(cFullFileName, "zip");
var zipper = new ZipBuilder();
zipper.AddFile(xmlFileName, delete: false);
zipper.SaveArchive(zipFullFileName);
Upload(accountId, zipFullFileName, innerPath);
}

За передачу замовлень від Ebay до нас відповідає 1 фід.
4.1.5 Order
Замовлення падають в папку order/output
Тут також файли згруповані по датах
{date:M-дд-РРРР}/… .xml
Наприклад /store/order/output/Jul-10-2016/
Можна парсити папку output по датах.
Але для зручності MIP скидає останній фід відповіді в файлик /store/order/output/order-latest
Причому недавній замовлення не перетирається новими, а тримається там певний час. Так що навіть незважаючи на можливість фейла тасков у поточних клієнтів було досить регулярно (раз в 5-10 хвилин) парсити саме цей файл.
Лайфхак:
Для зручності тестування можна логіку з тягаючи винести в сервіс і створити власну команду, і для тіста зробити можливість передачі на вхід параметром файлу xml. Тоді для тестування ви можете завантажити документ в кабінеті ибея або по фтп і прогнати його кілька разів.
В шапку документа продажу треба додати поле Номер EBAY. При обробці замовлення перевіряємо, що замовлень з таким номером немає (інакше просто игонрируем).
І останнє — всі замовлення каналу вже оплачені. Схема розрахунків в даному випадку еквівалентна еквайрингу, де виступає банком paypal. Відповідно, після створення замовлення необхідно створити відповідні документи еквайрингу (у базовій конфігурації через методи PaymentService).
Фід замовлення містить таку важливу інформацію
  1. Інфа про клієнта: Піб, email, телефон
  2. Адресу, детально, включаючи zip
  3. Список товарів з унікальним ідентифікатором, який ви передавали у каналах 1-3 в тезі SKU
  4. Ціну товарів
    Не забувайте, що ніяк не можна отримувати в момент імпорту актуальну ціну по колонці, тому що невідомо якою була ціна того каналу, по якому клієнт побачив і купив цей товар. Її беремо з Ebay.
  5. Ціну доставки
    Разом алгоритм імпорту повинен виглядати грубо так:
    • Беремо номер замовлення Ebay. Перевіряємо по ньому не імпортували вже його.
    • Робимо GetOrCreateAgent – за даними (#Піб, email, телефон#)

    • Робимо у агента GetOrCreateDeliveryAddress
    • Створюємо резерв, заповнюємо тч товарів з потрібними цінами.
    • Активуємо тч доставки, проставляємо код адреси та ціну Ebay.
Приклад каналу
<?xml version='1.0' encoding='UTF-8'?>
<pendingOrderFulfillmentResponse>
<pendingOrderFulfillment>
<order>
<orderID>222034713176-1766499808012</orderID>
<channelID>EBAY_US</channelID>
<createdDate>2016-07-20T12:45:09.000 Z</createdDate>
<buyer>
<buyerID>andreyka.mosin.1997-3</buyerID>
<firstName>Андрій</firstName>
<lastName>Мосін</lastName>
<email>andreyka.mosin.1997@mail.ru</email>
</buyer>
<seller>
<sellerID>pdirect.ua</sellerID>
</seller>
<logisticsPlan fulfillmentType="SHIP">
<shipping shippingType="DIRECT">
<shippingService>
<service/>
<method>RU_ExpeditedMoscowOnly</method>
</shippingService>
<shipToAddress>
<addressID>4504234220015</addressID>
<name>Андрій Мосін</name>
<phone>9527720655</phone>
<addressLine1>курчатова 16</addressLine1>
<addressLine2/>
<city>саров</city>
<stateOrProvince/>
<postalCode>607183</postalCode>
<country>RU</country>
</shipToAddress>
<expectedDeliveryDate/>
</shipping>
</logisticsPlan>
<lineItem>
<lineItemID>222034713176-1766499808012</lineItemID>
<listing>
<channelID>CustomCode</channelID>
<itemID>222034713176</itemID>
<SKU>413480</SKU>
<title>ELM327 BlueTooth V1.5 BIG автосканер ELM327 BlueTooth V1.5 BIG chip pic18f25k80</title>
</listing>
<quantity>1</quantity>
<unitPrice currencyCode="RUB">990.0</unitPrice>
<subtotal>
<priceline type="ITEM">
<amount currencyCode="RUB">990.00</amount>
</priceline>
<priceline type="SHIPPING">
<amount currencyCode="">350.00</amount>
</priceline>
<priceline type="ITEM_TAX">
<description>SalesTax</description>
<amount currencyCode="RUB">0.00</amount>
</priceline>
<priceline type="RECYCLING">
<description>ElectronicWasteRecyclingFee</description>
<amount currencyCode="RUB">0.00</amount>
</priceline>
<sumtotal currencyCode="RUB">1340.00</sumtotal>
</subtotal>
</lineItem>
<payment>
<paymentID>3TD678480B805680J</paymentID>
<total currencyCode="RUB">1340.0</total>
</payment>
<total>
<priceline type="ITEM">
<amount currencyCode="RUB">990.0</amount>
</priceline>
<priceline type="ITEM_TAX">
<amount currencyCode="RUB">0.0</amount>
</priceline>
<priceline type="SHIPPING">
<amount currencyCode="RUB">350.0</amount>
</priceline>
<priceline type="DISCOUNT">
<amount currencyCode="RUB">0.0</amount>
</priceline>
<sumtotal currencyCode="RUB">1340.0</sumtotal>
</total>
<status>
<paymentStatus>PAID</paymentStatus>
<logisticsStatus>NOT_SHIPPED</logisticsStatus>
</status>
<note/>
</order>
</pendingOrderFulfillment>
</pendingOrderFulfillmentResponse>

Приклади імпорту
var accountId = 1L; //Выбирам аккаунт
var EbayAccount = DictionaryManager.GetRecord<EbayAccountSetting>(accountId); //Отримаємо дані, щоб передати далі
var doc = Download(accountId); //завантажити Renci
foreach (var ordXml in doc.Root.Elements("pendingOrderFulfillment"))
{
if (ordXml.Elements("order").Count() == 0)
{
continue;
}
CreateReserve(ordXml.Elements("order").First(), EbayAccount); //Створимо резерв
}

Стрибка організуємо також, як і закачували свій фід в Ebay
private XDocument Download(long accountId)
{
var EbayAccount = DictionaryManager.GetRecord<EbayAccountSetting>(accountId);
var serverAdr = EbayAccount.MipServerAddress; //mip.ebay.com;
var port = (int)EbayAccount.MipServerPort;// 22
var login = EbayAccount.MipServerLogin; // my acc
var password = EbayAccount.MipServerPassword; 

var doc = new XDocument();
using(var c = new Renci.SshNet.SftpClient(serverAdr, port, login, password))
{
c.Connect();
c.OperationTimeout = new TimeSpan(0, 15, 0);
byte[] fileBytes = c.ReadAllBytes(@"/store/order/output/order-latest");
if ( fileBytes == null || fileBytes.Length == 0)
{
return doc;
}
using (var stream = new System.IO.MemoryStream(fileBytes, false))
{
try
{
doc = XDocument.Load(new System.Xml.XmlTextReader(stream));
}
catch(Exception e)
{
LogManager.GetLogger().Info(@"Import ebay files. error = {0}", e.Message);
}
}
}

return doc;
}

Власне створення документа продажу.
private void CreateReserve(XElement ebayOrdXml, EbayAccountSetting EbayAccount)
{
var ebayOrderID = ebayOrdXml.Elements("orderID").First().Value;
if (CheckOrderExists(ebayOrderID)) //Перевіримо за кодом Ebay немає такого вже в базі. Вони не апдейтятся. Якщо є - пропускаємо.
{
LogManager.GetLogger().Info(@"Замовлення вже існує");
return;

}

var shippingXml = ebayOrdXml.Elements("logisticsPlan").First().Elements("shipping").First().Elements("shipToAddress").First();
var delivAmountFromEbay = 0M;
//Отримаємо ціну доставки. Вважати нічого не треба, так як нам треба зафіксувати ціну за якою вже купили і оплатили.
delivAmountFromEbay = Decimal.Parse(ebayOrdXml.Elements("total").First().Elements("priceline").First().Elements("amount").First().Value.Trim(), System.Globalization.NumberStyles.AllowDecimalPoint, System.Globalization.CultureInfo.InvariantCulture);
//Витягнемо все що нам знадобиться з тегів
var phone = shippingXml.Elements("phone").First().Value;
var firstName = shippingXml.Elements("firstName").First().Value;
var lastName = shippingXml.Elements("lastName").First().Value;
var Street1 = shippingXml.Elements("addressLine1").First().Value;

var Street2 = shippingXml.Elements("addressLine2").First().Value;
var CityName = shippingXml.Elements("центр").First().Value;

var StateOrProvince = shippingXml.Elements("stateOrProvince").First().Value;
var PostalCode = shippingXml.Elements("postalCode").First().Value;
var EbayAddressID = shippingXml.Elements("addressID").First().Value;
var ebayBuyerUserID = ebayOrdXml.Elements("buyerID").First().Value.Trim();
var email =ebayOrdXml.Elements("buyer").First().Elements("email").First().Value;
//Створимо або отримаємо КА
var agentID = CreateAgent(firstName, lastName, email, телефон,Street1,ebayBuyerUserID, EbayAccount);
//Створимо або отримаємо Адреса
var adressID = CreateAgentAddress(agentID, PostalCode, firstName, phone, CityName, StateOrProvince, EbayAddressID, Street1, Street2);
//Всі дані отримали, створюємо резерв
var initialSubtype = SaleDocument.Subtypes.Reserve;
var document = DocumentManager.NewDocument<SaleDocument>(initialSubtype);
document.AgentID = agentID;
document.OfficeID = EbayAccount.OfficeReserveID;
document.PriceTypeID = EbayAccount.PriceTypeID;
document.FirmID = EbayAccount.FirmID;
document.StoreID = EbayAccount.StoreReserveID;
//Основні заголовки є, импортим товари, за SKU
foreach(var transXml in ebayOrdXml.Elements("lineItem"))
{
var articleNo= Convert.ToInt64(transXml.Elements("listing").First().Elements("SKU").First().Value);
var qty= Convert.ToInt64(transXml.Elements("quantity").First().Value);
//Отримаємо ціну доставки. Вважати нічого не треба, так як нам треба зафіксувати ціну за якою вже купили і оплатили.
var price = Decimal.Parse(transXml.Elements("unitPrice").First().Value.Replace(".", ","));
document.Articles.Add(new SaleArticleTablePartRow {
ArticleID = articleNo,
SaleQuantity = qty,
ReservedQuantity = qty,
OriginalPrice = price,
SalePrice = price,
Amount = price * qty
});
}
//Скористаємося дуже зручною функой розрахунку доставки вебсервиса і сайтів. У нас буде доставка outsource. Суму му вже отримали
var deliveryWizardRow = WebService.GetDeliveryWizardRow(document, adressID, AgentType.Constants.CustomerID, DateTime.Now, "outsource", EbayAccount.ID delivAmountFromEbay);
document.DeliveryWizard.Clear();
document.DeliveryWizard.Add(deliveryWizardRow);
document.Delivery.AddRange(DeliveryService.CreateDeliveries(document));
DocumentManager.SaveDocument(document);
}

Перевіряємо унікальність замовлення за кодом Ebay
private bool CheckOrderExists(string ebayOrderID)
{
return (d from in DataContext.GetTable<SaleDocument>().Where(x => x.EbayDocumentNo == ebayOrderID) select d).Any();
}

Агента та адресу ми створюємо АБО апдейтим. Спочатку пробуємо отримати запис по унікальному ідентифікатору Ebay, і тільки якщо немає – створити
private long CreateAgent(string firstName, string lastName, string email, string phone, string Street1, string ebayBuyerUserID, EbayAccountSetting EbayAccount)
{
var customer = new CustomerAttribute() {};
var privatePerson = new PrivatePersonAttribute() {};
var agent = new Agent() {};
var exAgents = (d from in DataContext.GetTable<Agent>().Where(x => x.Customer.EbayUserID == ebayBuyerUserID)

select new {
Id = d.ID
CustomerID = d.CustomerID,
PrivatePersonID = d.PrivatePersonID
}).ToList();

if (exAgents.Any())
{
customer = DictionaryManager.GetRecord<CustomerAttribute>(exAgents.First().CustomerID.GetValueOrDefault());
privatePerson = DictionaryManager.GetRecord<PrivatePersonAttribute>(exAgents.First().PrivatePersonID.GetValueOrDefault());
agent = DictionaryManager.GetRecord<Agent>(exAgents.First().Id);
}
else
{
customer = DictionaryManager.NewRecord(new CustomerAttribute
{
EbayUserID = ebayBuyerUserID,
PriceTypeID = EbayAccount.PriceTypeID //Унік. ід. Ибея
});

privatePerson = DictionaryManager.NewRecord(new PrivatePersonAttribute
{
GenderID = Gender.Constants.Undisclosed,
});

agent = DictionaryManager.NewRecord(new Agent
{
TypeID = AgentType.Constants.CustomerID,
FormID = AgentForm.Constants.PrivatePersonID,
GroupID = EbayAccount.AgentGroupID
});
}
customer.ReserveLifetime = 1;
DictionaryManager.SaveRecord(customer);

privatePerson.FirstName = firstName;
privatePerson.LastName = lastName;
privatePerson.Phone =phone;
privatePerson.Email = email;

DictionaryManager.SaveRecord(privatePerson);
agent.Name = string.Format("{0} {1}", lastName, firstName);
agent.PrivatePersonID = privatePerson.ID;
agent.CustomerID = customer.ID;
DictionaryManager.SaveRecord(agent);
return agent.ID;
}

private long CreateAgentAddress(long agentID, string PostalCode, string firstName, string phone, string CityName, string StateOrProvince, string EbayAddressID, string Street1, string Street2)
{
var address = new DeliveryAddress() {};
var exAddress = DictionaryManager.GetRecords<DeliveryAddress>(x => x. EbayAddressID == EbayAddressID);//пошукаємо за Унік. ід. ebay
if (exAddress.Any())
{
address = exAddress.First();
}
else
{
address = DictionaryManager.NewRecord(new DeliveryAddress
{
//Заповнити константами. МИ не завжди зможемо отримати ці дані
DeliveryAreaID = DeliveryArea.Constants.Default,
Latitude = 0M,
Longitude = 0M,
EbayAddressID = EbayAddressID
});
}
address.Address = String.Format("{1}, {2}, {3}, {4}", PostalCode, CityName, StateOrProvince, Street1, Street2);
if (address.Latitude == 0)
{
//Спробуємо координати Геосервисом
var locations = GeoService.GetLocations(address.Address);
if (locations.Any())
{
address.Latitude = locations.First().Latitude;
address.Longitude = locations.First().Longitude;
address.DeliveryAreaID = DeliveryService.FindDeliveryArea(address.Latitude, address.Longitude);
}
}
address.ContactPersonName = firstName;
address.ContactPersonPhones = phone;
DictionaryManager.SaveRecord(address);
return address.ID;
}

5.Категорії Ebay
В Ebay є власна структура категорій товарів. Спостерігати її ми можемо на
http://www.ebay.com/sch/allcategories/all-categories
Для всіх локалей структура однакова, є тільки переклади назв, якщо ми зайдемо на російську Ebay, але структура дерева та код категорій інваріанта.
MIP вимагає зазначення належності товару до тієї чи іншої категорії, Products.xml є тег обов'язковий, причому сам Міп не надає будь-якого користувача або методу Апі для отримання списку цих категорій, припускаючи, що інтегратори тут впораються самі).
Існує не дуже зручна і заплутана система включення автоматичного визначення категорії Мипом (за його власним маркетингових рекомендацій); але ми її не будемо розглядати, бо досвід показує, що для точного позиціонування товару краще нам мати на руках весь список категорій, а вірніше надати його контентщикам, і дати їм самим налаштувати прив'язки.
Для цього нам знадобиться:
  • створити у себе деревовидний довідник для зберігання категорій Ebay (описаний в 3.5)
  • додати посилання на категорію Ebay у нашого довідника категорій товарів (описаний в 3.5)
  • навісити перевірку заповнення поля при збереженні нашої категорії
  • власне вкачати до собі структура категорій.
5.1 Імпорт категорій з Ebay.
Оновлювати дерево категорій доведеться вкрай рідко. Ebay завчасно вивішує продавцям нагадування про майбутні зміни в структурі категорій. Краще зробити власну команду, якій менеджери зможуть самі перезаливать список категорій у міру потреби.
Ви в принципі можете написати парсер HTML, який буде ходити по сторінці http://www.ebay.com/sch/allcategories/all-categories і її посиланнях і забирати категорії, але в нашому випадку розглянемо можливість отримання категорій через рідне Api Ebay.
Це окреме Api, надбудовою над яким служить MIP, про який ми говоримо в даній статті. Може здатися образливим підключати його у списку категорій, але Настійно не рекомендується намагатися через нього працювати з товарами і замовленнями. Особливо якщо мова та російськомовному сайті – підтримка Ebay вас відразу заверне на Mip.
5.2. Підключення API Ebay.
Для підключення до Api Ebay вам знадобиться зареєструватися як Ebay Developer. https://developer.ebay.com/base/membership/signin/fyp Раніше це являло деякі труднощі, але зараз це цілком швидко і просто, тільки вводите валідні реальні дані.
Після реєстрації зможете створити Applicationб до якого вам видадуть AppId, DevId і CertId. Також знадобиться створити токен, але щоб його валідувати треба буде зайти під вашим обліковим записом продавця Ebay, так що на момент підключення вже потрібно буде, щоб наш клієнт зареєстрував аккаунт.
5.3. Зберігаємо авторизаційні дані для АПІ
Ці дані ми збережемо string константи, незалежно від планованого кол-ва акаунтів Ebay, так як набір категорій всього один для будь-якого облікового запису і будь-якого юзера.
Отже, нам потрібні константи типу string
EbayAppId
EbayAppToken
EbayDevId
EbayCertId

Також відразу внесемо до константи адреса вебсервиса Ebay, локаль і версію апі, яку ми використовуємо.
EbayEndPoint
EbayVersion
EbaySiteId – 215, якщо хочете вкачати категорії російською.

5.4 Імпортуємо wsdl
Сама wsdl розташована за адресою http://developer.ebay.com/webservices/latest/ebaySvc.wsdl
На випадок, якщо адреса зміниться, його можна знайти в https://go.developer.ebay.com/api-documentation.
Нам треба буде створити клас або wsdl.
Нагадуємо, як це робиться.
Беремо утиліту wsdl.exe (лежить приблизно тут c:\Program Files (x86)\Microsoft SDKs\Windows\v8.1A\bin\NETFX 4.5.1 Tools), яка в поточному каталозі створює отримані інтеграційні класи.
Приклад запиту:
>"c:\Program Files (x86)\Microsoft SDKs\Windows\v8.1A\bin\NETFX 4.5.1 Tools\wsdl.exe http://wstest.dpd.ru/services/geography?wsdl /out:MyEbayclass.cs

Зауваження:
a) не використовуйте x64 версію. у мене відмовилася генерувати
б) використовуйте ту версію фрейморка, під яким планується використання класів
Отриманий клас вставимо в кінець обробника, який буде отримувати категорії
5.5.Пишемо команду-імпортер
Вставляємо вниз коду обробника код згенерованого клас wsdl
Треба створити об'єкт нашого класу, щоб пізніше їм користуватися.
private static object _lock = new Object();

private eBayAPIInterfaceService EbayService
{
get
{
if (_instance == null)
{
lock ( _lock)
{
if (_instance == null)
{
_instance = new eBayAPIInterfaceService();
_instance.Timeout = 1200000;
return _instance;
}
}
}
return _instance;
}

}

Отримаємо список категорій від Ebay.
var a = Constants.Url;
var endpoint = Constants.Ebay.ApiEndpoint;
var callName = "GetCategories";
var siteId = "215" ;//Зона росія
var appId = Constants.EbayAppId;// '// use your app ID
var devId = Constants.EbayDevId;// use your dev ID
var certId = Constants.EbayCertId; // use your cert ID
var version= "405";
var token = Constants.EbayToken;
// Build the request URL
var requestURL = endpoint + "?callname=" + callName + "&siteid=" + siteId + "&appid=" + appId + "&version=" + version + "&routing=default";
EbayService.Url = requestURL;
// Set credentials
EbayService.RequesterCredentials = new CustomSecurityHeaderType();
EbayService.RequesterCredentials.eBayAuthToken = token;
EbayService.RequesterCredentials.Credentials = new UserIdPasswordType()
EbayService.RequesterCredentials.Credentials.AppId = appId;
EbayService.RequesterCredentials.Credentials.DevId = devId;
EbayService.RequesterCredentials.Credentials.AuthCert = certId;
GetCategoriesRequestType request = new GetCategoriesRequestType();
request.Version = "405";
request.CategorySiteID = "215";
request.CategoryParent = new string[] {"0"};
request.ViewAllNodes = true; 
request.LevelLimit = 0;
request.DetailLevel = new DetailLevelCodeType[] {DetailLevelCodeType.ReturnAll};
var a = ConstantManager["Url"].ToString();
var endpoint = ConstantManager["Ebay. Api Endpoint"].ToString();
var callName = "GetCategories";
var siteId = "215" ;//Зона росія
var appId = ConstantManager["EbayAppId"].ToString() ;// '// use your app ID
var devId = ConstantManager["EbayDevId"].ToString();// use your dev ID
var certId = ConstantManager["EbayCertId"].ToString(); // use your cert ID
var version= "405";
var token = onstantManager["EbayToken"].ToString();
// Build the request URL
var requestURL = endpoint + "?callname=" + callName + "&siteid=" + siteId + "&appid=" + appId + "&version=" + version + "&routing=default";

GetCategoriesResponseType cats = EbayService.GetCategories(request);
if (cats.Errors != null && cats.Errors.Length > 0 )
{
foreach(var er in cats.Errors)
{
// Опрацюємо кожну текстову помилку
}
throw new Exception("Якщо треба вийдемо з команди з помилкою");
}

if ( cats.CategoryArray == null || cats.CategoryArray.Length <= 0)
{
// Опрацюємо той факт, що прийшов порожній список категорій, такого не миє бути
}

CategoryType[] catArray = cats.CategoryArray;

SyncronizeCats(-1, "", catArray); // пішому метод валідного відповіді

Напишемо функцію імпорту рядків з категоріями з Ebay, не забудемо видалити у нас записи, які не приходять більше. Краще йти драбинкою по дереву довідника
private void SyncronizeCats(long parentID, string ebayParentCode, CategoryType[] catArray)
{
//Отримаємо вже наявні у нас категорії з тим же батьком
var ourEbayCats = DictionaryManager.GetRecords<ArticleGroup>(x => (x.ParentID == parentId));
//Записуємо прийшли коди ибей
var processedCodes = new List < string>();
foreach(var ebayCat in catArray.Where(c => (string.IsNullOrEmpty(ebayParentCode) && c.CategoryLevel == 1 ) || (c.CategoryParentID != null
&& c.CategoryParentID.Any(h => h.Trim() != c.CategoryID && h.Trim() == ebayParentCode))))
{
if (!string.IsNullOrEmpty(ebayCat.CategoryID) && !string.IsNullOrEmpty(ebayCat.CategoryName))
{
GetOrCreateCategory(bayCat.CategoryID.Trim(), ebayCat.CategoryName, parentID);
processedCodes.Add(ebayCat.CategoryID.Trim());
}
}
foreach(var catRow in ourEbayCats)
{
if (!(processedCodes.Contains(catRow.EbeyCode)))
{
//Видаляємо ті, яких вже немає в новому списку
DictionaryManager.DeleteRecord<ArticleGroup>(catRow.ID);
}
else
{
//Вкачаем рівнем нижче
SyncronizeCats(catRow.ID catRow.Code, catArray);
}
}
}

Ну і, нарешті, апдейт або створення категорії.
private void GetOrCreateCategory(string ebayCategoryID, string ebayCatCategoryName, long parentID)
{
var ebayCat = new ArticleGroup() { };
var cats = DictionaryManager.GetRecords<ArticleGroup>(x => x.Name == ebayCatCategoryName);
if (cats.Any())
{
ebayCat = cats.First();
}
else
{
ebayCat = DictionaryManager.NewRecord( new ArticleGroup() { });
}
//Проставляємо парент, назву та інші властивості
DictionaryManager.SaveRecord(ebayCat);
}

Разом
Технічно, інтеграція не представляє складності (весь обсяг код не перевищує декількох киллобайт), проте в деталях спливає чимало підводних каменів з деякими з яких ми і намагалися вас познайомити.
Джерело: Хабрахабр

0 коментарів

Тільки зареєстровані та авторизовані користувачі можуть залишати коментарі.