carono/yii2-1c-exchange

Yii2 module for the exchange of goods and documents from 1C

Installs: 16 710

Dependents: 0

Suggesters: 0

Security: 0

Stars: 74

Watchers: 11

Forks: 26

Open Issues: 6

Type:yii2-extension

0.3.1 2020-09-08 12:58 UTC

This package is auto-updated.

Last update: 2024-03-12 21:56:29 UTC


README

Scrutinizer Code Quality Latest Stable Version Total Downloads License

Введение

Что это за модуль, и какие задачи он должен выполнять?

Установка этого модуля, должна упрощать интеграцию 1С в ваш сайт.

Модуль содержит набор интерфейсов, которые необходимо реализовать, чтобы получить возможность обмениваться товарами и документами с 1С. Предполагается, что у Вас есть 1С:Предприятие 8, Управление торговлей", редакция 11.3, версия 11.3.2 на платформе 8.3.9.2033. Если у вас версия конфигурации ниже, то скорее всего модуль все равно будет работать, т.к. по большей части, обмен с сайтами сильно не меняется в 1С от версии к версии.

После подключения модуля к вашему проекту, вы можете получить доступ к текущей документации по ссылке /exchange/article/index

Подключение модуля

1. Подключаем пакет через компосер

composer require carono/yii2-1c-exchange

2. Подключаем модуль в конфиге приложения

'modules' => [
    'exchange' => [
        'class' => \carono\exchange1c\ExchangeModule::class
    ]
]

3. Если используете apache как веб сервер, не забудьте создать и настроить .htaccess в web директории

Свойства модуля обмена

Свойство По умолчанию Описание
productClass null Класс для продукта
offerClass null Класс для предложения
documentClass null Класс для документа (заказа)
groupClass null Класс для группы продуктов
partnerClass null Класс контрагента (пользователя/клиента)
warehouseClass null Класс для склада (не используется, может быть удалён)
exchangeDocuments false Обмен заказами
debug false Режим отладки, данные сохраняются в tmpDir
useZip true Использовать архивы при обмене, если доступны
tmpDir @runtime/1c_exchange Папка для временных файлов
validateModelOnSave false При сохранении товара, используем валидацию или нет (может быть удалено)
timeLimit 1800 Время выполнения скрипта (set_time_limit)
memoryLimit null Ограничение памяти при обмене (memory_limit)
bootstrapUrlRule true Автоматически подключать правило для роутинга 1c_exchange.php
appendRule false Добавлять правило роутинга в конец
auth null Фукнция для авторизации

Настройка 1С

1. Устанавливаем 1С:Предприятие 8 Управление торговлей, Управление торговлей", редакция 11.3, версия 11.3.2 (магнитная ссылка), платформа 8.3.9.2033 (и выше)

Настройки будут производиться на демо версии.

2. Переходим в настройки синхронизации данных, через пункт НСИ и администрирование, или через поиск

68747470733a2f2f7261772e6769746875622e636f6d2f6361726f6e6f2f796969322d31632d65786368616e67652f484541442f66696c65732f61727469636c65732f3130302f633238313864613936322d736c696465312e706e67

3. Переходим в узлы обмена с сайтами

68747470733a2f2f7261772e6769746875622e636f6d2f6361726f6e6f2f796969322d31632d65786368616e67652f484541442f66696c65732f61727469636c65732f3130302f333064653062646331382d736c696465322e706e67

4. Создаём новый узел, и заполняем данные

  • Наименование
  • Выгрузка товаров
  • Адрес сайта, указываем ваш сайт/1c_exchange.php
  • Логин и пароль от пользователя, от чьего имени будем выгружать товары (настройка авторизации)

68747470733a2f2f7261772e6769746875622e636f6d2f6361726f6e6f2f796969322d31632d65786368616e67652f484541442f66696c65732f61727469636c65732f3130302f646366333636333962322d323031382d30322d323631392d35352d34312e706e67

Настройка авторизации

Авторизация в модуле реализована через поведение \yii\filters\auth\HttpBasicAuth

'modules' => [
    'exchange' => [
        'class' => \carono\exchange1c\ExchangeModule::class,
        'auth' => function ($username, $password) {
            if ($user = \app\models\User::findByUsername($username)) {
                if ($user->validatePassword($password)) {
                    return $user;
                }
            }
            return false;
        }
     ]
],


groupClass Группа продуктов

Настройка модуля, указываем класс модели работы группы

[
    'exchange' => [
        'class' => \carono\exchange1c\ExchangeModule::class,
        'groupClass' => \app\models\Group::class,
    ]
]

Миграция, создаём группу, для хранения продукции, должна быть древовидная структура с неограниченной вложенностью, рекомендуется использовать nested sets , но для примера используем более простой пример

$this->createTable('{{%group}}', [
    'id' => $this->primaryKey(),
    'name' => $this->string()->comment('Наименование группы'),
    'parent_id' => $this->integer()->comment('Родительская группа'),
    'accounting_id' => $this->string()->comment('Код в 1С')->unique(),
]);

Список интерфейсов, которые необходимо реализовать здесь

createTree1c

public static function createTree1c($groups)

В функции createTree1c нам требуется реализовать создаение всего дерева продуктов родитель->потомок. Метод вызывается только один раз перед началом импорта, поэтому в этой функции нужно создать всё дерево групп полностью.

<?php
/**
 * This class is generated using the package carono/codegen
 */
namespace app\models;
use carono\exchange1c\interfaces\GroupInterface;
/**
 * This is the model class for table "group".
 */
class Group extends base\Group implements GroupInterface
{
    /**
     * Возвращаем имя поля в базе данных, в котором хранится ID из 1с
     *
     * @return string
     */
    public static function getIdFieldName1c()
    {
        return 'accounting_id';
    }
    /**
     * Создание дерева групп
     * в параметр передаётся массив всех групп (import.xml > Классификатор > Группы)
     * $groups[0]->parent - родительская группа
     * $groups[0]->children - дочерние группы
     *
     * @param \Zenwalker\CommerceML\Model\Group[] $groups
     * @return void
     */
    public static function createTree1c($groups)
    {
        foreach ($groups as $group) {
            self::createByML($group);
            if ($children = $group->getChildren()) {
                self::createTree1c($children);
            }
        }
    }
    /**
     * Создаём группу по модели группы CommerceML
     * проверяем все дерево родителей группы, если родителя нет в базе - создаём
     *
     * @param \Zenwalker\CommerceML\Model\Group $group
     * @return Group|array|null
     */
    public static function createByML(\Zenwalker\CommerceML\Model\Group $group)
    {
        /**
         * @var \Zenwalker\CommerceML\Model\Group $parent
         */
        if (!$model = Group::findOne(['accounting_id' => $group->id])) {
            $model = new self;
            $model->accounting_id = $group->id;
        }
        $model->name = $group->name;
        if ($parent = $group->getParent()) {
            $parentModel = self::createByML($parent);
            $model->parent_id = $parentModel->id;
            unset($parentModel);
        } else {
            $model->parent_id = null;
        }
        $model->save();
        return $model;
    }
}

Протестировать вашу реализацию, можно здесь

productClass Модель продукта

Продукт - моделью продукта является сам товар, картинки, его свойства и реквизиты, но не остаток или цена.

Для тех разработчиков, которые не очень хорошо знакомы с концепцией хранения данных в 1С, нужно дополнительно пояснение. В 1С существуют продукты и предложения. Продукт эта сама сущность товара, предложение, это то что можно продать, т.е. предложения и учавствуют в продажах.

Пример:

Туфли лабутены Модель X - это продукт, у него есть картинки, различные реквизиты (производитель, цвет, материал и т.д.), которые присущи данному продукту.

Туфли лабутены Модель X, размер 32, за 20000р - это предложение, от одного продукта может быть несколько предложений, с разными характеристиками, такими как размер, и разными ценами, на каждое предложение может быть свой остаток.

Настройка

Добавляем в настройки модуля вашу модель для продукта 'productClass' => \app\models\Product::class

[
    'exchange' => [
        'class' => \carono\exchange1c\ExchangeModule::class,
        'groupClass' => \app\models\Group::class,
        'productClass' => \app\models\Product::class,
    ]
]

Интерфейсы

В вашей модели имплементируем интерфейс carono\exchange1c\interfaces\ProductInterface

setRequisite1c

public function setRequisite1c($name, $value)

Установка реквизитов для продукта. Список резвизитов находится в import.xml > Каталог > Товары > Товар > ЗначенияРеквизитов > ЗначениеРеквизита

Для хранения реквизитов, потребуется таблица реквизитов, а также сводная таблица продут+реквизит+значение

68747470733a2f2f7261772e6769746875622e636f6d2f6361726f6e6f2f796969322d31632d65786368616e67652f484541442f66696c65732f61727469636c65732f3130302f303438396430616535622d7265717569736974652e706e67

public function setRequisite1c($name, $value)
{
    if (!$requisite = Requisite::findOne(['name' => $name])) {
        $requisite = new Requisite();
        $requisite->name = $name;
        $requisite->save();
    }
    $this->addPivot($requisite, PvProductRequisite::class, ['value' => $value]);
}

setGroup1c

public function setGroup1c($group)

Установка группы, где находится продукт. Все группы у вас уже должны быть сохранены в базе, т.к. ранее вызывался метод \carono\exchange1c\interfaces\GroupInterface::createTree1c, и все дерево групп уже создано Вами, а значит можно не проверять на существование группы.

    public function setGroup1c($group)
    {
        $id = Group::find()->select(['id'])->andWhere(['accounting_id' => $group->id])->scalar();
        $this->updateAttributes(['group_id' => $id]);
    }

createProperties1c

public static function createProperties1c($properties)

Функция вызывается один раз при импорте, в ней необходимо создать все свойста и значения свойств.

    /**
     * @param PropertyCollection $properties
     * @return mixed
     */
    public static function createProperties1c($properties)
    {
        /**
         * @var \Zenwalker\CommerceML\Model\Property $property
         */
        foreach ($properties as $property) {
            $propertyModel = Property::createByMl($property);
            foreach ($property->getAvailableValues() as $value) {
                if (!$propertyValue = PropertyValue::findOne(['accounting_id' => $value->id])) {
                    $propertyValue = new PropertyValue();
                    $propertyValue->name = (string)$value->Значение;
                    $propertyValue->property_id = $propertyModel->id;
                    $propertyValue->accounting_id = (string)$value->ИдЗначения;
                    $propertyValue->save();
                    unset($propertyValue);
                }
            }
        }
    }

68747470733a2f2f7261772e6769746875622e636f6d2f6361726f6e6f2f796969322d31632d65786368616e67652f484541442f66696c65732f61727469636c65732f3130302f333064376430383639632d70726f70657274792e706e67

setProperty1c

public function setProperty1c($property)

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

Все свойства уже должны быть заполнены, т.к. ранее выполнялся createProperties1c($properties), поэтому можем искать свойства и значения по id.

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

* в этой фукнции используется трейт из пакета carono/yii2-migrate

    
    /**
     * $property - Свойство товара (import.xml > Классификатор > Свойства > Свойство)
     * $property->value - Разыменованное значение (string) (import.xml > Классификатор > Свойства > Свойство > Значение)
     * $property->getValueModel() - Данные по значению, Ид значения, и т.д (import.xml > Классификатор > Свойства > Свойство > ВариантыЗначений > Справочник)
     *
     * @param MlProperty $property
     * @return void
     */
    public function setProperty1c($property)
    {
        $propertyModel = Property::findOne(['accounting_id' => $property->id]);
        $propertyValue = $property->getValueModel();
        if ($propertyAccountingId = (string)$propertyValue->ИдЗначения) {
            $value = PropertyValue::findOne(['accounting_id' => $propertyAccountingId]);
            $attributes = ['property_value_id' => $value->id];
        } else {
            $attributes = ['value' => $propertyValue->value];
        }
        $this->addPivot($propertyModel, PvProductProperty::class, $attributes);
    }


68747470733a2f2f7261772e6769746875622e636f6d2f6361726f6e6f2f796969322d31632d65786368616e67652f484541442f66696c65732f61727469636c65732f3130302f616463343531363164642d70726f706572746965732e706e67

addImage1c

public function addImage1c($path, $caption)

В этой фукнции мы получаем абсолютный путь до картинки и название изрбражения (для alt аттрибута)

* в этой фукнции используются трейт из пакета carono/yii2-migrate и управление файлами из carono/yii2-file-upload

    /**
     * @param string $path
     * @param string $caption
     * @return mixed
     */
    public function addImage1c($path, $caption)
    {
        if (!$this->getImages()->andWhere(['md5' => md5_file($path)])->exists()) {
            $this->addPivot(FileUpload::startUpload($path)->process(), PvProductImage::class, ['caption' => $caption]);
        }
    }

getGroup1c

public function getGroup1c()

Получаем группу, где находится текущий продукт, группа должна наследовать интерфейс \carono\exchange1c\interfaces\GroupInterface

    /**
     * @return GroupInterface
     */
    public function getGroup1c()
    {
        return $this->group;
    }

getOffer1c

public function getOffer1c($offer)

В эту фукнцию отправляется xml данные предложения из файла, необходимо создать или найти вашу модель предложения (интерфейс \carono\exchange1c\interfaces\OfferInterface) и вернуть в результате.

    /**
     * @param \Zenwalker\CommerceML\Model\Offer $offer
     * @return OfferInterface
     */
    public function getOffer1c($offer)
    {
        $offerModel = Offer::createByMl($offer);
        $offerModel->product_id = $this->id;
        if ($offerModel->getDirtyAttributes()) {
            $offerModel->save();
        }
        return $offerModel;
    }

Пример парсинга предложения

    
class Offer extends BaseOffer implements OfferInterface {   
    /**
     * @param MlOffer $offer
     * @return Offer
     */
    public static function createByMl($offer)
    {
        if (!$offerModel = self::findOne(['accounting_id' => $offer->id])) {
            $offerModel = new self;
            $offerModel->name = (string)$offer->name;
            $offerModel->accounting_id = (string)$offer->id;
        }
        $offerModel->remnant = (string)$offer->Количество;
        return $offerModel;
    }
}

createModel1c

public static function createModel1c($product)

В этой фукнции мы должны найти или создать новый продукт и вернуть вашу модель.

    /**
     * @param \Zenwalker\CommerceML\Model\Product $product
     * @return self
     */
    public static function createModel1c($product)
    {
        if (!$model = Product::findOne(['accounting_id' => $product->id])) {
            $model = new Product();
            $model->accounting_id = $product->id;
        }
        $model->name = $product->name;
        $model->description = (string)$product->Описание;
        $model->article = (string)$product->Артикул;
        $model->save();
        return $model;
    }

offerClass Модель предложения

Предложение - модель товара, которая учавствует в продажах, у нее есть остаток и набор цен

Настройка

Добавляем в настройки модуля вашу модель для предложения 'offerClass' => \app\models\Offer::class

[
    'exchange' => [
        'class' => \carono\exchange1c\ExchangeModule::class,
        'groupClass' => \app\models\Group::class,
        'productClass' => \app\models\Product::class,
        'offerClass' => \app\models\Offer::class,
    ]
]

Интерфейсы

В вашей модели имплементируем интерфейс carono\exchange1c\interfaces\OfferInterface

getGroup1c

public function getGroup1c()

Здесь нам необходимо получить группу, где находится предложение. Берем её через связь с продуктом.

* Вероятно в будущем будет заменено на getProduct1c()

    /**
     * @return GroupInterface
     */
    public function getGroup1c()
    {
        return $this->product->group;
    }

createPriceTypes1c

public static function createPriceTypes1c($types)

В этом методе необходимо создать все типы цен, фукнция вызывается один раз. Тип цены содержит название (розничная, оптовая и др.), а так же название валюты.

68747470733a2f2f7261772e6769746875622e636f6d2f6361726f6e6f2f796969322d31632d65786368616e67652f484541442f66696c65732f61727469636c65732f3130302f343765313766333935302d7072696365747970652e706e67

    /**
     * @param $types
     * @return void
     */
    public static function createPriceTypes1c($types)
    {
        foreach ($types as $type) {
            PriceType::createByMl($type);
        }
    }

Пример реализации создания типа

class PriceType extends BasePriceType
{
    /**
     * @param Simple $type
     * @return PriceType
     */
    public static function createByMl($type)
    {
        if (!$priceType = self::findOne(['accounting_id' => $type->id])) {
            $priceType = new self;
            $priceType->accounting_id = $type->id;
        }
        $priceType->name = $type->name;
        $priceType->currency = (string)$type->Валюта;
        if ($priceType->getDirtyAttributes()) {
            $priceType->save();
        }
        return $priceType;
    }
}

setPrice1c

public function setPrice1c($price)

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

68747470733a2f2f7261772e6769746875622e636f6d2f6361726f6e6f2f796969322d31632d65786368616e67652f484541442f66696c65732f61727469636c65732f3130302f663463613066303664632d7072696365732e706e67

    /**
     * offers.xml > ПакетПредложений > Предложения > Предложение > Цены
     *
     * Цена товара,
     * К $price можно обратиться как к массиву, чтобы получить список цен (Цены > Цена)
     * $price->type - тип цены (offers.xml > ПакетПредложений > ТипыЦен > ТипЦены)
     *
     * @param \Zenwalker\CommerceML\Model\Price $price
     * @return void
     */
    public function setPrice1c($price)
    {
        $priceType = PriceType::findOne(['accounting_id' => $price->getType()->id]);
        $priceModel = Price::createByMl($price, $this, $priceType);
        $this->addPivot($priceModel, PvOfferPrice::class);
    }

Пример создания цены

class Price extends BasePrice
{
    /**
     * @param MlPrice $price
     * @param Offer $offer
     * @param PriceType $type
     * @return Price
     */
    public static function createByMl($price, $offer, $type)
    {
        if (!$priceModel = $offer->getPrices()->andWhere(['type_id' => $type->id])->one()) {
            $priceModel = new self();
        }
        $priceModel->value = $price->cost;
        $priceModel->performance = $price->performance;
        $priceModel->currency = $price->currency;
        $priceModel->rate = $price->rate;
        $priceModel->type_id = $type->id;
        $priceModel->save();
        return $priceModel;
    }
}

setSpecification1c

public function setSpecification1c($specification)

Характеристики для предложения являются отдельной сущностью и должны соединятся через сводную таблицу.

68747470733a2f2f7261772e6769746875622e636f6d2f6361726f6e6f2f796969322d31632d65786368616e67652f484541442f66696c65732f61727469636c65732f3130302f623130626266343930382d73706563696669636174696f6e732e706e67

    /**
     * offers.xml > ПакетПредложений > Предложения > Предложение > ХарактеристикиТовара > ХарактеристикаТовара
     *
     * Характеристики товара
     * $name - Наименование
     * $value - Значение
     *
     * @param \Zenwalker\CommerceML\Model\Simple $specification
     * @return void
     */
    public function setSpecification1c($specification)
    {
        $specificationModel = Specification::createByMl($specification);
        $this->addPivot($specificationModel, PvOfferSpecification::class, ['value' => (string)$specification->Значение]);
    }

Пример парсинга характеристики

class Specification extends BaseSpecification
{
    public static function createByMl($specification)
    {
        if (!$specificationModel = self::findOne(['accounting_id' => $specification->id])) {
            $specificationModel = new self;
            $specificationModel->name = $specification->name;
            $specificationModel->accounting_id = $specification->id;
            $specificationModel->save();
        }
        return $specificationModel;
    }
}

partnerClass Модель пользователя

Данный интерфейс на данный момент требуется только для работы обмена документов. Единственное что нужно реализовать, это общий метод public function getExportFields1c, который описывает поля для сериализации в xml при обмене. Необходимо возвращать массив, где ключ, это тег в xml, а значение - ваши данные. Все поддерживаемые стандартом данные можно найти в спецификации, исчерпывающую информацию лучше смотреть в xsd файлах, для этого потребуется visual studio, т.к. в официальных pdf файлах присутствуют неточности. Чуть подробнее о методе можно почитать здесь

Настройка

Добавляем в настройки модуля вашу модель для предложения 'partnerClass' => \app\models\Partner::class

[
    'exchange' => [
        'class' => \carono\exchange1c\ExchangeModule::class,
        'groupClass' => \app\models\Group::class,
        'productClass' => \app\models\Product::class,
        'offerClass' => \app\models\Offer::class,
        'partnerClass' => \app\models\Partner::class, 
    ]
]

Интерфейсы

В вашей модели имплементируем интерфейс carono\exchange1c\interfaces\PartnerInterface

    public function getExportFields1c($context = null)
    {
        return [
            'Ид' => 'id',
            'Наименование' => 'username',
            'ПолноеНаименование' => 'full_name',
            'Фамилия' => 'surname',
            'Имя' => 'name',
        ];
    }

documentClass Модель документа

Документ в 1С, он же заказ на сайте. У документа дожны быть связи на предложения через сводную таблицу. Суммы желательно указывать и в сводной таблице и в самом заказе, чтобы не расчитывать её в динамике т.к. цена на предложение может поменятся и клиенту в итоге поступит счет на другую сумму.

Настройка

Добавляем в настройки модуля вашу модель для предложения 'documentClass' => \app\models\Document::class

[
    'exchange' => [
        'class' => \carono\exchange1c\ExchangeModule::class,
        'groupClass' => \app\models\Group::class,
        'productClass' => \app\models\Product::class,
        'offerClass' => \app\models\Offer::class,
        'partnerClass' => \app\models\Partner::class, 
        'documentClass' => \app\models\Document::class, 
    ]
]

Интерфейсы

В вашей модели имплементируем интерфейс carono\exchange1c\interfaces\DocumentInterface


68747470733a2f2f7261772e6769746875622e636f6d2f6361726f6e6f2f796969322d31632d65786368616e67652f484541442f66696c65732f61727469636c65732f3130302f373964353730613832382d6f726465722e706e67

findDocuments1c

public static function findDocuments1c()

Получение всех подготовленных документов (заказов). В этой функции необходимо возвращать все документы, которые готовы для импорта в 1С.

Необходимо обратить внимение, 1С заменит существующие документы, если при следующем импорте они будут в этой фукнции, поэтому по завершению импорта, необходимо выставлять флаг или менять статус, чтобы при повторном импорте этих документов уже небыло. Это значит, что если вы создали документы в 1С, начали с ними работать, двигать по статусам или наполнять данными, то при следующем импорте, они перезапишутся и придется всё начинать сначала. Как обновлять уже экспортированные данные, можно почитать в разделе с событиями.

    /**    
     * @return DocumentInterface[]
     */
    public static function findDocuments1c()
    {
        return self::find()->andWhere(['status_id' => 2])->all();
    }

getOffers1c

public function getOffers1c()

Получаем все предложения, которые были добавлены в этот документ (заказ)

    /**
     * @return OfferInterface[]
     */
    public function getOffers1c()
    {
        return $this->offers;
    }

getRequisites1c

public function getRequisites1c()

Еще сам не уверен где эта фукнция используется, пока не обязательно к заполнению.

getPartner1c

public function getPartner1c()

Необходимо вернуть пользователя, который сделал заказ, класс должен имплементировать \carono\exchange1c\interfaces\PartnerInterface

    /**
     * Получаем контрагента у документа
     *
     * @return PartnerInterface
     */
    public function getPartner1c()
    {
        return $this->user;
    }

warehouseClass Модель склада

На данный момент не используется

getExportFields1c

public function getExportFields1c($context = null)

Объекты, которые создаются в 1С, имеют интерфейс \carono\exchange1c\interfaces\ExportFieldsInterface с этим методом. В этом методе мы должны вернуть массив, который сериализуется в xml для 1С. Ключ массива, это название тега в xml.

Значение может иметь разные типы, аналогично с функцией fields для rest api.

string - передаём название аттрибута, если такого аттрибута нет, вернется эта строка

Closure - передаём фукцнию function($model)

array - с помощью массива можно кастомизировать xml, или когда требуется создать несколько элементов. В таком массиве есть несколько зарезервированных ключей: @content - тело тега, @name - имя тега, @attributes - массив аттрибутов.

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

Входной параметр $context - это контекст, в рамках которого необходимо сериализовать объект, например для предложения, контектом будет заказ.

Пример сериализации для контрагента в документе.

    public function getExportFields1c($context = null)
    {
        return [
            'Ид' => 'id',
            'Наименование' => 'login',
            'ПолноеНаименование' => 'full_name',
            'Фамилия' => 'surname',
            'Имя' => 'name',
            'Контакты' => [
                [
                    '@name' => 'Контакт',
                    'Тип' => 'Почта',
                    'Значение' => $this->email,
                ],
                [
                    '@name' => 'Контакт',
                    'Тип' => 'ТелефонРабочий',
                    'Значение' => $this->phone,
                ],
            ],
        ];
    }

Результат

<Контрагенты>
    <Контрагент>
        <Ид>13</Ид>
        <Наименование>info@carono.ru</Наименование>
        <ПолноеНаименование>Иванов Иван Иванович</ПолноеНаименование>
        <Контакт>
            <Тип>Почта</КонтактВид>
            <Значение>info@carono.ru</Значение>
        </Контакт>
        <Контакт>
            <Тип>ТелефонРабочий</КонтактВид>
            <Значение>+8(908)123-45-67</Значение>
        </Контакт>
    </Контрагент>
</Контрагенты>

getIdFieldName1c

public static function getIdFieldName1c()

У всех сущностей в 1С имеется уникальный идентификатор Ид, необходимо вернуть название поля, в котором будет хранится это значение.

    /**
     * Возвращаем имя поля в базе данных, в котором хранится ID из 1с
     *
     * @return string
     */
    public static function getIdFieldName1c()
    {
        return 'accounting_id';
    }

setRaw1cData

public function setRaw1cData($cml, $object)

Если по каким то причинам файлы import.xml или offers.xml были модифицированы
и какие то данные не попадают в парсер, в самом начале вызывается данный метод, в
$object и $cml можно получить все данные для ручного парсинга.

К сожалению на данный момент, не все сущности проходят этот метод.

Описание протокола обмена

Источник: http://v8.1c.ru/edi/edi_stnd/131/

Данный открытый протокол разработан компаниями "1С" и "1С-Битрикс".

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

Функционально обмен делится на два блока:

Первый блок обеспечивает публикацию на сайте каталога номенклатурных позиций и данных. Второй блок необходим для передачи с сайта в систему "1С:Предприятие" информации о заказах интернет-магазина, и дальнейшую синхронизацию статусов и параметров заказов.

В обоих случаях инициатором обмена выступает система "1С:Предприятие". Обмен электронными документами осуществляется в соответствии с правилами и форматами, описанными в стандарте CommerceML 2.

При инициализации взаимодействия устанавливается HTTP соединение. Система "1С:Предприятие" запрашивает у сайта необходимые параметры, такие, как максимальный объем пакета, поддержка сжатия и др.. На основании этих данных система 1С:Предприятие формирует XML сообщения и передает их на сайт.

687474703a2f2f76382e31632e72752f726f6d62696b2e676966Выгрузка на сайт

Данные для публикации на сайте выгружаются одним пакетом.

A. Начало сеанса

Выгрузка данных начинается с того, что система "1С:Предприятие" отправляет http-запрос следующего вида:
http://<сайт>/<путь> /1c_exchange.php?type=catalog&mode=checkauth.

В ответ система управления сайтом передает системе «1С:Предприятие» три строки (используется разделитель строк "\n"):

  • слово "success";
  • имя Cookie;
  • значение Cookie.

Примечание. Все последующие запросы к системе управления сайтом со стороны "1С:Предприятия" содержат в заголовке запроса имя и значение Cookie.

B. Запрос параметров от сайта

Далее следует запрос следующего вида:
http://<сайт>/<путь> /1c_exchange.php?type=catalog&mode=init

В ответ система управления сайтом передает две строки:

1. zip=yes, если сервер поддерживает обмен в zip-формате - в этом случае на следующем шаге файлы должны быть упакованы в zip-формате
или
zip=no - в этом случае на следующем шаге файлы не упаковываются и передаются каждый по отдельности.

2. file_limit=<число>, где <число> - максимально допустимый размер файла в байтах для передачи за один запрос. Если системе "1С:Предприятие" понадобится передать файл большего размера, его следует разделить на фрагменты.

C. Выгрузка на сайт файлов обмена

Затем "1С:Предприятие" запросами с параметрами вида
http://<сайт>/<путь> /1c_exchange.php?type=catalog&mode=file&filename=<имя файла>
выгружает на сайт файлы обмена в формате CommerceML 2, посылая содержимое файла или его части в виде POST.

В случае успешной записи файла система управления сайтом выдает строку "success".

D. Пошаговая загрузка данных

На последнем шаге по запросу из "1С:Предприятия" производится пошаговая загрузка данных по запросу с параметрами вида http://<сайт>/<путь> /1c_exchange.php?type=catalog&mode=import&filename=<имя файла>

Во время загрузки система управления сайтом может отвечать в одном из следующих вариантов.

1. Если в первой строке содержится слово "progress" - это означает необходимость послать тот же запрос еще раз. В этом случае во второй строке будет возвращен текущий статус обработки, объем загруженных данных, статус импорта и т.д.

2. Если в ответ передается строка со словом "success", то это будет означать сообщение об успешном окончании обработки файла.

Примечание. Если в ходе какого-либо запроса произошла ошибка, то в первой строке ответа системы управления сайтом будет содержаться слово "failure", а в следующих строках - описание ошибки, произошедшей в процессе обработки запроса.
Если произошла необрабатываемая ошибка уровня ядра продукта или sql-запроса, то будет возвращен html-код.

Примеры файлов выгрузки

Сведения о товарах в формате XML.
Сведения о ценах в формате XML.

687474703a2f2f76382e31632e72752f726f6d62696b2e676966Обмен информацией о заказах

Заказы, оформленные на сайте, загружаются в систему "1С:Предприятие".

Последовательность действий при работе с заказом

1. Заказ оформляется на сайте

2. При передаче в систему "1С:Предприятие" в заказе устанавливается категория "Заказ с сайта".
При формировании заказа в системе "1С:Предприятие" записываются номер и дата заказа, с которыми он оформлен на сайте. Поиск контрагента осуществляется по ИНН или наименованию, в зависимости от указанных настроек.

3. При загрузке заказа производится поиск договора с контрагентом. Договор ищется среди существующих договоров с клиентом, с признаком ведения взаиморасчетов по заказам (по указанной в настройках загрузки Организации). Если не находится ни один договор, то создается новый.

4. При загрузке заказа загружаются все его свойства, переданные с сайта. Свойства ищутся в системе "1С:Предприятие" по наименованию. Если с таким наименованием свойства нет, то заводится новое свойство со значениями типа строка или число.

5. Заказ может модифицироваться в системе "1С:Предприятие", при этом его изменения будут выгружаться на сайт

6. Если заказ оплачивается или отгружается в системе "1С:Предприятие", то состояния заказа по оплате и по отгрузке выгружаются на сайт только при полном выполнении операции (полной оплате и полной отгрузке). До этого момента заказ считается не оплаченным и не отгруженным.

7. При попытке в системе "1С:Предприятие" изменить заказ, по которому произведена оплата или отгрузка, заказ на сайт не загрузится как измененный. При этом пользователь получит об этом сообщение.

8. После каждой выгрузка заказа на сайт, на стороне сайта определяются значения его категорий (ссылка на категории). Эти значения устанавливаются в системе "1С:Предприятие" так, как они присвоены заказу на сайте

A. Начало сеанса

Выгрузка данных начинается с того, что система "1С:Предприятие" отправляет http-запрос следующего вида:
http://<сайт>/<путь> /1c_exchange.php?type=sale&mode=checkauth.

В ответ система управления сайтом передает системе «1С:Предприятие» три строки (используется разделитель строк "\n"):

  • слово "success";
  • имя Cookie;
  • значение Cookie.

Примечание. Все последующие запросы к системе управления сайтом со стороны "1С:Предприятия" содержат в заголовке запроса имя и значение Cookie.

B. Уточнение параметров сеанса

Далее следует запрос следующего вида:
http://<сайт>/<путь> /1c_exchange.php?type=sale&mode=init

В ответ система управления сайтом передает две строки:

1. zip=yes, если сервер поддерживает обмен в zip-формате - в этом случае на следующем шаге файлы должны быть упакованы в zip-формате
или
zip=no - в этом случае на следующем шаге файлы не упаковываются и передаются каждый по отдельности.

2. file_limit=<число>, где <число> - максимально допустимый размер файла в байтах для передачи за один запрос. Если системе "1С:Предприятие" понадобится передать файл большего размера, его следует разделить на фрагменты.

C. Получение файла обмена с сайта

Затем на сайт отправляется запрос вида
http://<сайт>/<путь> /1c_exchange.php?type=sale&mode=query.

Сайт передает сведения о заказах в формате CommerceML 2. В случае успешного получения и записи заказов "1С:Предприятие" передает на сайт запрос вида
http://<сайт>/<путь> /1c_exchange.php?type=sale&mode=success

D. Отправка файла обмена на сайт

Затем система "1С:Предприятие" отправляет на сайт запрос вида
http://<сайт>/<путь> /1c_exchange.php?type=sale&mode=file&filename=<имя файла>
,
который загружает на сервер файл обмена, посылая содержимое файла в виде POST.

В случае успешной записи файла система управления сайтом передает строку со словом "success". Дополнительно на следующих строчках могут содержаться замечания по загрузке.

Примечание. Если в ходе какого-либо запроса произошла ошибка, то в первой строке ответа системы управления сайтом будет содержаться слово "failure", а в следующих строках - описание ошибки, произошедшей в процессе обработки запроса.
Если произошла необрабатываемая ошибка уровня ядра продукта или sql-запроса, то будет возвращен html-код.

Примеры файлов обмена информацией

Заказ на сайт в формате XML.
Заказ с сайта в формате XML.

Представленный протокол используется для интеграции системы "1С:Предприятие" с системами "1С-Битрикс: Управление сайтом", "UMI.CMS" и другими.

Тестирование и поиск ошибок

Раздел находится в разработке

События модуля

beforeUpdateProduct

Событие перед началом парсинга продукта

afterUpdateProduct

Событие после парсинга продукта

beforeUpdateOffer

Событие перед началом парсинга предложения

afterUpdateOffer

Событие после парсинга предложения

beforeProductSync

Событие перед началом парсинга всех продуктов

afterProductSync

Событие после парсинга всех продуктов

beforeOfferSync

Событие перед началом парсинга всех предложений

afterOfferSync

Событие после парсинга всех предложений

afterFinishUploadFile

Событие, которое вызывается после загрузки архива или xml файла от 1С на ваш сайт

afterExportOrders

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