21 апреля 2019 г. 17:07:09

Яндекс-Маркет

8 years ago
#77 Цитировать
Здравствуйте!
Собираюсь адаптировать nopCommerce для яндекс-маркета. Раньше никогда не занимался яндекс-маркетом, подскажите в общих чертах как подойти к этой задаче.
Мне кажется, надо написать хранимую процедуру в MSSQL, которая выгрузит данные в YML файл... правильно ли я понимаю... или нет?
0
8 years ago
#78 Цитировать
Процедуру писать не надо. Стоит посмотреть на проект Froogle в /Library/ Можно взять его за основу.
Просто в новом проекте потянуть нужные товары с бд.
Я попробую сделать, может и выложу код через пару дней.
0
8 years ago
#79 Цитировать
Есть ли успехи? Вроде бы должно быть просто, но самому лень :)
Кстати, спасибо за совет, я чуть не начал хранимую процедуру писать!
0
8 years ago
#269 Цитировать
Гость wrote:
Процедуру писать не надо. Стоит посмотреть на проект Froogle в /Library/ Можно взять его за основу.
Просто в новом проекте потянуть нужные товары с бд.
Я попробую сделать, может и выложу код через пару дней.

расскажите подробнее где найти этот проект?
0
8 years ago
#274 Цитировать
PromotionProviders/Nop.Froogle/FroogleService.cs
0
8 years ago
#326 Цитировать
по поводу формирования прайса для yandexа: если кому интересно - могу скинуть класс, формирующий прайс. Запуск производится автоматически в заданное время через web.config. класс ориентирован под собственные нужды, но при необходимости, его легко можно переделать под себя
0
8 years ago
#331 Цитировать
pasha2100 wrote:
по поводу формирования прайса для yandexа: если кому интересно - могу скинуть класс, формирующий прайс. Запуск производится автоматически в заданное время через web.config. класс ориентирован под собственные нужды, но при необходимости, его легко можно переделать под себя

Был бы Вам очень признателен!
0
8 years ago
#332 Цитировать
код класса:
//------------------------------------------------------------------------------
// The contents of this file are subject to the nopCommerce Public License Version 1.0 ("License"); you may not use this file except in compliance with the License.
// You may obtain a copy of the License at  http://www.nopCommerce.com/License.aspx.
//
// Software distributed under the License is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or implied.
// See the License for the specific language governing rights and limitations under the License.
//
// The Original Code is nopCommerce.
// The Initial Developer of the Original Code is NopSolutions.
// All Rights Reserved.
//
// Contributor(s): _______.
//------------------------------------------------------------------------------

using System;
using System.Xml;
using System.IO;
using System.Text;
using NopSolutions.NopCommerce.BusinessLogic.Caching;
using NopSolutions.NopCommerce.BusinessLogic.Categories;
using NopSolutions.NopCommerce.BusinessLogic.Configuration.Settings;
using NopSolutions.NopCommerce.BusinessLogic.Infrastructure;
using NopSolutions.NopCommerce.BusinessLogic.Media;
using NopSolutions.NopCommerce.BusinessLogic.Products;
using NopSolutions.NopCommerce.BusinessLogic.SEO;
using NopSolutions.NopCommerce.BusinessLogic.Products.Specs;
using NopSolutions.NopCommerce.BusinessLogic.Tasks;
using Ionic.Zip;

namespace NopSolutions.NopCommerce.BusinessLogic.Utils
{
    /// <summary>
    ///
    /// </summary>
    public partial class GenerateYMLTask : ITask
    {
        private const string DateFormat = @"yyyy-MM-dd HH:mm";
        private const string ALSOGENERATEYML = "nop.AlsoGenerateYML";
        private TimeSpan timeGenerate;
        private ICacheManager cacheManager;
        private readonly object l_state = new object();
        int languageId = 0;

        /// <summary>
        /// Executes a task
        /// </summary>
        /// <param name="node">Xml node that represents a task description</param>
        public void Execute(XmlNode node)
        {
            lock (l_state)
            {
                cacheManager = new NopStaticCache();
                var attribute1 = node.Attributes["timeGenerate"];
                if (attribute1 != null && !String.IsNullOrEmpty(attribute1.Value))
                {
                    timeGenerate = TimeSpan.Parse(attribute1.Value);
                }

                bool alsoGenerate = false;
                object obj = cacheManager.Get(ALSOGENERATEYML);
                if (obj != null)
                {
                    alsoGenerate = Convert.ToBoolean(obj);
                }
                DateTime nowTime = DateTime.Now;
                DateTime nextGenerateTime = DateTime.Now;

                if (alsoGenerate && (nowTime.Date + timeGenerate) <= nowTime)
                {
                    nextGenerateTime = (nowTime.Date + new TimeSpan(1, 0, 0, 0)) + timeGenerate;
                }
                else
                {
                    nextGenerateTime = nowTime.Date + timeGenerate;
                }

                if (nowTime > (nextGenerateTime - (new TimeSpan(3, 0, 0))) && nowTime < (nextGenerateTime - (new TimeSpan(1, 0, 0))))
                {
                    cacheManager.Remove(ALSOGENERATEYML);
                }

                if (!alsoGenerate && (nowTime > nextGenerateTime && nowTime < (nextGenerateTime + new TimeSpan(2, 0, 0))))
                {
                    string filename = AppDomain.CurrentDomain.BaseDirectory + "\\files\\yml\\priceyml.zip";
                    if (File.Exists(filename))
                        File.Delete(filename);
                    languageId = IoC.Resolve<ISettingManager>().GetSettingValueInteger("YMLPrices.LanguageId", 0);
                    ZipFile zipFile = new ZipFile(filename, Encoding.UTF8);
                    zipFile.AddEntry("priceyml.xml", (name, stream) =>
                    {
                        XmlTextWriter xtw = new XmlTextWriter(stream, Encoding.UTF8);
                        xtw.Formatting = Formatting.Indented;
                        xtw.WriteStartDocument();

                        Generate(xtw);

                        xtw.Flush();
                        xtw.Close();

                    });
                    zipFile.Save();
                    cacheManager.Add(ALSOGENERATEYML, true);
                }
            }
        }
        string EncodingString(string value)
        {
            //return win1251.GetString(Encoding.UTF8.GetBytes(value));
            return value;
        }

        /// <summary>
        /// This will build an xml sitemap for better index with search engines.
        /// See http://en.wikipedia.org/wiki/Sitemaps for more information.
        /// </summary>
        /// <param name="stream">Stream of sitemap.</param>
        public void Generate(XmlTextWriter writer)
        {


            //Write the DocumentType node.
            writer.WriteDocType("yml_catalog", null, "shops.dtd", null);

            writer.WriteStartElement("yml_catalog");

            writer.WriteAttributeString("date", DateTime.Now.ToString(DateFormat));

            writer.WriteStartElement("shop");

            writer.WriteElementString("name", EncodingString(IoC.Resolve<ISettingManager>().GetSettingValue("YMLPrices.StoreName")));

            writer.WriteElementString("company", EncodingString(IoC.Resolve<ISettingManager>().GetSettingValue("StoreOwner.ShortName")));

            writer.WriteElementString("url", IoC.Resolve<ISettingManager>().StoreUrl);

            GenerateCurrencies(writer);

            GenerateCategories(writer);

            GenerateProducts(writer);

            writer.WriteEndElement();

            writer.WriteEndElement();
        }

        void GenerateCurrencies(XmlTextWriter writer)
        {
            writer.WriteStartElement("currencies");

            writer.WriteStartElement("currency");

            writer.WriteAttributeString("id", "RUR");

            writer.WriteAttributeString("rate", "1");

            writer.WriteEndElement();

            writer.WriteEndElement();
        }

        void GenerateCategories(XmlTextWriter writer)
        {
            writer.WriteStartElement("categories");

            var categories = IoC.Resolve<ICategoryService>().GetAllCategories(false);

            foreach (var category in categories)
            {
                writer.WriteStartElement("category");

                writer.WriteAttributeString("id", category.CategoryId.ToString());

                if (category.ParentCategoryId != 0)
                    writer.WriteAttributeString("parentId", category.ParentCategoryId.ToString());

                writer.WriteString(EncodingString(category.GetLocalizedName(languageId)));

                writer.WriteEndElement();
            }

            writer.WriteEndElement();
        }

        void GenerateProducts(XmlTextWriter writer)
        {
            writer.WriteStartElement("offers");
            var products = IoC.Resolve<IProductService>().GetAllProducts(false);

            foreach (var product in products)
            {
                writer.WriteStartElement("offer");

                //_writer.WriteAttributeString("type", "vendor.model");

                writer.WriteAttributeString("available", "false");

                writer.WriteAttributeString("id", product.ProductId.ToString());

                if (product == null)
                    throw new ArgumentNullException("product");
                string seName = SEOHelper.GetProductSEName(product);

                string url2 = SEOHelper.EnableUrlRewriting ? IoC.Resolve<ISettingManager>().GetSettingValue("SEO.Product.UrlRewriteFormat") : "{0}Product.aspx?ProductID={1}";

                string url = string.Format(url2, IoC.Resolve<ISettingManager>().StoreUrl, product.ProductId, seName).ToLowerInvariant();

                writer.WriteElementString("url", url);

                writer.WriteElementString("price", PriceHelper.GetFinalPrice(product.ProductVariants[0], false).ToString().Replace(",", "."));

                writer.WriteElementString("currencyId", "RUR");
                string prefixName = "";
                if (product.ProductCategories.Count > 0)
                {
                    writer.WriteElementString("categoryId", product.ProductCategories[0].CategoryId.ToString());
                    prefixName = IoC.Resolve<ISettingManager>().GetSettingValue("YMLPrices.Prefix" + product.ProductCategories[0].Category.GetLocalizedSEName(languageId));
                }

                writer.WriteElementString("picture", IoC.Resolve<IPictureService>().GetPictureUrl(product.DefaultPicture, IoC.Resolve<ISettingManager>().GetSettingValueInteger("Media.Product.ThumbnailImageSize", 125), true));

                writer.WriteElementString("delivery", "true");

                writer.WriteElementString("local_delivery_cost", "0");

                string man = "";
                if (product.ProductManufacturers.Count > 0)
                {
                    man = product.ProductManufacturers[0].Manufacturer.GetLocalizedName(languageId);
                }

                string model = "";

                model = product.GetLocalizedName(languageId);

                string name = "";

                if (!string.IsNullOrEmpty(prefixName))
                    prefixName = prefixName + " ";

                name = prefixName + man + " - " + model;

                writer.WriteElementString("name", EncodingString(name));

                if (!string.IsNullOrEmpty(man))
                {
                    writer.WriteElementString("vendor", EncodingString(man));
                }

                string specDescription = "";
                var productSpecificationAttributes = IoC.Resolve<ISpecificationAttributeService>().GetProductSpecificationAttributesByProductId(product.ProductId, null, true);
                foreach (var productSpecificationAttribute in productSpecificationAttributes)
                {
                    specDescription = specDescription + productSpecificationAttribute.SpecificationAttribute.GetLocalizedName(languageId) + " - " + productSpecificationAttribute.SpecificationAttributeOption.GetLocalizedName(languageId) + "; ";
                }

                writer.WriteElementString("description", EncodingString(product.GetLocalizedShortDescription(languageId) + " " + specDescription));

                writer.WriteElementString("sales_notes", EncodingString(IoC.Resolve<ISettingManager>().GetSettingValue("YMLPrices.SalesNote")));

                writer.WriteEndElement();
            }

            writer.WriteEndElement();
        }
    }
}


файл сохраняется в архив в папке \files\yml\priceyml.zip
здесь берется допущение, что каждому товару соответствует одна категория и один производитель
в название товара подставляется префикс - имя категории в единственном числе - он находится в настройках магазина, название настройки для префикса - YMLPrices.Prefix+название категории
описание товара в прайсе - короткое описание товара в базе+ характеристики товара
также используются другие дополнительные настройки магазина - посмотрите код, увидите сами

следует также внести изменения в файл Libraries\Nop.BusinessLogic\Utils\PictureService.cs
в процедуре public string GetPictureUrl(Picture picture, int targetSize, bool showDefaultPicture)
поменять
url = CommonHelper.GetStoreLocation() + "images/thumbs/" + localFilename;
            return url;

на
            if (HttpContext.Current == null)
                url = IoC.Resolve<ISettingManager>().StoreUrl + "images/thumbs/" + localFilename;
            else
                url = CommonHelper.GetStoreLocation() + "images/thumbs/" + localFilename;
            return url;


в процедуре public string GetDefaultPictureUrl(PictureTypeEnum defaultPictureType, int targetSize)
 return CommonHelper.GetStoreLocation() + "images/thumbs/" + fname;

на
                    if (HttpContext.Current == null)
                        return IoC.Resolve<ISettingManager>().StoreUrl + "images/thumbs/" + fname;
                    else
                        return CommonHelper.GetStoreLocation() + "images/thumbs/" + fname;


в свойстве public string LocalThumbImagePath получение значения заменить на
string path = "";
                if (HttpContext.Current == null)
                    path = AppDomain.CurrentDomain.BaseDirectory + "images\\thumbs";
                else
                    path = HttpContext.Current.Request.PhysicalApplicationPath + "images\\thumbs";
                return path;

и в свойстве public string LocalImagePath получение значения заменить на
string path = "";
                if (HttpContext.Current == null)
                    path = AppDomain.CurrentDomain.BaseDirectory + "images";
                else
                    path = HttpContext.Current.Request.PhysicalApplicationPath + "images";
                return path;


в процедуре public string GetDefaultPictureUrl(PictureTypeEnum defaultPictureType, int targetSize)
string relPath = CommonHelper.GetStoreLocation() + "images/" + defaultImageName;

заменить на
string relPath = "";
            if (HttpContext.Current == null)
                relPath = IoC.Resolve<ISettingManager>().StoreUrl + "images/" + defaultImageName;
            else
                relPath = CommonHelper.GetStoreLocation() + "images/" + defaultImageName;



ну и самое последнее - в web.config добавить строку в раздел <ScheduleTasks>
      <Thread seconds="60">
<task name="GeneratYML" type="NopSolutions.NopCommerce.BusinessLogic.Utils.GenerateYMLTask, Nop.BusinessLogic" enabled="true" stopOnError="false" timeGenerate="14:00:00"/>

где timeGenerate - время запуска генерирования прайса


ps: забыл добавить - это все делалось в версии 1.9
pps: исправление описания -
в название товара подставляется префикс - имя категории в единственном числе - он находится в настройках магазина, название настройки для префикса - YMLPrices.Prefix+название страницы с категорией в поле SEName
0
8 years ago
#333 Цитировать
pasha2100 wrote:
код класса:
[code]//------------------------------------------------------------------------------
// The contents of this file are subject to the nopCommerce Public License Version 1.0 ("License"); you may not use this file except in compliance with the License.

а можно на почту: [email protected]
0
7 years ago
#393 Цитировать
Да яндекс-марекет это нужное дело! самому забивать это Кошмар. Попробую воспользоваться данным решением.
0