readme.md

    Протокол обмена информацией с 1С

    • Установка: composer require mavsan/laravel-1c-protocol
    • Для версии Laravel ниже 11 используйте ветку (тестировалось только для версии Laravel 7!) v7: composer require mavsan/laravel-1c-protocol:v7.x-dev. Там же инструкция в файле readme.md.
    • Для версии Laravel 5 используйте ветку v5: composer require mavsan/laravel-1c-protocol:v5.x-dev. Там же инструкция в файле readme.md.
    • Зарегистрировать сервис-провайдер в config/app.php (для Laravel 5.4 и ниже)
    /*
     * Протокол обмена информацией с 1С
     */
    Mavsan\LaProtocol\Providers\ProtocolProvider::class,
    
    • Опубликовать конфигурацию и изменить при необходимости: php artisan vendor:publish --tag=la1CProtocolConfig
    • Проверить параметр middleware файла конфигурации. Значением этого параметра должен быть массив. В это массиве должен быть указан промежуточный слой (middleware) или группа промежуточных слоев, в котором как минимум - стартует сессия \Illuminate\Session\Middleware\StartSession::class. Если сессия не стартует - ничего работать не будет. Важно в этой-же группе middleware НЕ ДОЛЖНО быть класса шифрования куки \App\Http\Middleware\EncryptCookies::class или любого другого способа шифрования куки, т.к. в 1С передается id сессии как есть или если по приходу от 1С будет произведена попытка шифрации/дешифрации - ничего хорошего из этого не будет.
    • При необходимости осуществлять выгрузку информации с сайта в 1С можно поставить готовый пакет с минимально необходимым функционалом (которого, обычно, хватает с головой) composer require arsengoian/commerce-ml, см. раздел Отправка данных в 1С.
    • В случае, если необходима выгрузка данных о заказах в 1С, но чем-то не устраивает пакет, рекомендуемый выше (например в 1С какой-то уникальный модуль обмена, который не распознает версию XML или еще что, это-же 1С) - можете написать свой обработчик выгрузки, см. раздел Отправка данных в 1С.
    • Создать модель, которая будет обрабатывать пришедшие данные. Модель должна реализовывать интерфейс \Mavsan\LaProtocol\Interfaces\Import. Если на стороне 1С используется модуль обмена 1С Битрикс необходимо, чтобы эта модель так-же реализовывала интерфейс \Mavsan\LaProtocol\Interfaces\ImportBitrix. Контроллер обмена в нужный момент создает эту модель и вызывает метод import, передавая туда полный путь к файлу, который необходимо обработать. Имя этого файла присылает 1С. В файле конфигурации прописать эту модель:
    'catalogWorkModel'   => \App\Model::class,
    

    Если на стороне 1С используется модуль обмена 1C Битрикс

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

    • isBitrixOn1C=true в этом случае в 1С будет отправляться желаемая версия файла обмена с данными каталога и версия XML с данными о заказах. Собственно только из-за того, что при отправке информации о заказах необходимо отправлять версию файла обмена и был введен этот и другие, касающиеся битрикса параметры. Кроме этого отправляется CSRF.
    • saleXmlVersion версия XML с данными и о заказах, т.к. я не нашел ни какого описания версий 1С Битрикс, то ставлю там то, что предлагают на сайте с описанием протокола, т.е. 2.03. На момент написания этого текста - несмотря на то, что в 1С улетает требование версии 2.03 1C все-равно присылает версию 3.1. Возможно они что-то изменили, но забыли в доку написать или я что-то не правильно понял, нужна помощь.
    • catalogXmlVersion версия XML с данными и о заказах, тоже ставлю, как описано в стандарте - 2.08.

    Как работает

    Сервис провайдер регистрирует роут, с url, указанным в файле конфигурации. 1C стучится по этому ури и отсылает команды, описанные в стандарте.

    Для аутентификации соединения должен быть создан пользовать. Его имя пользователя и пароль присылает 1C. Верификация пользователя производится как обычно: Auth::attempt(['email' => $user, 'password' => $pass]).

    Контроллер обрабатывает эти команды, отсылая требуемые ответы.

    При приеме файлов подчищаются файлы предыдущих обменов, т.к. в протоколе не описана команда, что обмен завершен. Если файл zip - после получения он автоматически распаковывается.

    Как выполнить тестирование

    Внимание! Для тестирования используется пакет madnest/madzipper для работы с ZIP архивом. Если будете использовать предложенный мной тест - необходимо добавить его в ваш composer: composer require madnest/madzipper.

    При необходимости можно протестировать работу обмена, для этого в файле конфигурации пропишите:

    // файл(ы) которые будут отосланы тестом для обработки, эмулируя отправку файлов 1С
    'filesToSendTest'    => ['fileName.zip'],
    // файл(ы) которые требуется обработать моделью после получения, эмулируя отправку команд обработки 1С
    'filesToWorkTest'    => ['import.xml', 'offers.xml'],
    

    Если в вашем файле обмена имеются подпапки, то в filesToWorkTest путь упакованным в архив файлам необходимо указывать с указанием этих подпапок. В общем, структуру пути к файлам, как они запакованы в архиве, указанном в filesToSendTest:

    'filesToWorkTest'    => ['import/import01.xml', 'import/import02.xml', 'offers.xml'],
    

    PhpUnit должен использовать файл phpunit.xml, который идет с этим модулем, или в стандартных тестах в phpunit.xml необходимо прописать путь к тестам этого модуля:

    <testsuites>
        ...
        <testsuite name="1с Protocol">
            <directory suffix="Test.php">./vendor/mavsan/laravel-1c-protocol/src/Tests/Unit</directory>
        </testsuite>
        ...
    </testsuites>
    

    В версию 0.0.8 в файл конфигурации добавлена возможность изменить имя пользователя и пароль для тестов. Если пользователя нет - он создаётся, но не удаляется (актуально для тестов на реальной базе, а не в памяти).

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

    Для полного тестирования лучше всего будет в /tests/Unit создать свой тест, скопировав в него код из
    \Mavsan\LaProtocol\Tests\Unit\CatalogTest (к сожалению, унаследовать нельзя, т.к. в этом случае будет ошибка phpunit: ’This test depends on ’) в методе testImport, в котором выполняется отправка команды обработчику протокола - type=catalig&mode=import, свои проверки лучше всего добавлять после полной обработки каталога:

    public function testImport($session)
    {
        ...
        
        $this->importFiles($files, $session);
        
        // вот тут
        
        return $session;
    }
    

    Обратите внимание, что в каждой итерации метода testImport происходит обновление приложения: $this->refreshApplication();. Если этого не делать, то каждый POST или GET запрос в цикле будет один и тот же для всех обрабатываемых файлов, таким образом имя файла для тестов будет всегда передаваться одно и то же. Обратите внимание, что если вы используете память для тестирования БД, то при создании нового приложения эта БД в памяти будет обнуляться. Поэтому в качестве БД для тестов надо использовать что-то другое. Например, для DB_CONNECTION значение sqlite, а для DB_DATABASE вместо значения :memory: файл для sqlite: sqlite.test и это файл sqlite.test надо предварительно создать.

    Если есть необходимость протестировать работу файла обмена, стоит добавить в тесты несколько разных тестовых методов. Например: testCategories, testProducts, testContargents и т.д. В этом случае тесты могут выглядеть вот так (вместо одного единственного теста testImport):

    #[Depends('testInit')]
    public function testImportProducts()
    {
        $session = $this->testFile(); // отправка обычного файла обмена и получение сессии
        $files = $this->getFilesToWorkTest('goods');
        $this->importFiles($files, $session);
        
        $model = Product::where('id1c', 'ИД товара в 1С')->first();
        $this->assertCount('name', $model->name);
        ...
        
        // проверка удаления/восстановления/изменения
        $session = $this->sendRestoreRemoveExchangeFiles();
        $this->importFiles($files, $session);
        
        $model = Product::where('id1c', 'ИД товара в 1С')->first();
        $this->assertCount('new name', $model->name);
        ...
    }
    
    /**
     * Файлы для обработки. Например, для импорта вам нужны не все файлы, указанные в конфиге, а только их часть:
     * ['folder/goods_1.xml', ..., 'folder/goods_n.xml', 'folder2/clients_1.xml', ..., 'folder2/clients_n.xml', 'folder3/store_1.xml', ..., 'folder3/store_n.xml', ...]
     * а нужны только goods.
     * 
     * @param string $type тип файлов, например goods
     * @return array
     */
    protected function getFilesToWorkTest(string $type = ''): array
    {
        $files = config('protocolExchange1C.filesToWorkTest', []);
    
        if (empty($files)) {
            $files = ['fakeImportProtocolTest.txt'];
        }
    
        if (!empty($files)) {
            $files = collect((array)$files)->filter(fn($file) => str_contains($file, $type))->toArray();
        }
    
        return (array)$files;
    }
    
    // отправка еще одно файла обмена, в котором содержатся измененные данные, для тестов
    protected function sendRestoreRemoveExchangeFiles(): array|Store
    {
        return $this->sendFiles(['storage/test/fileToTest2.zip']);
    }
    

    Отправка данных заказа в 1С

    Для выгрузки данных о заказах можно использовать пакет arsengoian/commerce-ml, который позволяет быстро реализовать выгрузку минимально необходимого количества данных. Это количества данных обычно вполне хватает. Имейте в виду, что этот пакет только формирует ответ в виде XML. Т.е. реализовывать модель, которая будет выбирать данные о заказах и передавать их для формирования ответа в виде XML в библиотеку arsengoian/commerce-ml все равно прийдется.

    Для начала необходимо установить пакет arsengoian/commerce-ml, прописать модель, которая будет подготавливать данные о заказе в файле конфигурации в параметре saleShareModel. Эта модель должна реализовывать интерфейс \Mavsan\LaProtocol\Interfaces\ExportOrders.

    В случае, если функционала не хватает, то в файле конфигурации, в параметре saleShareToXML необходимо указать модель, которая реализует интерфейс \Mavsan\LaProtocol\Interfaces\ExportOrdersSelf. Данная модель не только должна выбрать данные о заказе, но и сформировать XML, который будет отправлен 1С.

    После того, как 1С отправит команду на получение данных о заказах - контроллер проверяет файл конфигурации. Если указана модель в параметре saleShareModel, то будет запущено формирование ответа пакетом arsengoian/commerce-ml.

    Если параметр saleShareModel пуст, то будет создан экземпляр класса, указанный в параметре saleShareToXML. У этого класса будет вызван метод getXML(). Этот метод должен вернуть данные в формате XML, которые и будут отправлены в 1С.

    При этом необходимо учитывать, что параметр saleShareModel имеет больший приоритет, т.е. если там что-то указано, то работа по формированию XML будет вестись пакетом arsengoian/commerce-ml не зависимо от того - прописано ли что-то параметре конфигруации saleShareToXML.

    В некоторых случаях 1С может прислать команду success. Тогда, если модель экспорта заказов реализует интерфейс Mavsan\LaProtocol\Interfaces\ExportSuccess будет вызван метод stepSuccess().

    1С-Битрикс

    С версии модуля обмена 1С-Битрикс 6.5.0.0 применяется контейнерный механизм обмена документов. Т.е. каждый документ в файле обмена должен быть обернут в тэг <Контейнер> см здесь. Предполагается, что каждый документ должен быть обернут в тег <Контейнер>. Во всяком случае, когда приходит информация от 1С о заказах - все выглядит именно так: <КоммерческаяИнформация ...><Контейнер><Документ>...</Документ></Контейнер><Контейнер><Документ>...</Документ></Контейнер></КоммерческаяИнформация>.

    В какой-то из версий обмена модулем 1С-Битрикс более не применяется кодировка windows-1251, т.е. XML с данными о заказах надо слать в кодировке utf-8, к сожалению, я не выяснил, с какой версии модуля обмена 1С-Битрикс это произошло.

    Мне так и не удалось корректно отправлять адресную информацию контагента (покупателя), для версии обмена CommerceML 3.1, поэтому эту информацию просто отправляю в комментарии к заказу. Возможно, в 1С какой-то справочник не настроен или что-то вроде того. Обратите внимание, что в доке указано, что Адрес доставки должен быть в теге <ЗначенияРеквизитов><ЗначениеРеквизита><Наименование>Адрес доставки</Наименование><Значение>Тут адрес доставки</Значение></ЗначениеРеквизита></ЗначенияРеквизитов>. До того, как клиент что-то в 1С не изменил - адрес доставки сохранялся нормально.

    Если ни разу не реализовывали обмен между сайтом и модулем 1С-Битрикс, то единственный совет, который я могу дать - поставить себе локально 1С (торренты в помощь), узнать какая версия модуля обмена 1С-Битрикс используется клиентом, узнать какая версия и какая конфигурация использует эту версию обмена. Найти, скачать и установить конфиграцию (опять-же торренты) и модуль обмена, создать в 1С базу с демонстрационными данными и тестировать, т.к. с первого (или даже сотого) раза может не заработать. В конфигруации 1С точку старта, я искал глобальным поиском по названию кнопки, с которой начинается обмен (в разных версиях модуля обмена может называться по-разному, см. в форме). При поиске надо отметить чекбоксом “Элементы форм”. Как попасть в конфигратор и прочее - ищите в интернетах.

    Обработка данных каталога

    На определенном этапе 1С отсылает команду начать обработку присланных файлов. Контроллер создает экземпляр объекта модели, указанной в конфигурации (ключ конфигурации - catalogWorkModel) и вызывает метод import($fileName), где $fileName - имя полный путь к файлу, который требуется обработать.

    По мере работы этот метод должен возвращать:

    • self::answerSuccess, если обработка файла завершена;
    • slef::answerProgress, если требуется продолжить обработку файла (например большой объём данных);
    • slef::answerFailure, если произошла ошибка.

    В не зависимости от ответа - будет вызван метод getAnswerDetail(), который должен вернуть детальный ответ или пустую строку. Например:

    • обработано 500 записей из 100500;
    • ошибка: у товара указана группа, но такой группы нет
    • файл успешно обработан

    Если необходимо передать несколько строк - они должны быть разделены символов перевода каретки \n.

    1C-Битрикс

    В случае, если на стороне 1С используется модуль обмена 1С-Битрикс, необходимо в файле конфигурации параметр isBitrixOn1C установить в true. Это заставит контроллер отправлять запросы в в виде, который требует модуль обмена 1C-Битрикс.

    Если в настройках модуля обмена включена опция “Деактивировать товары и разделы, не попавшие в полную выгрузку” в конце обмена (а возможно и в каких-то других случаях) будет прислана команда deactivate (в стандартном протоколе такой команды нет).

    В этом случае, если модель обработки данных от 1С реализует интерфейс \Mavsan\LaProtocol\Interfaces\ImportBitrix, будет вызван метод modeDeactivate($startTime) этого интерфейса. Эта команда говорит, что необходимо очистить данные, которых не было в файле обмена. Т.е. надо обязательно при обработке каталога всем товарам и группам товаров, справочниками и т.д. (в общем всем записям, которые прилетели и не должны быть удалены) - обязательно устанавливать дату обновления updated_at, чтобы в методе modeDeactivate($startTime) вы знали, что надо удалять.

    Собственно вот это $startTime и есть метка времени начала обработки. Его отсылает контроллер вместе с другими параметрами, в самом начале обмена, при инициализации сессии. Это метка времени, в формате date('Y-m-d H:i:s').

    Последней от 1С-Битрикс прилетает команда complete, если модель обработки данных от 1С реализует интерфейс \Mavsan\LaProtocol\Interfaces\ImportBitrix, то будет вызван метод modeComplete() этого интерфейса.

    Описание

    Протокол обмена информацией с 1С

    Конвейеры
    0 успешных
    0 с ошибкой