├── .docker └── Dockerfile ├── .env-dist ├── .github └── workflows │ └── woo.yml ├── .gitignore ├── CHANGELOG.md ├── LICENSE ├── Makefile ├── README.md ├── VERSION ├── composer.json ├── composer.lock ├── doc ├── 1.Setup │ ├── Abandoned carts settings.md │ ├── Catalog settings.md │ ├── Cron tasks.md │ ├── Custom fields settings.md │ ├── Debug information.md │ ├── Delivery methods.md │ ├── Generating ICML catalog.md │ ├── Loading data from CRM.md │ ├── Loading orders.md │ ├── Main Settings.md │ ├── Methods order registration.md │ ├── Order statuses.md │ ├── Payment methods.md │ ├── Setting WhatsApp.md │ ├── Setting stock.md │ ├── Settings order number.md │ ├── Settings.md │ ├── Updating data in CRM.md │ ├── User roles.md │ └── Сorporate сlients.md ├── 2. Workflow │ ├── Client Registration.md │ ├── Address.md │ └── Order statuses.md ├── 3. Customization │ └── Filters.md ├── 4. Known issues │ └── Issues.md ├── FAQ │ └── FAQ.md └── README.md ├── docker-compose.yml ├── phpunit.xml.dist ├── resources └── pot │ ├── retailcrm-es_ES.pot │ └── retailcrm-ru_RU.pot ├── src ├── assets │ ├── css │ │ ├── debug-info.css │ │ ├── debug-info.min.css │ │ ├── meta-fields.css │ │ ├── meta-fields.min.css │ │ ├── progress-bar.css │ │ ├── progress-bar.min.css │ │ ├── retailcrm-loyalty-style.css │ │ ├── whatsapp-icon.css │ │ └── whatsapp-icon.min.css │ ├── default │ │ └── default_meta_fields.txt │ └── js │ │ ├── retailcrm-cron-info.js │ │ ├── retailcrm-export.js │ │ ├── retailcrm-loyalty-actions.js │ │ ├── retailcrm-loyalty-cart.js │ │ ├── retailcrm-loyalty.js │ │ ├── retailcrm-meta-fields.js │ │ ├── retailcrm-module-settings.js │ │ ├── retailcrm-tracker-interface.js │ │ └── retailcrm-tracker.js ├── config │ └── objects.xml ├── include │ ├── abstracts │ │ ├── class-wc-retailcrm-abstract-builder.php │ │ ├── class-wc-retailcrm-abstracts-address.php │ │ ├── class-wc-retailcrm-abstracts-data.php │ │ └── class-wc-retailcrm-abstracts-settings.php │ ├── api │ │ ├── class-wc-retailcrm-client-v5.php │ │ ├── class-wc-retailcrm-exception-curl.php │ │ ├── class-wc-retailcrm-exception-json.php │ │ ├── class-wc-retailcrm-proxy.php │ │ ├── class-wc-retailcrm-request.php │ │ ├── class-wc-retailcrm-response.php │ │ └── index.php │ ├── class-wc-retailcrm-base.php │ ├── class-wc-retailcrm-cart.php │ ├── class-wc-retailcrm-customers.php │ ├── class-wc-retailcrm-daemon-collector.php │ ├── class-wc-retailcrm-ga.php │ ├── class-wc-retailcrm-history.php │ ├── class-wc-retailcrm-icml.php │ ├── class-wc-retailcrm-inventories.php │ ├── class-wc-retailcrm-loyalty.php │ ├── class-wc-retailcrm-orders.php │ ├── class-wc-retailcrm-plugin.php │ ├── class-wc-retailcrm-upload-discount-price.php │ ├── class-wc-retailcrm-uploader.php │ ├── components │ │ ├── class-wc-retailcrm-customer-switcher.php │ │ ├── class-wc-retailcrm-history-assembler.php │ │ ├── class-wc-retailcrm-logger.php │ │ └── class-wc-retailcrm-loyalty-form.php │ ├── customer │ │ ├── class-wc-retailcrm-customer-address.php │ │ ├── class-wc-retailcrm-customer-corporate-address.php │ │ └── woocommerce │ │ │ └── class-wc-retailcrm-wc-customer-builder.php │ ├── functions.php │ ├── icml │ │ └── class-wc-retailcrm-icml-writer.php │ ├── index.php │ ├── interfaces │ │ └── class-wc-retailcrm-builder-interface.php │ ├── models │ │ ├── class-wc-retailcrm-customer-switcher-result.php │ │ └── class-wc-retailcrm-customer-switcher-state.php │ ├── order │ │ ├── class-wc-retailcrm-order-address.php │ │ ├── class-wc-retailcrm-order-item.php │ │ ├── class-wc-retailcrm-order-payment.php │ │ └── class-wc-retailcrm-order.php │ └── validators │ │ ├── class-wc-retailcrm-validator-exception.php │ │ ├── loyalty-validator │ │ ├── class-wc-retailcrm-loyalty-constraint.php │ │ └── class-wc-retailcrm-loyalty-validator.php │ │ └── url-validator │ │ ├── class-wc-retailcrm-url-constraint.php │ │ └── class-wc-retailcrm-url-validator.php ├── index.php ├── languages │ ├── index.php │ ├── retailcrm-es_ES.l10n.php │ ├── retailcrm-es_ES.mo │ ├── retailcrm-ru_RU.l10n.php │ └── retailcrm-ru_RU.mo ├── readme.txt ├── retailcrm.php └── uninstall.php └── tests ├── abstracts └── test-wc-retailcrm-abstract-builder.php ├── bin └── install.sh ├── bootstrap.php ├── customer ├── test-wc-retailcrm-customer-address.php ├── test-wc-retailcrm-customer-corporate-address.php └── woocommerce │ └── test-wc-retailcrm-wc-customer-builder.php ├── datasets ├── data-base-retailcrm.php ├── data-cart-retailcrm.php ├── data-customers-retailcrm.php ├── data-history-retailcrm.php ├── data-inventories-retailcrm.php ├── data-loyalty-retailcrm.php └── data-upload-price-retailcrm.php ├── helpers ├── class-wc-retailcrm-log-handler-stdout.php ├── class-wc-retailcrm-response-helper.php └── class-wc-retailcrm-test-case-helper.php ├── loyalty └── test-wc-retailcrm-client-v5.php ├── models ├── test-wc-retailcrm-customer-switcher-result.php └── test-wc-retailcrm-customer-switcher-state.php ├── order ├── test-wc-retailcrm-order-address.php ├── test-wc-retailcrm-order-item.php ├── test-wc-retailcrm-order-payment.php └── test-wc-retailcrm-order.php ├── test-wc-retailcrm-base.php ├── test-wc-retailcrm-cart.php ├── test-wc-retailcrm-customers.php ├── test-wc-retailcrm-daemon-collector.php ├── test-wc-retailcrm-ga.php ├── test-wc-retailcrm-history.php ├── test-wc-retailcrm-icml.php ├── test-wc-retailcrm-inventories.php ├── test-wc-retailcrm-loyalty.php ├── test-wc-retailcrm-orders.php ├── test-wc-retailcrm-plugin.php ├── test-wc-retailcrm-upload-discount-price.php ├── test-wc-retailcrm-uploader.php └── validators └── test-wc-retailcrm-loyalty-validator.php /.docker/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM php:7.1-fpm 2 | 3 | RUN apt-get update 4 | 5 | RUN apt-get install -y zlib1g-dev libpq-dev git libicu-dev libxml2-dev libpng-dev libjpeg-dev libmcrypt-dev libxslt-dev libfreetype6-dev \ 6 | && docker-php-ext-configure intl \ 7 | && docker-php-ext-install intl \ 8 | && docker-php-ext-configure pgsql -with-pgsql=/usr/local/pgsql \ 9 | && docker-php-ext-install mysqli pdo pdo_mysql \ 10 | && docker-php-ext-install zip \ 11 | && docker-php-ext-install xml \ 12 | && docker-php-ext-configure gd --with-png-dir=/usr/local/ --with-jpeg-dir=/usr/local/ --with-freetype-dir=/usr/local/ \ 13 | && docker-php-ext-install gd \ 14 | && docker-php-ext-install mcrypt \ 15 | && docker-php-ext-install bcmath \ 16 | && docker-php-ext-install soap \ 17 | && docker-php-ext-install xsl \ 18 | && docker-php-ext-install mbstring 19 | 20 | RUN apt-get install -y gettext 21 | RUN apt-get install -y subversion 22 | RUN apt-get install -y wget 23 | 24 | RUN wget -O /usr/bin/phpunit https://phar.phpunit.de/phpunit-7.phar && chmod +x /usr/bin/phpunit 25 | RUN curl --insecure https://getcomposer.org/download/1.9.3/composer.phar -o /usr/bin/composer && chmod +x /usr/bin/composer 26 | 27 | RUN curl -sL https://deb.nodesource.com/setup_10.x | bash - 28 | RUN apt-get install -y nodejs 29 | 30 | # Set timezone 31 | RUN rm /etc/localtime 32 | RUN ln -s /usr/share/zoneinfo/Europe/Moscow /etc/localtime 33 | RUN "date" 34 | 35 | WORKDIR /code -------------------------------------------------------------------------------- /.env-dist: -------------------------------------------------------------------------------- 1 | # MySQL host and credentials 2 | DB_NAME=wc_retailcrm_test 3 | DB_USER=wc_retailcrm 4 | DB_PASS=wc_retailcrm 5 | DB_HOST=mysql 6 | 7 | # WordPress and WooCommerce versions 8 | WP_VERSION=5.3 9 | WC_VERSION=3.9.0 10 | 11 | # Enable this in order to pipe all module log messages (including debug ones) to STDOUT. 12 | MODULE_LOGS_TO_STDOUT=0 13 | -------------------------------------------------------------------------------- /.github/workflows/woo.yml: -------------------------------------------------------------------------------- 1 | name: woo 2 | 3 | on: 4 | push: 5 | branches: 6 | - '**' 7 | tags-ignore: 8 | - '*.*' 9 | pull_request: 10 | 11 | env: 12 | DB_HOST: 127.0.0.1 13 | DB_USER: root 14 | DB_PASS: root 15 | DB_NAME: wc_retailcrm_test 16 | 17 | jobs: 18 | test: 19 | runs-on: ubuntu-latest 20 | strategy: 21 | matrix: 22 | include: 23 | # WordPress 5.3 24 | #PHP 7.1 and 7.4 25 | - php-version: '7.1' 26 | wp: '5.3' 27 | wc: '5.4.3' 28 | coverage: 1 29 | phpunit: 'phpunit:7.5.20' 30 | - php-version: '7.1' 31 | wp: '5.3' 32 | wc: '6.4.0' 33 | phpunit: 'phpunit:7.5.20' 34 | - php-version: '7.4' 35 | wp: '5.3' 36 | wc: '5.4.3' 37 | phpunit: 'phpunit:7.5.20' 38 | - php-version: '7.4' 39 | wp: '5.3' 40 | wc: '6.4.0' 41 | phpunit: 'phpunit:7.5.20' 42 | 43 | # WordPress 6.0 44 | # PHP 7.1 and 7.4 45 | - php-version: '7.1' 46 | wp: '6.0' 47 | wc: '5.4.3' 48 | phpunit: 'phpunit:7.5.20' 49 | - php-version: '7.1' 50 | wp: '6.0' 51 | wc: '6.4.0' 52 | phpunit: 'phpunit:7.5.20' 53 | - php-version: '7.4' 54 | wp: '6.0' 55 | wc: '5.4.3' 56 | phpunit: 'phpunit:7.5.20' 57 | - php-version: '7.4' 58 | wp: '6.0' 59 | wc: '6.4.0' 60 | phpunit: 'phpunit:7.5.20' 61 | services: 62 | mysql: 63 | image: mysql:5.7 64 | env: 65 | MYSQL_ALLOW_EMPTY_PASSWORD: false 66 | MYSQL_ROOT_PASSWORD: ${{ env.DB_PASS }} 67 | MYSQL_DATABASE: ${{ env.DB_NAME }} 68 | ports: 69 | - 3306:3306 70 | options: --health-cmd="mysqladmin ping" --health-interval=10s --health-timeout=5s --health-retries=5 71 | steps: 72 | - uses: actions/checkout@v2 73 | - name: Setup PHP ${{ matrix.php-version }} 74 | uses: shivammathur/setup-php@v2 75 | with: 76 | php-version: ${{ matrix.php-version }} 77 | tools: composer:2.1.14, ${{ matrix.phpunit }} 78 | extensions: gd, mbstring, mysqli, zip, unzip, mcrypt, mysql, pdo_mysql, dom 79 | coverage: xdebug 80 | 81 | - name: Tool versions 82 | run: | 83 | php --version 84 | composer --version 85 | phpunit --version 86 | 87 | - name: Install Polyfills dependency for WP 5.9 and 6.0 88 | if: ${{ matrix.wp }} == '6.0' || ${{ matrix.wp }} == '5.9' 89 | run: | 90 | composer require --dev yoast/phpunit-polyfills --ignore-platform-reqs 91 | 92 | - name: Install Woocommerce 93 | env: 94 | WP_VERSION: ${{ matrix.wp }} 95 | WC_VERSION: ${{ matrix.wc }} 96 | run: make install 97 | 98 | - name: Run tests 99 | env: 100 | WP_VERSION: ${{ matrix.wp }} 101 | WC_VERSION: ${{ matrix.wc }} 102 | run: make test 103 | 104 | - name: Coverage 105 | env: 106 | COVERAGE: ${{ matrix.coverage }} 107 | if: env.COVERAGE == 1 108 | run: | 109 | make coverage 110 | bash <(curl -s https://codecov.io/bash) 111 | deploy: 112 | needs: ['test'] 113 | if: success() && github.event_name == 'push' && github.repository_owner == 'retailcrm' && github.ref == 'refs/heads/master' 114 | runs-on: ubuntu-latest 115 | steps: 116 | - uses: actions/checkout@v2 117 | - name: Setup PHP 7.2 118 | uses: shivammathur/setup-php@v2 119 | with: 120 | php-version: '7.2' 121 | tools: composer:v2 122 | - name: Build release 123 | run: | 124 | git fetch origin --unshallow --tags 125 | export LAST_TAG=`git describe --abbrev=0 --tags` 126 | export VERSION=`cat VERSION` 127 | export ARCHIVE_NAME=retailcrm-$VERSION.zip 128 | export ARCHIVE_PATH="/tmp/$ARCHIVE_NAME" 129 | export RELEASE_TAG=v$VERSION 130 | export LAST_COMMIT=`git log --oneline --format=%B -n 1 HEAD | head -n 1` 131 | echo RELEASE_TAG=$RELEASE_TAG >> $GITHUB_ENV 132 | echo LAST_TAG=$LAST_TAG >> $GITHUB_ENV 133 | echo LAST_COMMIT=$LAST_COMMIT >> $GITHUB_ENV 134 | echo ARCHIVE_PATH=$ARCHIVE_PATH >> $GITHUB_ENV 135 | echo ARCHIVE_NAME=$ARCHIVE_NAME >> $GITHUB_ENV 136 | make build_archive 137 | - name: Create Release 138 | id: create_release 139 | uses: actions/create-release@v1 140 | if: env.LAST_TAG != env.RELEASE_TAG 141 | env: 142 | GITHUB_TOKEN: ${{ secrets.TOKEN }} 143 | with: 144 | tag_name: ${{ env.RELEASE_TAG }} 145 | release_name: ${{ env.RELEASE_TAG }} 146 | body: ${{ env.LAST_COMMIT }} 147 | draft: false 148 | prerelease: false 149 | - name: Deploy 150 | env: 151 | SVNREPOURL: ${{ secrets.SVNREPOURL }} 152 | USERNAME: ${{ secrets.USERNAME }} 153 | PASSWORD: ${{ secrets.PASSWORD }} 154 | run: | 155 | make svn_clone 156 | make svn_push 157 | - name: Cleanup 158 | if: env.LAST_TAG != env.RELEASE_TAG 159 | run: make remove_dir 160 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | /nbproject/ 2 | /vendor/ 3 | .idea/ 4 | .env 5 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2017 RetailDriver LLC 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | ROOT_DIR=$(shell dirname $(realpath $(lastword $(MAKEFILE_LIST)))) 2 | VERSION = `cat $(ROOT_DIR)/VERSION` 3 | ARCHIVE_NAME = '/tmp/retailcrm-'$(VERSION)'.zip' 4 | 5 | .PHONY: test 6 | 7 | svn_clone: 8 | sudo apt install subversion 9 | mkdir -p /tmp/svn_plugin_dir 10 | svn co $(SVNREPOURL) /tmp/svn_plugin_dir --no-auth-cache 11 | 12 | svn_push: /tmp/svn_plugin_dir 13 | if [ ! -d "/tmp/svn_plugin_dir/tags/$(VERSION)" ]; then \ 14 | svn delete /tmp/svn_plugin_dir/trunk/*; \ 15 | rm -rf /tmp/svn_plugin_dir/trunk/*; \ 16 | cp -R $(ROOT_DIR)/src/* /tmp/svn_plugin_dir/trunk; \ 17 | svn copy /tmp/svn_plugin_dir/trunk /tmp/svn_plugin_dir/tags/$(VERSION) --no-auth-cache; \ 18 | svn add /tmp/svn_plugin_dir/trunk/* --force; \ 19 | svn add /tmp/svn_plugin_dir/tags/$(VERSION)/* --force; \ 20 | svn ci /tmp/svn_plugin_dir -m $(VERSION) --username $(USERNAME) --password $(PASSWORD) --no-auth-cache; \ 21 | fi 22 | 23 | remove_dir: 24 | rm -rf /tmp/svn_plugin_dir 25 | 26 | compile_pot: 27 | msgfmt resources/pot/retailcrm-ru_RU.pot -o src/languages/retailcrm-ru_RU.mo 28 | msgfmt resources/pot/retailcrm-es_ES.pot -o src/languages/retailcrm-es_ES.mo 29 | 30 | install: 31 | sudo apt install subversion 32 | mkdir -p coverage 33 | bash tests/bin/install.sh $(DB_NAME) $(DB_USER) $(DB_HOST) $(DB_PASS) $(WP_VERSION) $(WC_VERSION) 34 | 35 | test: 36 | phpunit -c phpunit.xml.dist 37 | 38 | local_test: 39 | bash tests/bin/install.sh $(DB_NAME) $(DB_USER) $(DB_HOST) $(DB_PASS) $(WP_VERSION) $(WC_VERSION) 40 | phpunit -c phpunit.xml.dist 41 | 42 | run_tests: 43 | docker-compose --no-ansi up -d --build mysql 44 | docker-compose --no-ansi run --rm --no-deps app make local_test 45 | docker-compose down -v 46 | 47 | coverage: 48 | wget https://phar.phpunit.de/phpcov-2.0.2.phar && php phpcov-2.0.2.phar merge coverage/ --clover coverage.xml 49 | 50 | build_archive: 51 | zip -r $(ARCHIVE_NAME) ./src/* 52 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | [![Build Status](https://github.com/retailcrm/woocommerce-module/workflows/woo/badge.svg)](https://github.com/retailcrm/woocommerce-module/actions) 2 | [![Coverage](https://img.shields.io/codecov/c/gh/retailcrm/woocommerce-module/master.svg?logo=github)](https://codecov.io/gh/retailcrm/woocommerce-module) 3 | [![GitHub release](https://img.shields.io/github/release/retailcrm/woocommerce-module.svg?logo=codecov)](https://github.com/retailcrm/woocommerce-module/releases) 4 | [![PHP version](https://img.shields.io/badge/PHP->=7.0-blue.svg?logo=php)](https://php.net/) 5 | 6 | Woocommerce-module 7 | ================== 8 | 9 | Integration plugin for WooCommerce and [Simla.com](https://www.simla.com) 10 | 11 | [Documentation](https://docs.retailcrm.ru/Users/Integration/SiteModules/WooCommerce) page 12 | 13 | [Customization](https://github.com/retailcrm/woocommerce-module/wiki/%D0%9A%D0%B0%D1%81%D1%82%D0%BE%D0%BC%D0%B8%D0%B7%D0%B0%D1%86%D0%B8%D1%8F-%D0%B8%D0%BD%D1%82%D0%B5%D0%B3%D1%80%D0%B0%D1%86%D0%B8%D0%BE%D0%BD%D0%BD%D0%BE%D0%B3%D0%BE-%D0%BF%D0%BB%D0%B0%D0%B3%D0%B8%D0%BD%D0%B0) info 14 | 15 | #### Local testing 16 | 17 | To local testing run `make run_tests` 18 | -------------------------------------------------------------------------------- /VERSION: -------------------------------------------------------------------------------- 1 | 4.8.25 2 | -------------------------------------------------------------------------------- /composer.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "retailcrm/woocommerce-retailcrm", 3 | "description": "Integration plugin for WooCommerce & RetailCRM", 4 | "type": "wordpress-plugin", 5 | "authors": [ 6 | { 7 | "name": "RetailDriver LLC", 8 | "email": "integration@retailcrm.ru" 9 | } 10 | ], 11 | "minimum-stability": "dev", 12 | "require": { 13 | "ext-simplexml": "*", 14 | "ext-xmlwriter": "*" 15 | }, 16 | "require-dev": { 17 | "ext-json": "*", 18 | "ext-mbstring": "*", 19 | "phpunit/phpunit": "7.*", 20 | "yoast/phpunit-polyfills": "1.x-dev" 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /doc/1.Setup/Abandoned carts settings.md: -------------------------------------------------------------------------------- 1 | ### Настройки брошенных корзин 2 | 3 | В версии 4.6.0 добавлен функционал выгрузки брошенных корзин. 4 | 5 | Для активации необходимо включить опцию ***Выгружать брошенные корзины*** 6 | 7 | ### Брошенные корзины 8 | 9 | Брошенная корзина - клиент заходит на сайт, добавляет/удаляет товары в корзине, а затем завершает визит без оформления заказа. 10 | 11 | > Важно: 12 | > * Корзины выгружаются только для зарегестрированных клиентов; 13 | > * Для корректной работы корзин, один API ключ = один магизн в CRM; 14 | 15 | При разработке функционала, ориентировались на хуки корзины в WooCommerce: 16 | * Хуки для метода **set_cart**: 17 | * **woocommerce_add_to_cart** - добавление товара в корзину; 18 | * **woocommerce_after_cart_item_quantity_update** - изменение кол-во товара в корзине; 19 | * **woocommerce_cart_item_removed** - удаление товара с корзины; 20 | * Хуки для метода **clear_cart**: 21 | * **woocommerce_cart_emptied** - полная очистка корзины. Также срабатывает при создании заказа; 22 | 23 | Корзина создается в CRM, при первом добавлении товара. 24 | 25 | **Фильтры:** 26 | 27 | > retailcrm_process_cart - позволяет кастомизировать данные корзины. 28 | 29 | **Пример использования:** 30 | 31 | ```php 32 | карточка товара -> блок Опубликовано)*. Из товаров, чей статус будет соответствовать выбранному, будет сгенерирован ICML-файл каталога. Для выбора необходимо поставить галочку напротив нужного статуса и сохранить настройки. 4 | 5 | Статус видимости товара Личное, также относится к статусам товара "Статус: Опубликовано как личное". Анализ статусов товара был произведен в задаче [#76054](https://redmine.retailcrm.tech/issues/76054) 6 | 7 | ![](https://lh3.googleusercontent.com/A64aLvFUecO7kd73gEH0SbfQsYkhjDfOl0DRmcx6FsMfAWX7Z5DFX_Y5_lHnm7z3D3SpKzNHOFINI26mlihBNbqsuV_8Kd0S3QOqWt32Pv2AvrDWJQc44eG03J5wkyz2VL3BXV06=s0)![](https://lh6.googleusercontent.com/aG6m6-TGpU4kWPIVeMQ_EfN1kBsG0l3ISVRx9CU1KlvZdZ4n8NkhkM-DFLZctmQXqKi65Hv83paSZf9jK1mCj7QWCUn1syfvBme8kjYGzPBHH-3feSJE-G8dKtLTBqwvER4RbLON=s0) 8 | -------------------------------------------------------------------------------- /doc/1.Setup/Cron tasks.md: -------------------------------------------------------------------------------- 1 | ### Настройки cron задач 2 | 3 | В версии 4.4.5 добавлен функционал для изменения интервалов времени выполнения cron задач. 4 | 5 | Для изменения интервала времени необходимо с помощью фильтра **retailcrm_add_cron_interval** добавить пользовательский интервал. Затем изменить интервал для cron задач с помощью фильтра **retailcrm_cron_schedules**. 6 | Кастомизацию необходимо добавить на сервере в директорию wp-content -> mu-plugins -> mu-simla.php. После добавления кастомизации в настройках модуля необходимо очистить старые cron задачи. 7 | Перейдите в настройки, откройте "Отладочная информация" и нажмите на кнопку "Очистить cron задачи", появится окно с сообщением об успешной очистке, интервалы будут применены. 8 | 9 | Если необходимо вернуть стандартные интервалы, то удаляем кастомизацию и в настройках так же очищаем старые cron задачи. 10 | 11 | **Интервалы по умолчанию:** 12 | ```php 13 | [ 14 | 'icml' => 'three_hours', 15 | 'history' => 'five_minutes', 16 | 'inventories' => 'fiveteen_minutes', 17 | 'loyalty_upload_price' => 'four_hours' 18 | ] 19 | ``` 20 | > Важно! При использовании фильтра **retailcrm_cron_schedules**, можно использовать ключи: 'icml', 'history', 'inventories'. 21 | 22 | **Фильтры:** 23 | 24 | > retailcrm_add_cron_interval - позволяет добавить пользовательский интервал времени. 25 | 26 | > retailcrm_cron_schedules - позволяет изменить интервал времени для cron задач. 27 | 28 | **Пример использования:** 29 | ```php 30 | [ 37 | 'interval' => 120, // seconds 38 | 'display' => __('Every 2 minutes') 39 | ] 40 | ]; 41 | } 42 | 43 | 44 | add_filter('retailcrm_cron_schedules', 'change_cron_tasks'); 45 | 46 | function change_cron_tasks($cronTasks) 47 | { 48 | $cronTasks['history'] = 'two_minutes'; 49 | 50 | return $cronTasks; 51 | } 52 | ``` 53 | -------------------------------------------------------------------------------- /doc/1.Setup/Custom fields settings.md: -------------------------------------------------------------------------------- 1 | ### Пользовательские поля 2 | 3 | В настройках модуля есть возможность настроить передачу пользовательских полей из CMS в CRM и обратно. Для этого необходимо настроить соответствие между мета полями CMS и пользовательскими полями CRM. 4 | 5 | Слева располагаются мета поля CMS, справа пользовательские поля CRM. В выпадающем списке слева нужно выбрать мета поля CMS, в выпадающем списке справа нужно выбрать пользовательское поле CRM и таким образом сопоставить их. 6 | 7 | Пользовательские поля разбиты на две группы для заказов "Custom fields for order" и для клиентов "Custom fields for customer". 8 | 9 | При нажатии на кнопку "Custom fields for order/customer" будет добавлены новые списки для выбора. Количество списков не может быть больше количества мета полей CMS или пользовательских полей CRM. Поэтому когда добавили максимальное число списков, кнопка добавления пропадает. Для каждой группы предусмотрена своя кнопка добавления. 10 | 11 | Обратите внимание, что значения не должны повторяться, т.е. для двух мета полей CMS, не может быть выбрана одно и то же пользовательское поле CRM. Необходимо выбирать 1 к 1. Если вдруг выбрать значение которое уже используется, будет показано предупреждающее уведомление, выбранный список начнет мигать красным и в этом списке будет установленно значение по умолчанию "Select value". 12 | 13 | Сопоставление можно удалить, если нажать на крестик справа от списка пользовательский полей CRM. 14 | 15 | Для сохранения выбранных сопоставлений необходимо нажать на кнопку "Сохранить изменения" в конце страницы настроек. 16 | 17 | Также в списке значений для сопоставления выводятся **только Активные в CRM пользовательские поля**. 18 | 19 | На стороне CRM доступные пользовательские поля можно увидеть перейдя в **Настройки - Системные - Пользовательские поля.** 20 | 21 | 22 | В версии 4.6.5 добавлен функционал для передачи мета полей CMS в некоторые стандартные поля CRM. 23 | 24 | Список стандартных полей CRM доступных для передачи данных: 25 | * Имя 26 | * Фамилия 27 | * Телефон 28 | * E-mail 29 | * Адрес 30 | * Город 31 | * Индекс 32 | * Регион 33 | * Теги (доступно только для клиентов) 34 | * Комментарий клиента (доступно только для заказов) 35 | * Комментарий менеджера (доступно только для заказов) 36 | 37 | **Важно! Передача мета полей WC в стандартные поля CRM реализована только из CMS в CRM, обратная синхронизация не предусмотрена.** -------------------------------------------------------------------------------- /doc/1.Setup/Debug information.md: -------------------------------------------------------------------------------- 1 | ### Режим отладки 2 | 3 | Служит для включения расширенного логирования, предназначен для разработчиков. Для просмотра логов необходимо перейти *WooCommerce -> Статус -> Журналы* и там смотреть на файлы **retailcrm_debug**. 4 | 5 | ### Отладочная информация 6 | 7 | Вывод основной информации о работе модуля. На данный момент выводит время последнего запуска крон-задач: 8 |

**History** - показывает время и дату запуска обновления по истории *(данные будут отображены, если активна опция “Загрузка данных из CRM”)*. 9 | 10 | **Icml** - показывает время и дату, когда последний раз срабатывала команда генерации каталога по WP-CRON *(информация будет отображена, если активна опция “Генерация ICML каталога товаров с помощью wp-cron”)*. 11 | 12 | **Inventories** - показывает время и дату запуска получения остатков по товарам из CRM в WooCommerce *(данные будут отображены, если активна опция “Настройка управления остатками”)*. 13 | 14 | Добавлено в версии 4.3.3. 15 | 16 | ![](https://lh4.googleusercontent.com/_tdjxonwGp40uafwaEK__QenxJurbNPsvlMu8Pw3eZk_vN6-KzKB8RWeYXwJqYPRfjzTZnvT0TGYVBry-4JWXTzQuDt9zDIBWQO0lnrUSHP84PxoomFiDPIVHsRIZ7gHzKdgmDUc=s0) 17 | -------------------------------------------------------------------------------- /doc/1.Setup/Delivery methods.md: -------------------------------------------------------------------------------- 1 | ### Способы доставки 2 | 3 | Необходимо настроить соответствие между доставками WooCommerce и CRM. 4 | 5 | Слева располагаются доставки WooCommerce, справа CRM. В выпадающем списке справа нужно выбрать справочник доставки в CRM, который будет соответствовать доставке в WooCommerce. Данное сопоставление требуется **выбрать для всех доставок**. Если ни одно из предложенных значений не подходит, то необходимо создать в CRM новый справочник доставки, либо пропустить сопоставление. 6 | 7 | После создания новой доставки *(справочника)* на стороне CRM, в WooCommerce необходимо обновить страницу настроек *(перед этим не забудьте сохранить указанные ранее настройки)*, чтобы в предложенных значениях появился новый созданный справочник. 8 | 9 | Если сопоставление пропустить и не выбрать ни одного значения, в этом случае, если в заказе в WooCommerce будет выбрана данная доставка, то в CRM в заказе “Тип доставки” **будет не указан**. 10 | 11 | Обратите внимание, что значения не должны повторяться, т.е. для двух доставок WooCommerce, не может быть выбрана одна и та же доставка *(справочник доставки)* в CRM. Необходимо выбирать 1 к 1. 12 | 13 | **1 доставка WooCommerce = 1 доставка CRM**. Иначе будут наблюдаться ошибки в работе модуля. 14 | 15 | Также в списке значений для сопоставления выводятся **только Активные в CRM справочники**. 16 | 17 | Доставки на стороне WooCommerce можно посмотреть перейдя в *WooCommerce - Настройки - вкладка Доставка - Зоны доставки*; Для каждой зоны доставки будет перечень своих транспортных компаний. 18 | 19 | ![](https://lh3.googleusercontent.com/KVMF00-y5AvkDLQLEhwRWinDxfV9limBxlpPO16_SKjsmYTYwsbQ06EI3_avJwQlO6jDWnJ8Z2QqhHyqw166gHY__dAWb1dgpLwYY-vmxnJyX049mHKdJpBLmfnlYmRh8ghed3Qh=s0) 20 | 21 | 22 | На стороне CRM доступные доставки можно увидеть перейдя в *Настройки - Справочники - Типы доставок.* 23 | 24 | ![](https://lh3.googleusercontent.com/G4DvOhXuLXTPmyHmeUkAgdgbRHBn2ZJH9vnJaYpOQbHrKIpaBjddQ83AdjLfukRUnxiLZ4bLbrvCRNqsJPJQ1xk0oHpfnbfWyVMrt1Tem9fT_6GplLYFBBDuuK0pXDdXFw9A6rV1=s0) 25 | -------------------------------------------------------------------------------- /doc/1.Setup/Generating ICML catalog.md: -------------------------------------------------------------------------------- 1 | ### Генерация ICML 2 | 3 | Данная опция позволяет сгенерировать каталог вручную. 4 | 5 | Для активации необходимо нажать на кнопку и дождаться сообщения о завершении генерации. Сгенерированный каталог будет доступен по ссылке https://yoursite.com/simla.xml 6 | 7 | Чтобы проверить, что команда по генерации каталога отработала успешно, можно перейти по ссылке и сравнить дату генерации каталога с текущей: 8 | 9 | ![](https://lh3.googleusercontent.com/Z6qwxSjGA9AHjaDi3RgbJyeQRhvXAkXRvtzXmEJzVdMpwQ2Rc4N0FM9YFhMWYTL-dwoxKmSYgPsJU68TfCer_og-BmnUruKJMJlmjIM7suz42OMCJFH3cwPoKfbW66AYzUL3UZTd=s0) 10 | 11 | Ссылку на каталог требуется указать в настройках магазина в CRM. Для его загрузки в CRM необходимо активировать опцию “Загрузить каталог из ICML сейчас” и сохранить настройки. 12 | ![](https://lh4.googleusercontent.com/oPPNIbm11rgYUbeWIKkyp1XdqC47_N-iTh-jp0C3V5QrRemD-0Gco6pholEP0HKKmkZCpag1n7oiMNvPXiUh5yjSF2DSLtX5_wJPqwQ97XsJDdFdbAPsTYU4LpMdSlPyfs-hviw7=s0) 13 | 14 | ### Генерация ICML каталога товаров с помощью wp-cron 15 | 16 | Данный функционал позволяет генерировать каталог в автоматическом режиме с помощью WP-CRON, в этом случае не потребуется каждый раз при необходимости обновить каталог, заходить в настройки и запускать его загрузку вручную. 17 | 18 | Каталог генерируется **раз в 3 часа**. 19 | 20 | ### Синхронизация остатков и связь товаров 21 | 22 | Функционал служит для **идентификации товаров по SKU (xmlID)**. Для активации необходимо поставить галочку. 23 | 24 | После активации опции при генерации каталога в нем появится параметр xmlID *(артикул)*. 25 | 26 | С версии 4.7.5 после активации/деактивации опции и сохранении настроек, каталог будет сгенерирован автоматически. 27 | 28 | **xmlID** - внешний идентификатор товара, элемент не является обязательным. В случае, если интернет-магазин использует выгрузку номенклатуры товаров из складской системы *(1С, МойСклад)*, то значение этого элемента соответствует идентификатору товара в данной системе. Активируется, когда клиенты используют Woocommerce + MC/1C. 29 | 30 | ### Недавние обновления: 31 | 32 | **В версии 4.4.4** добавлен функционал передачи описания товара в каталог. В настройках каталога необходимо выбрать, какое описание передавать краткое или полное. По умолчанию передается полное описание товара. 33 | 34 | Поле description(описание) выводится в карточке товара, так же его можно использовать в twig-шаблонах. Например, для вывода в печатных формах. 35 | Пример получения описание торгового предложения: 36 | ```twig 37 | {% for availableOrderProduct in order.availableOrderProducts %} {{ availableOrderProduct.getOffer().getDescription() }} {% endfor %} 38 | ``` 39 | 40 | **В версии 4.4.6** добавлен фильтр: 41 | 42 | > retailcrm_process_offer - позволяет изменить данные товара, перед записью в ICML каталог. 43 | 44 | 45 | **Пример использования:** 46 | ```php 47 | После указания отмены заказу, в WooCommerce остатки сразу возвращаются, в CRM, если настроена автоматическая отмена товара при отмене заказа, остатки возвращаются, иначе нужно ждать обновления каталога | 19 | | Оформить заказ в CRM, после указать статус из группы Выполнен в CRM | В CRM по товарам остатки не списываются;
В WooCommerce остаток по товару списывается, когда приходит статус Выполнен | 20 | | Оформить заказ в CRM, после указать статус из группы Отмена в CRM | В CRM остатки не списываются *(в т.ч. и при установлении отмены статусу товара)*, в WooCommerce остатки по товару также не возвращаются | 21 | 22 | #### На стороне CRM 23 | При ведении стока **на стороне CRM**, остаток по товару сразу списывается в CRM. На стороне WooCommerce остатки по товарам обновляются **раз в 15 минут**. 24 | Если данный статус в CRM не выставляет автоматически статус отмены товару, то в CRM остатки не возвращаются, также, как и на стороне WooCommerce
Если будет выставлен статус отмены товару, остатки возвратятся в CRM и спустя 15 минут в WooCommerce 25 | 26 | | Кейс | Результат | 27 | |------------------------------------------------------------------------------|-----------| 28 | | Оформить заказ в CRM, после указать в CRM **статус товару** из группы Отмена | В CRM остаток по товару возвращается, в WooCommerce сток обновляется через 15 минут | 29 | | Оформить заказ на сайте в WooCommerce | Заказ выгружается в CRM, по нему списываются остатки по товару, данное списание передается на сторону WooCommerce | 30 | | Оформить заказ на сайте в WooCommerce, после указать статус Отменен в админке сайта WooCommerce | В CRM приходит статус отмены заказа, если в CRM настроена опция автоматической отмены товара при отмене заказа, то остатки возвращаются в CRM, спустя время и в WooCommerce | -------------------------------------------------------------------------------- /doc/1.Setup/Settings order number.md: -------------------------------------------------------------------------------- 1 | ### Передача номера заказа 2 | 3 | Функционал позволяет в номер заказа CRM передавать номер заказа WooCommerce. 4 | 5 | Если опция не активна, то номер, с которым выгрузится заказ из WooCommerce в CRM, будет сгенерирован с использованием “Шаблона генерации номера заказа из API” *(в CRM Настройки - Системные - Заказы)*. 6 | 7 | ![](https://lh4.googleusercontent.com/SeL8ZOuBRSOqpMeuORL8WBEOgFc4iGijFK6SgKRnyYShVYtTueUtJEJrFE0_o1tJgZFU7-sTOvtN5XeulqeCGpORNIsBLiq9N50qoxZgC4V0W-7Y0anVuK83nZTnpRZdynI4Ue9c=s0) 8 | -------------------------------------------------------------------------------- /doc/1.Setup/Settings.md: -------------------------------------------------------------------------------- 1 | 2 | # Settings 3 | 4 | -------------------------------------------------------------------------------- /doc/1.Setup/Updating data in CRM.md: -------------------------------------------------------------------------------- 1 | ### Обновление данных в Simla.com 2 | 3 | Если активировать данную опцию, **любые изменения заказов из Woocommerce не будут передаваться в CRM**. Изменения заказа из CRM в Woocomerce передаются. 4 | -------------------------------------------------------------------------------- /doc/1.Setup/User roles.md: -------------------------------------------------------------------------------- 1 | # User roles 2 | 3 | С версии `4.3.7` опция `"Роли клиентов"` была удалена из настроек модуля. Сейчас выгружаются пользователи со всеми доступными ролями в CMS. 4 | Добавили фильтр `retailcrm_customer_roles` для корректировки выгружаемых пользователей. 5 | 6 | ## Пример работы фильтра 7 | В приведенном ниже примере показано, как возможно корректировать роли выгружаемых пользователей: 8 | 9 | ```php 10 |
14 | *__В блоке Контактные лица__*: 15 | - ФИО клиента, 16 | - Email, 17 | - Email-подписка *(значение зависит от флага подписки на рассылку на сайте)*, 18 | - признак Основное, 19 | - Телефон, 20 | - нет привязки к Компании.

21 | *__В блоке Компании__*: 22 | - Название, 23 | - Статус, 24 | - признак Основная, 25 | - Тип контрагента (Юр лицо), 26 | - Адрес регистрации *(из Платежного адреса)*, 27 | - есть привязка к адресу.

28 | *__В блоке Адреса__* 29 | - указан Платежный адрес *(заполнены все строки)*. 30 | 31 | В карточку контактного лица *(клиента, на которого оформлен заказ)* в данные адреса выгружается **Платежный адрес**. 32 | 33 | #### На стороне WooCommerce 34 | | # | Кейс *(действие на стороне Woo)* | Результат | 35 | |--|--|--| 36 | |1| Зарегистрировать клиента, указать в профиле Billing address | При добавлении Billing address в профиле клиента в Woo, он выгружается в CRM в карточку клиента | 37 | |2| Зарегистрировать клиента, указать в профиле Shipping address | Shipping address не выгружается в CRM в карточку клиента | 38 | |3| Зарегистрировать клиента, указать в профиле Billing и Shipping address | В карточку клиента в CRM выгружается только Billing address | 39 | |4| Изменить в профиле клиента Billing address | В карточке клиента в CRM обновляется адрес | 40 | |5| Изменить в профиле клиента Shipping address | Shipping address не выгружается в карточку клиента в CRM | 41 | |6| Зарегистрировать клиента, указать в профиле Billing address, после удалить адрес | При указании Billing address в профиле клиента в Woo, он выгружается в карточку клиента в CRM
Адрес в Woo можно изменить, но удалить нельзя *(возможность отсутствует)* | 42 | |7| Зарегистрировать клиента, указать в профиле Shipping address, после удалить | При указании Shipping address в профиле клиента в Woo, он не выгружается в карточку клиента в CRM
Адрес в Woo можно изменить, но удалить нельзя (возможность отсутствует) | 43 | |8| Оформить заказ с указанием только Billing address | В карточке клиента в CRM указан Billing address
В карточке заказа в CRM указан Billing address | 44 | |9| Оформить заказ с указанием Billing и Shipping address | В карточке клиента в CRM указан Billing address
В карточке заказа в CRM в блоке Доставка указан Shipping address | 45 | | | | | 46 | |10| Зарегистрировать клиента, указать при оформлении заказа значение в поле Компания в Billing address | При указании компании в Billing address в профиле клиента, **корп клиент не создается**
**Корп клиент создается при оформлении заказа**
**billing = shipping**
В карточке заказа в CRM в блоке Доставка указан Billing address
В карточке корп клиента в CRM в блоке Компании в поле "Адрес регистрации" указан Billing address; в блоке Адреса также указан Billing address
В карточке Контактного лица *(клиента, на которого оформлен заказ)* в CRM указан Billing address | 47 | |11| Указать при оформлении заказа значение в поле Компания в Shipping address (в Billing address не указано значение в поле Компания) | Корп клиент не создается в CRM; В карточке заказа в CRM указан Shipping address
В карточке клиента в CRM указан Billing address | 48 | |12| Указать при оформлении заказа значение в поле Компания в Billing и Shipping address | Корп клиент создан
В карточке заказа в CRM указан Shipping address
В карточке корп клиента в CRM в блоке Компании в поле Адрес регистрации указан Billing address; в блоке Адреса также указан Billing address
В карточке Контактного лица в CRM указан Billing address | 49 | |13| Изменить значение в поле Компания в профиле клиента в Billing адресе, оформить заказ | Создается новый корп клиент в CRM | 50 | |14| Изменить значение в поле Компания в профиле клиента в Shipping адресе | Никаких изменений на стороне CRM нет | 51 | |15| Зарегистрировать нового клиента, в Billing address в поле Компания указать название существующего корп клиента в CRM | Новый корп клиент не создается в CRM, добавляется новое Контактное лицо в карточку корп клиента + добавляется Billing address в блок Адреса, если он отличен от существующего | 52 | |16| Зарегистрировать нового клиента, в Billing address в поле Компания указать название существующего корп клиента в CRM и существующий адрес | Адрес не дублируется | 53 | | | | | 54 | -------------------------------------------------------------------------------- /doc/2. Workflow/ Client Registration.md: -------------------------------------------------------------------------------- 1 | #Регистрация клиентов 2 | 3 | Клиент может зарегистрироваться на сайте: 4 | * Администратор может зарегистрировать клиента через административную панель WordPress. 5 | * На странице wp-admin. 6 | * При заполнении email в форме регистрации на сайте. 7 | * Когда оформляет новый заказ, если клиент является "гостем" ему будет предложено зарегистрироваться. 8 | 9 | Модуль обрабатывает регистрацию клиентов через хук **user_register**. 10 | 11 | Модуль обрабатывает таких клиентов по следующей логике: 12 | 1. Производится поиск по email в CRM, если клиент найден, происходит обновление его данных в CRM и актуализируется его externalId. 13 | 2. Если клиент не найден в CRM, будет создан новый. 14 | 15 | Данная логика позволяет минимизировать количество дублей. 16 | Возможно ситуация, когда клиент был удален в CMS, но в CRM такой клиент продолжает существовать, чтобы не потерять таких клиентов производится актуализация их данных. 17 | 18 | -------------------------------------------------------------------------------- /doc/2. Workflow/Address.md: -------------------------------------------------------------------------------- 1 | # Работа с адресами 2 | 3 | С версии `4.3.8` изменена логика работы с адресами. 4 | 5 | В заказ CRM c заказа WooCommerce будет передаваться, только shipping адрес. Если при оформлении заказа указан только billing адрес, то WooCommerce записывает в БД эти же данные в shipping, то есть shipping = billing. 6 | 7 | При создании обычных/корпоративных клиентов в CRM будет передаваться billing адрес с заказа/пользователя WooCommerce. Если клиент гость и у него нет данных по billing адресу, тогда будет передан billing адрес заказа. 8 | 9 | Для кастомизаций адресов в CRM, добавили новые фильтры: 10 | * `retailcrm_process_order_address` 11 | * `retailcrm_process_customer_address` 12 | * `retailcrm_process_customer_corporate_address` 13 | 14 | ## Пример работы фильтров 15 | В приведенном ниже примере показано, как возможно кастомизировать адрес заказа: 16 | 17 | ```php 18 | WC): 4 | * Если в CRM создают заказ со статусом для которого не выбран маппинг, модуль по умолчанию поставит "pending" для этого заказа в WC. 5 | * Если в CRM изменении заказ со статусом для которого не выбран маппинг, статус заказа в WC изменен не будет. 6 | 7 | Прямая синхронизация (WC --> CRM): 8 | * Если в WC создают/изменяют заказ со статусом для которого в настройках маппинга статусов выбрано "Не отправлять в CRM", при создании заказа, в массиве данных заказа поля "status" не будет, CRM поставит статус по умолчанию, при изменении статус заказа в CRM изменен не будет. 9 | 10 | 11 | -------------------------------------------------------------------------------- /doc/3. Customization/Filters.md: -------------------------------------------------------------------------------- 1 | ### Фильтры 2 | 3 | Если вы хотите изменить данные отправляемые между CRM и CMS, вы можете использовать **пользовательские фильтры**. 4 | 5 | Чтобы использовать фильтры, необходимо в директории wp-content создать директорию mu-plugins и в ней создать кастомный файл mu-simla.php. 6 | 7 | ### Список доступных фильтров 8 | 9 | > retailcrm_process_customer - позволяет изменить данные клиента при передачи из CMS -> CRM. 10 | 11 | > retailcrm_process_customer_address - позволяет изменить адрес клиента при передачи из CMS -> CRM. 12 | 13 | > retailcrm_process_customer_corporate - позволяет изменить данные корпоративного клиента при передачи из CMS -> CRM. 14 | 15 | > retailcrm_process_customer_corporate_address - позволяет изменить адрес корпоративного клиента при передачи из CMS -> CRM. 16 | 17 | > retailcrm_process_customer_corporate_company - позволяет изменить компанию корпоративного клиента при передачи из CMS -> CRM. 18 | 19 | > retailcrm_customer_roles - позволяет изменить допустимые роли клиентов. 20 | 21 | > retailcrm_daemon_collector - позволяет изменить данные для Daemon Collector. 22 | 23 | > retailcrm_initialize_analytics - позволяет изменить данные скрипта для Google Analytics. 24 | 25 | > retailcrm_send_analytics - позволяет изменить отправляемые данные Google Analytics. 26 | 27 | > retailcrm_process_customer_custom_fields - позволяет изменить данные кастомных полей клиента при передачи из CRM -> CMS . 28 | 29 | > retailcrm_history_before_save - позволяет изменить данные заказа и клиента при передачи из CRM -> CMS. 30 | 31 | > retailcrm_process_order_custom_fields - позволяет изменить данные кастомных полей заказ при передачи из CRM -> CMS. 32 | 33 | > retailcrm_process_offer - позволяет изменить данные товара перед записью в ICML каталог. 34 | 35 | > retailcrm_process_order - позволяет изменить данные заказа при передачи из CMS -> CRM. 36 | 37 | > retailcrm_process_order_address - позволяет изменить адрес заказа при передачи из CMS -> CRM. 38 | 39 | > retailcrm_add_cron_interval - позволяет добавить пользовательский интервал времени. 40 | 41 | > retailcrm_cron_schedules - позволяет изменить интервал времени для cron задач. 42 | 43 | > retailcrm_shipping_list - позволяет изменить методы доставки с CMS. 44 | 45 | > retailcrm_order_create_after - позволяет проверить создание заказа и произвести дополнительные действия 46 | 47 | > retailcrm_order_update_after - позволяет проверить изменение заказа и произвести дополнительные действия 48 | 49 | > retailcrm_change_default_meta_fields - позволяет изменить список получаемых по умолчанию мета-полей 50 | 51 | **Пример использования:** 52 | ```php 53 | 2 | 13 | 14 | 15 | tests 16 | 17 | 18 | 19 | 20 | src 21 | 22 | src/include/api 23 | src/config 24 | src/languages 25 | src/readme.txt 26 | 27 | 28 | 29 | 30 | 31 | 32 | -------------------------------------------------------------------------------- /src/assets/css/debug-info.css: -------------------------------------------------------------------------------- 1 | .retail-cron-info-title { 2 | font-weight: bold; 3 | } 4 | -------------------------------------------------------------------------------- /src/assets/css/debug-info.min.css: -------------------------------------------------------------------------------- 1 | .retail-cron-info-title{font-weight:700} -------------------------------------------------------------------------------- /src/assets/css/meta-fields.css: -------------------------------------------------------------------------------- 1 | .retailcrm-meta-select { 2 | margin-right: 25px !important; 3 | float:left !important; 4 | width: 200px !important; 5 | text-align-last: center; 6 | border: 2px solid #2271b1; 7 | } 8 | 9 | .retailcrm-select-pair { 10 | margin-bottom: 30px; 11 | } 12 | 13 | .retailcrm-order-label, .retailcrm-customer-label { 14 | display: block; 15 | font-size: 14px !important; 16 | margin-top: 16px; 17 | margin-bottom: 10px; 18 | } 19 | 20 | .add-new-select-retailcrm { 21 | display:block; 22 | width: 200px !important; 23 | height: 34px; 24 | color: #4169e1; 25 | border-color: #4169e1; 26 | background: #f6f7f7; 27 | vertical-align: top; 28 | margin-top: 16px; 29 | margin-left: 128px; 30 | margin-bottom: 10px; 31 | } 32 | 33 | .delete-select-retailcrm { 34 | display: block; 35 | width: 34px; 36 | height: 34px; 37 | --weight: 0.5px; 38 | --aa: 0.5px; /* anti-aliasing */ 39 | --color: #4169e1; 40 | border-color: #4169e1; 41 | background: #f6f7f7; 42 | padding: 0; 43 | background: 44 | linear-gradient(45deg, transparent calc(50% - var(--weight) - var(--aa)), var(--color) calc(50% - var(--weight)), var(--color) calc(50% + var(--weight)), transparent calc(50% + var(--weight) + var(--aa))), 45 | linear-gradient(-45deg, transparent calc(50% - var(--weight) - var(--aa)), var(--color) calc(50% - var(--weight)), var(--color) calc(50% + var(--weight)), transparent calc(50% + var(--weight) + var(--aa))); 46 | } 47 | 48 | 49 | .red-selected-retailcrm { 50 | border: solid 3px red !important; 51 | animation: 2s blinker linear infinite; 52 | #transition: border-width 0.6s linear; 53 | } 54 | 55 | @keyframes blinker { 56 | 0% { opacity: 1.0; } 57 | 50% { opacity: 0.3; } 58 | 100% { opacity: 1.0; } 59 | } -------------------------------------------------------------------------------- /src/assets/css/meta-fields.min.css: -------------------------------------------------------------------------------- 1 | .retailcrm-meta-select{margin-right:25px!important;float:left!important;width:200px!important;text-align-last:center;border:2px solid #2271b1}.retailcrm-select-pair{margin-bottom:30px}.retailcrm-customer-label,.retailcrm-order-label{display:block;font-size:14px!important;margin-top:16px;margin-bottom:10px}.add-new-select-retailcrm{display:block;width:200px!important;height:34px;color:#4169e1;border-color:#4169e1;background:#f6f7f7;vertical-align:top;margin-top:16px;margin-left:128px;margin-bottom:10px}.delete-select-retailcrm{display:block;width:34px;height:34px;--weight:0.5px;--aa:0.5px;--color:#4169e1;border-color:#4169e1;background:#f6f7f7;padding:0;background:linear-gradient(45deg,transparent calc(50% - var(--weight) - var(--aa)),var(--color) calc(50% - var(--weight)),var(--color) calc(50% + var(--weight)),transparent calc(50% + var(--weight) + var(--aa))),linear-gradient(-45deg,transparent calc(50% - var(--weight) - var(--aa)),var(--color) calc(50% - var(--weight)),var(--color) calc(50% + var(--weight)),transparent calc(50% + var(--weight) + var(--aa)))}.red-selected-retailcrm{border:solid 3px red!important; animation:2s blinker linear infinite}@keyframes blinker{0%{opacity:1}50%{opacity:.3}100%{opacity:1}} -------------------------------------------------------------------------------- /src/assets/css/progress-bar.css: -------------------------------------------------------------------------------- 1 | .retail-progress { 2 | border-radius: 18px; 3 | border: 1px solid rgba(122, 122, 122, 0.15); 4 | width: 400px; 5 | height: 18px; 6 | overflow: hidden; 7 | transition: height 0.25s ease; 8 | } 9 | 10 | .retail-progress__loader { 11 | width: 0; 12 | border-radius: 18px; 13 | background: #4169e1; 14 | color: white; 15 | text-align: center; 16 | padding: 0 30px; 17 | font-size: 18px; 18 | font-weight: 600; 19 | transition: width 0.4s ease-in; 20 | line-height: 18px; 21 | box-sizing: border-box; 22 | } 23 | 24 | .retailcrm-hidden { 25 | display: none !important; 26 | } 27 | 28 | .retail-count-data-upload { 29 | margin-bottom: 20px; 30 | size: 30px; 31 | color: #4169e1; 32 | font-weight: bold; 33 | } -------------------------------------------------------------------------------- /src/assets/css/progress-bar.min.css: -------------------------------------------------------------------------------- 1 | <<<<<<< HEAD 2 | .retail-progress{border-radius:18px;border:1px solid rgba(122,122,122,.15);width:400px;height:18px;overflow:hidden;transition:height .25s ease}.retail-progress__loader{width:0;border-radius:18px;background:#4169e1;color:#fff;text-align:center;padding:0 30px;font-size:18px;font-weight:600;transition:width .4s ease-in;line-height:18px;box-sizing:border-box}.retail-hidden{display:none!important}.retail-count-data-upload{margin-bottom:20px;size:30px;color:#4169e1;font-weight:700} 3 | ======= 4 | .retail-progress{border-radius:18px;border:1px solid rgba(122,122,122,.15);width:400px;height:18px;overflow:hidden;transition:height .25s ease}.retail-progress__loader{width:0;border-radius:18px;background:#4169e1;color:#fff;text-align:center;padding:0 30px;font-size:18px;font-weight:600;transition:width .4s ease-in;line-height:18px;box-sizing:border-box}.retailcrm-hidden{display:none!important}.retail-count-data-upload{margin-bottom:20px;size:30px;color:#4169e1;font-weight:700} 5 | >>>>>>> Add mapping metadata fields in settings 6 | -------------------------------------------------------------------------------- /src/assets/css/retailcrm-loyalty-style.css: -------------------------------------------------------------------------------- 1 | .popup-fade-loyalty { 2 | display: none; 3 | } 4 | .popup-fade-loyalty:before { 5 | content: ''; 6 | background: #000; 7 | position: fixed; 8 | left: 0; 9 | top: 0; 10 | width: 100%; 11 | height: 100%; 12 | opacity: 0.7; 13 | z-index: 9999; 14 | } 15 | .popup-loyalty { 16 | position: fixed; 17 | top: 20%; 18 | left: 50%; 19 | padding: 20px; 20 | width: 1000px; 21 | margin-left: -500px; 22 | height: 50%; 23 | background: #fff; 24 | border: 1px solid orange; 25 | border-radius: 4px; 26 | z-index: 99999; 27 | opacity: 1; 28 | overflow: auto; 29 | } 30 | .popup-close-loyalty { 31 | position: absolute; 32 | top: 10px; 33 | right: 10px; 34 | } 35 | -------------------------------------------------------------------------------- /src/assets/css/whatsapp-icon.css: -------------------------------------------------------------------------------- 1 | .whatsapp-icon { 2 | position: fixed; 3 | z-index: 999; 4 | left: 0; 5 | bottom: 32px; 6 | width: 60px; 7 | height: 60px; 8 | box-sizing: border-box; 9 | } 10 | 11 | .whatsapp-icon_left { 12 | left: 32px; 13 | right: auto; 14 | } 15 | 16 | .whatsapp-icon_right { 17 | right: 32px; 18 | left: auto; 19 | } 20 | 21 | .whatsapp-icon__icon { 22 | width: 100%; 23 | height: 100%; 24 | } 25 | 26 | .chat-btn__text { 27 | color: #8A96A6; 28 | font-weight: 600; 29 | font-size: 8px; 30 | line-height: 10px; 31 | text-align: center; 32 | margin: 0 0 8px; 33 | white-space: nowrap; 34 | } 35 | -------------------------------------------------------------------------------- /src/assets/css/whatsapp-icon.min.css: -------------------------------------------------------------------------------- 1 | .whatsapp-icon{position:fixed;z-index: 999;left:0;bottom:32px;width:60px;height:60px;box-sizing:border-box}.whatsapp-icon_left{left:32px;right:auto}.whatsapp-icon_right{right:32px;left:auto}.whatsapp-icon__icon{width:100%;height:100%}.chat-btn__text{color:#8A96A6;font-weight:600;font-size:8px;line-height:10px;text-align:center;margin:0 0 8px;white-space:nowrap} -------------------------------------------------------------------------------- /src/assets/default/default_meta_fields.txt: -------------------------------------------------------------------------------- 1 | _backorders 2 | _billing_address_1 3 | _billing_address_2 4 | _billing_address_index 5 | _billing_city 6 | _billing_company 7 | _billing_country 8 | _billing_email 9 | _billing_first_name 10 | _billing_last_name 11 | _billing_phone 12 | _billing_postcode 13 | _billing_state 14 | _button_text 15 | _cart_discount 16 | _cart_discount_tax 17 | _cart_hash 18 | _children 19 | _completed_date 20 | _created_via 21 | _customer_ip_address 22 | _customer_user 23 | _customer_user_agent 24 | _date_completed 25 | _date_paid 26 | _download_expiry 27 | _download_limit 28 | _download_permissions_granted 29 | _downloadable 30 | _downloadable_files 31 | _edit_last 32 | _edit_lock 33 | _manage_stock 34 | _new_order_email_sent 35 | _order_currency 36 | _order_key 37 | _order_shipping 38 | _order_shipping_tax 39 | _order_stock_reduced 40 | _order_tax 41 | _order_total 42 | _order_version 43 | _paid_date 44 | _payment_method 45 | _payment_method_title 46 | _price 47 | _prices_include_tax 48 | _product_attributes 49 | _product_image_gallery 50 | _product_url 51 | _product_version 52 | _recorded_coupon_usage_counts 53 | _recorded_sales 54 | _refund_amount 55 | _refund_reason 56 | _refunded_by 57 | _refunded_payment 58 | _regular_price 59 | _sale_price 60 | _shipping_address_1 61 | _shipping_address_2 62 | _shipping_address_index 63 | _shipping_city 64 | _shipping_company 65 | _shipping_country 66 | _shipping_first_name 67 | _shipping_last_name 68 | _shipping_postcode 69 | _shipping_state 70 | _sku 71 | _sold_individually 72 | _stock 73 | _stock_status 74 | _stripe_charge_captured 75 | _stripe_currency 76 | _stripe_customer_id 77 | _stripe_fee 78 | _stripe_intent_id 79 | _stripe_net 80 | _stripe_source_id 81 | _tax_class 82 | _tax_status 83 | _thumbnail_id 84 | _transaction_id 85 | _variation_description 86 | _virtual 87 | _wc_attachment_source 88 | _wc_average_rating 89 | _wc_review_count 90 | _wp_attached_file 91 | _wp_attachment_metadata 92 | _wp_desired_post_slug 93 | _wp_old_slug 94 | _wp_page_template 95 | _wp_trash_meta_comments_status 96 | _wp_trash_meta_status 97 | _wp_trash_meta_time 98 | _wpcom_is_markdown 99 | _used_by 100 | attribute_logo 101 | attribute_pa_color 102 | attribute_pa_size 103 | total_sales 104 | coupon_amount 105 | date_expires 106 | discount_type 107 | exclude_sale_items 108 | free_shipping 109 | individual_use 110 | is_vat_exempt 111 | limit_usage_to_x_items 112 | usage_count 113 | usage_limit 114 | usage_limit_per_user 115 | _shipping_phone 116 | 117 | ## Customer 118 | 119 | _last_order 120 | _order_count 121 | _woocommerce_persistent_cart_1 122 | _woocommerce_tracks_anon_id 123 | admin_color 124 | billing_address_1 125 | billing_address_2 126 | billing_city 127 | billing_company 128 | billing_country 129 | billing_email 130 | billing_first_name 131 | billing_last_name 132 | billing_phone 133 | billing_postcode 134 | billing_state 135 | comment_shortcuts 136 | community-events-location 137 | default_password_nag 138 | description 139 | dismissed_maxmind_license_key_notice 140 | dismissed_no_secure_connection_notice 141 | dismissed_regenerating_thumbnails_notice 142 | dismissed_update_notice 143 | dismissed_wc_admin_notice 144 | dismissed_wp_pointers 145 | first_name 146 | last_name 147 | last_update 148 | locale 149 | paying_customer 150 | rich_editing 151 | session_tokens 152 | shipping_address_1 153 | shipping_address_2 154 | shipping_city 155 | shipping_company 156 | shipping_country 157 | shipping_first_name 158 | shipping_last_name 159 | shipping_method 160 | shipping_phone 161 | shipping_postcode 162 | shipping_state 163 | show_admin_bar_front 164 | show_welcome_panel 165 | syntax_highlighting 166 | use_ssl 167 | wc_last_active 168 | woocommerce_admin_activity_panel_inbox_last_read 169 | wp_capabilities 170 | wp_dashboard_quick_press_last_post_id 171 | wp_product_import_error_log 172 | wp_user_level 173 | wp_user-settings 174 | wp_user-settings-time 175 | wp_woocommerce_product_import_mapping 176 | closedpostboxes_shop_order 177 | metaboxhidden_shop_order 178 | wp__stripe_customer_id 179 | -------------------------------------------------------------------------------- /src/assets/js/retailcrm-cron-info.js: -------------------------------------------------------------------------------- 1 | jQuery(function () { 2 | function RetailcrmCronInfo() 3 | { 4 | this.title = jQuery('.debug_info_options').get(0) 5 | this.submitButton = jQuery('button[id="clear_cron_tasks"]').get(0); 6 | 7 | if (typeof this.title === 'undefined') { 8 | return false; 9 | } 10 | 11 | if (typeof this.submitButton === 'undefined') { 12 | return false; 13 | } 14 | 15 | this.icml = 0; 16 | this.history = 0; 17 | this.inventories = 0; 18 | this.messageSuccessful = ''; 19 | this.loyaltyUploadPrice = 0; 20 | 21 | this.adminUrl = AdminUrl.url; 22 | 23 | let _this = this; 24 | 25 | jQuery.ajax({ 26 | url: this.adminUrl + '/admin-ajax.php?action=cron_info', 27 | method: "POST", 28 | timeout: 0, 29 | data: {ajax: 1}, 30 | dataType: "json" 31 | }) 32 | .done(function (response) { 33 | _this.history = response.history; 34 | _this.icml = response.icml; 35 | _this.inventories = response.inventories; 36 | _this.messageSuccessful = response.translate.tr_successful; 37 | _this.loyaltyUploadPrice = response.loyaltyUploadPrice 38 | 39 | _this.displayInfoAboutCron( 40 | response.translate.tr_td_cron, 41 | response.translate.tr_td_icml, 42 | response.translate.tr_td_history, 43 | response.translate.tr_td_inventories, 44 | response.translate.tr_td_loyaltyUploadPrice 45 | ); 46 | }) 47 | 48 | this.clearCronTasks = this.clearCronTasks.bind(this); 49 | 50 | jQuery(this.submitButton).click(this.clearCronTasks); 51 | } 52 | 53 | RetailcrmCronInfo.prototype.displayInfoAboutCron = function (cron, icml, history, inventories, loyaltyUploadPrice) { 54 | this.table = jQuery(this.title).next(); 55 | this.table.append(''); 56 | this.infoTable = jQuery('tbody[class="retail-debug-info"]').get(0); 57 | 58 | jQuery(this.infoTable).append("" + cron + " : " + ""); 59 | jQuery(this.infoTable).append("" + icml + " " + this.icml + ""); 60 | jQuery(this.infoTable).append("" + history + " " + this.history + ""); 61 | jQuery(this.infoTable).append("" + inventories + " " + this.inventories + ""); 62 | jQuery(this.infoTable).append("" + loyaltyUploadPrice + "" + this.loyaltyUploadPrice + ""); 63 | } 64 | 65 | RetailcrmCronInfo.prototype.clearCronTasks = function () { 66 | let _this = this; 67 | 68 | jQuery.ajax({ 69 | type: "POST", 70 | url: this.adminUrl + '/admin-ajax.php?action=clear_cron_tasks', 71 | success: function (response) { 72 | alert(_this.messageSuccessful); 73 | } 74 | }); 75 | }; 76 | 77 | window.RetailcrmCronInfo = RetailcrmCronInfo; 78 | 79 | if (!(typeof RetailcrmCronInfo === 'undefined')) { 80 | new window.RetailcrmCronInfo(); 81 | } 82 | }); 83 | -------------------------------------------------------------------------------- /src/assets/js/retailcrm-loyalty-actions.js: -------------------------------------------------------------------------------- 1 | jQuery(function() { 2 | jQuery('#loyaltyRegisterForm').on("submit", function (event) { 3 | var termsCheck = jQuery('#termsLoyalty'); 4 | var privacyCheck = jQuery('#privacyLoyalty'); 5 | 6 | if (termsCheck.length) { 7 | if (!termsCheck.is(':checked')) { 8 | event.preventDefault(); 9 | return false; 10 | } 11 | } 12 | 13 | if (privacyCheck.length) { 14 | if (!privacyCheck.is(':checked')) { 15 | event.preventDefault(); 16 | return false; 17 | } 18 | } 19 | 20 | let phone = jQuery('#phoneLoyalty'); 21 | 22 | if (!phone.val().match(/(?:\+|\d)[\d\-\(\) ]{7,}\d/)) { 23 | 24 | if (!jQuery('#warningLoyaltyPhone').length) { 25 | phone.parent().append('' + messagePhone + '') 26 | } 27 | 28 | event.preventDefault(); 29 | return false; 30 | } else { 31 | jQuery('#warningLoyaltyPhone').remove(); 32 | } 33 | 34 | jQuery.ajax({ 35 | url: loyaltyUrl.url + '/admin-ajax.php?action=register_customer_loyalty', 36 | method: 'POST', 37 | timeout: 0, 38 | data: {ajax: 1, phone: phone.val(), userId: customerId}, 39 | dataType: 'json' 40 | }) 41 | .done(function (response) { 42 | if (response.hasOwnProperty('error')) { 43 | jQuery('#loyaltyRegisterForm').append('

'+ response.error + '

') 44 | 45 | event.preventDefault(); 46 | return false; 47 | } else { 48 | location.reload(); 49 | } 50 | }) 51 | 52 | event.preventDefault(); 53 | }); 54 | 55 | jQuery('#loyaltyActivateForm').on("submit", function (event) { 56 | var activateCheckbox = jQuery('#loyaltyActiveCheckbox'); 57 | 58 | if (activateCheckbox.length) { 59 | if (!activateCheckbox.is(':checked')) { 60 | event.preventDefault(); 61 | return false; 62 | } 63 | } 64 | 65 | jQuery.ajax({ 66 | url: loyaltyUrl.url + '/admin-ajax.php?action=activate_customer_loyalty', 67 | method: 'POST', 68 | timeout: 0, 69 | data: {ajax: 1, loyaltyId: loyaltyId}, 70 | dataType: 'json' 71 | }) 72 | .done(function (response) { 73 | if (response.hasOwnProperty('error')) { 74 | jQuery('#loyaltyRegisterForm').append('

'+ response.error + '

') 75 | 76 | event.preventDefault(); 77 | return false; 78 | } else { 79 | location.reload(); 80 | } 81 | }) 82 | 83 | event.preventDefault(); 84 | }); 85 | 86 | jQuery('.popup-open-loyalty').click(function() { 87 | if (jQuery(this).attr('id') === 'terms-popup') { 88 | jQuery('#popup-loyalty-text').html(termsLoyalty); 89 | } else { 90 | jQuery('#popup-loyalty-text').html(privacyLoyalty); 91 | } 92 | 93 | jQuery('.popup-fade-loyalty').fadeIn(); 94 | return false; 95 | }); 96 | 97 | jQuery('.popup-close-loyalty').click(function() { 98 | jQuery(this).parents('.popup-fade-loyalty').fadeOut(); 99 | return false; 100 | }); 101 | 102 | jQuery(document).keydown(function(e) { 103 | if (e.keyCode === 27) { // Key Escape 104 | e.stopPropagation(); 105 | jQuery('.popup-fade-loyalty').fadeOut(); 106 | } 107 | }); 108 | 109 | jQuery('.popup-fade-loyalty').click(function(e) { 110 | if (jQuery(e.target).closest('.popup-loyalty').length == 0) { 111 | jQuery(this).fadeOut(); 112 | } 113 | }); 114 | 115 | jQuery('#phoneLoyalty').keydown(function (e) { 116 | let key = e.key; 117 | 118 | return (key >= '0' && key <= '9') || key == '+' || key == '(' || key == ')'|| key == '-' || 119 | key == 'ArrowLeft' || key == 'ArrowRight' || key == 'Delete' || key == 'Backspace'; 120 | }); 121 | }); 122 | -------------------------------------------------------------------------------- /src/assets/js/retailcrm-loyalty-cart.js: -------------------------------------------------------------------------------- 1 | function inputLoyaltyCode() { 2 | let couponInput = document.getElementById('coupon_code'); 3 | let couponCode = document.getElementById('input_loyalty_code'); 4 | 5 | couponInput.value = couponCode.innerText; 6 | } 7 | -------------------------------------------------------------------------------- /src/assets/js/retailcrm-loyalty.js: -------------------------------------------------------------------------------- 1 | jQuery(function() { 2 | if (jQuery('#woocommerce_integration-retailcrm_loyalty').is(':checked')) { 3 | checkActiveCoupon(); 4 | } 5 | 6 | jQuery('#woocommerce_integration-retailcrm_loyalty').change(function () { 7 | if (this.checked) { 8 | checkActiveCoupon(); 9 | } 10 | }) 11 | 12 | function checkActiveCoupon() 13 | { 14 | jQuery.ajax({ 15 | url: AdminUrl.url + '/admin-ajax.php?action=get_status_coupon', 16 | method: 'POST', 17 | timeout: 0, 18 | data: {ajax: 1}, 19 | dataType: 'json' 20 | }) 21 | .done(function (response) { 22 | if (response.coupon_status !== 'yes') { 23 | var checkElement = jQuery('#woocommerce_integration-retailcrm_loyalty'); 24 | checkElement.parent().css('color', 'red'); 25 | checkElement.css('border-color', 'red'); 26 | checkElement.prop('checked', false); 27 | 28 | if (!jQuery('#coupon_warning').length) { 29 | checkElement.parent().parent().append( 30 | "

" + response.translate.coupon_warning + "

" 31 | ); 32 | } 33 | } 34 | }) 35 | } 36 | }); 37 | -------------------------------------------------------------------------------- /src/assets/js/retailcrm-module-settings.js: -------------------------------------------------------------------------------- 1 | jQuery(function () { 2 | if (document.querySelector('#woocommerce_integration-retailcrm_bind_by_sku')) { 3 | document.querySelector('#woocommerce_integration-retailcrm_bind_by_sku').onchange = function() { 4 | let useXmlId = this.checked ? 'yes' : 'no'; 5 | 6 | document.querySelector('.submit').onmousedown = function() { 7 | jQuery.ajax({ 8 | url: AdminUrl.url + '/admin-ajax.php?action=generate_icml', 9 | method: 'POST', 10 | timeout: 0, 11 | data: {useXmlId: useXmlId}, 12 | dataType: 'json' 13 | }) 14 | } 15 | }; 16 | } 17 | }) 18 | -------------------------------------------------------------------------------- /src/assets/js/retailcrm-tracker-interface.js: -------------------------------------------------------------------------------- 1 | jQuery(document).ready(function () { 2 | const assistantCode = jQuery('#woocommerce_integration-retailcrm_online_assistant'); 3 | const textarea = jQuery('#woocommerce_integration-retailcrm_tracker_settings'); 4 | const trackerCheckboxId = 'retailcrm_activation_tracker'; 5 | const eventsContainerId = 'retailcrm_events_container'; 6 | 7 | let trackerContainer = jQuery('
'); 8 | let eventsContainer = jQuery('
'); 9 | 10 | assistantCode.after(trackerContainer); 11 | trackerContainer.after(eventsContainer); 12 | 13 | function getSavedData() { 14 | try { 15 | const text = textarea.val().trim(); 16 | return text ? JSON.parse(text) : { tracker_enabled: false, tracked_events: [] }; 17 | } catch (exception) { 18 | return { tracker_enabled: false, tracked_events: [] }; 19 | } 20 | } 21 | 22 | function renderMainCheckbox() { 23 | if (jQuery('#' + trackerCheckboxId).length === 0) { 24 | const savedData = getSavedData(); 25 | const trackerCheckbox = ` 26 | 30 | `; 31 | 32 | trackerContainer.html(trackerCheckbox); 33 | } 34 | } 35 | 36 | function renderEventCheckboxes() { 37 | const savedData = getSavedData(); 38 | const events = [ 39 | {value: 'page_view', label: retailcrm_localized.page_view, title: retailcrm_localized.page_view_desc}, 40 | {value: 'cart', label: retailcrm_localized.cart, title: retailcrm_localized.cart_desc}, 41 | {value: 'open_cart', label: retailcrm_localized.open_cart, title: retailcrm_localized.open_cart_desc} 42 | ]; 43 | 44 | let checkboxes = ''; 45 | events.forEach(event => { 46 | const isChecked = savedData.tracked_events.includes(event.value); 47 | checkboxes += ` 48 |
53 | `; 54 | }); 55 | 56 | eventsContainer.html(checkboxes); 57 | } 58 | 59 | function updateTextarea() { 60 | const isTrackerEnabled = jQuery('#' + trackerCheckboxId).is(':checked'); 61 | const selectedEvents = jQuery('.retailcrm-event:checked').map(function() { 62 | return this.value; 63 | }).get(); 64 | 65 | const data = { 66 | tracker_enabled: isTrackerEnabled, 67 | tracked_events: selectedEvents 68 | }; 69 | 70 | textarea.val(JSON.stringify(data)); 71 | } 72 | 73 | function clearAll() { 74 | trackerContainer.empty(); 75 | eventsContainer.empty(); 76 | textarea.val(''); 77 | } 78 | 79 | function updateDisplay() { 80 | const value = assistantCode.val().trim(); 81 | 82 | if (value === '') { 83 | clearAll(); 84 | } else { 85 | renderMainCheckbox(); 86 | renderEventCheckboxes(); 87 | 88 | if (!jQuery('#' + trackerCheckboxId).is(':checked')) { 89 | eventsContainer.hide(); 90 | } else { 91 | eventsContainer.show(); 92 | } 93 | } 94 | } 95 | 96 | assistantCode.on('input', updateDisplay); 97 | trackerContainer.on('change', '#' + trackerCheckboxId, function() { 98 | if (jQuery(this).is(':checked')) { 99 | eventsContainer.show(); 100 | } else { 101 | eventsContainer.hide(); 102 | } 103 | updateTextarea(); 104 | }); 105 | 106 | eventsContainer.on('change', '.retailcrm-event', updateTextarea); 107 | 108 | updateDisplay(); 109 | }); -------------------------------------------------------------------------------- /src/assets/js/retailcrm-tracker.js: -------------------------------------------------------------------------------- 1 | let cartListenersInitialized = false; 2 | 3 | function startTrack(...trackerEvents) 4 | { 5 | try { 6 | if (trackerEvents.includes('page_view')) { 7 | sendProductView(); 8 | } 9 | 10 | if (trackerEvents.includes('open_cart')) { 11 | sendCartView() 12 | } 13 | 14 | if (trackerEvents.includes('cart')) { 15 | if (!cartListenersInitialized) { 16 | jQuery(document.body).on('added_to_cart updated_cart_totals', sendCartChange); 17 | } 18 | 19 | jQuery(document.body).on('click', '.single_add_to_cart_button', function () { 20 | sessionStorage.setItem('click_single__add_to_cart_button', '1'); 21 | }); 22 | 23 | if (sessionStorage.getItem('click_single__add_to_cart_button') === '1') { 24 | sessionStorage.removeItem('click_single__add_to_cart_button'); 25 | 26 | sendCartChange(); 27 | } 28 | 29 | cartListenersInitialized = true; 30 | } 31 | } catch (error) { 32 | console.error('Ошибка при выполнении трекинга данных', error) 33 | } 34 | 35 | function sendProductView() 36 | { 37 | let offerId = jQuery('.single_add_to_cart_button').val() || jQuery('input[name="product_id"]').val(); 38 | 39 | if (offerId) { 40 | getCustomerInfo().then(function (customer) { 41 | if (!customer?.externalId) { 42 | return; 43 | } 44 | 45 | setTimeout(() => { 46 | ocapi.setCustomerSiteId(String(customer.externalId)); 47 | ocapi.event('page_view', { offer_external_id: offerId }) 48 | }, 1000) 49 | }); 50 | } 51 | } 52 | 53 | function sendCartView() 54 | { 55 | if (jQuery(document.body).hasClass('woocommerce-cart')) { 56 | getCustomerInfo().then(function (customer) { 57 | if (!customer?.email || !customer?.externalId) { 58 | return; 59 | } 60 | 61 | setTimeout(function() { 62 | ocapi.setCustomerSiteId(String(customer.externalId)); 63 | ocapi.event('open_cart', { customer_email: customer.email}); 64 | }, 1000); 65 | }); 66 | } 67 | } 68 | 69 | function sendCartChange() 70 | { 71 | let cart = {}; 72 | cart.items = []; 73 | 74 | getCartItems().then(function(cartItems) { 75 | cartItems.forEach(item => { 76 | cart.items.push({ 77 | external_id: item.id, 78 | xml_id: item.sku, 79 | price: item.price, 80 | quantity: item.quantity 81 | }); 82 | }); 83 | }); 84 | 85 | if (cart.items !== []) { 86 | getCustomerInfo().then(function (customer) { 87 | if (!customer?.externalId) { 88 | return; 89 | } 90 | 91 | setTimeout(function() { 92 | ocapi.setCustomerSiteId(String(customer.externalId)); 93 | ocapi.event('cart', cart); 94 | }, 1000); 95 | }); 96 | } 97 | } 98 | 99 | async function getCustomerInfo() { 100 | try { 101 | const response = await jQuery.ajax({ 102 | url: AdminUrl.url + '/admin-ajax.php?action=get_customer_info_for_tracker', 103 | method: 'POST', 104 | data: { ajax: 1 }, 105 | dataType: 'json' 106 | }) 107 | 108 | return response.success ? response.data : [] 109 | } catch (error) { 110 | console.error('AJAX ошибка:', error); 111 | 112 | return []; 113 | } 114 | } 115 | 116 | async function getCartItems() { 117 | try { 118 | const response = await jQuery.ajax({ 119 | url: AdminUrl.url + '/admin-ajax.php?action=get_cart_items_for_tracker', 120 | method: 'POST', 121 | data: { ajax: 1 }, 122 | dataType: 'json' 123 | }) 124 | 125 | return response.success ? response.data : [] 126 | } catch (error) { 127 | console.error('AJAX ошибка:', error); 128 | 129 | return []; 130 | } 131 | } 132 | } 133 | 134 | -------------------------------------------------------------------------------- /src/include/abstracts/class-wc-retailcrm-abstract-builder.php: -------------------------------------------------------------------------------- 1 | 10 | * @license http://retailcrm.ru Proprietary 11 | * @link http://retailcrm.ru 12 | * @see http://help.retailcrm.ru 13 | */ 14 | abstract class WC_Retailcrm_Abstract_Builder implements WC_Retailcrm_Builder_Interface 15 | { 16 | /** @var array|mixed $data */ 17 | protected $data; 18 | 19 | /** 20 | * @param array|mixed $data 21 | * 22 | * @return \WC_Retailcrm_Abstract_Builder 23 | */ 24 | public function setData($data) 25 | { 26 | $this->data = $data; 27 | return $this; 28 | } 29 | 30 | /** 31 | * @return array|mixed 32 | * 33 | * @codeCoverageIgnore 34 | */ 35 | public function getData() 36 | { 37 | return $this->data; 38 | } 39 | 40 | /** 41 | * @return $this|\WC_Retailcrm_Builder_Interface 42 | */ 43 | public function reset() 44 | { 45 | $this->data = []; 46 | 47 | return $this; 48 | } 49 | 50 | /** 51 | * Returns key if it's present in data array (or object which implements ArrayAccess). 52 | * Returns default value if key is not present in data, or data is not accessible as array. 53 | * 54 | * @param string $key 55 | * @param mixed $default 56 | * 57 | * @return mixed 58 | */ 59 | protected function dataValue($key, $default = '') 60 | { 61 | return self::arrayValue($this->data, $key, $default); 62 | } 63 | 64 | /** 65 | * Returns key from array if it's present in array 66 | * 67 | * @param array|\ArrayObject $data 68 | * @param mixed $key 69 | * @param string $default 70 | * 71 | * @return mixed|string 72 | */ 73 | protected static function arrayValue($data, $key, $default = '') 74 | { 75 | if (!is_array($data) && !($data instanceof ArrayAccess)) { 76 | return $default; 77 | } 78 | 79 | if (array_key_exists($key, $data) && !empty($data[$key])) { 80 | return $data[$key]; 81 | } 82 | 83 | return $default; 84 | } 85 | 86 | /** 87 | * @return \WC_Retailcrm_Builder_Interface 88 | */ 89 | abstract public function build(); 90 | 91 | /** 92 | * Returns builder result. Should return null if WC_Retailcrm_Abstract_Builder::isBuilt() == false. 93 | * 94 | * @return mixed|null 95 | */ 96 | abstract public function getResult(); 97 | } 98 | -------------------------------------------------------------------------------- /src/include/abstracts/class-wc-retailcrm-abstracts-address.php: -------------------------------------------------------------------------------- 1 | 10 | * @license http://retailcrm.ru Proprietary 11 | * @link http://retailcrm.ru 12 | * @see http://help.retailcrm.ru 13 | */ 14 | abstract class WC_Retailcrm_Abstracts_Address extends WC_Retailcrm_Abstracts_Data 15 | { 16 | /** 17 | * Divider for order delivery address_1 and address_2 18 | */ 19 | const ADDRESS_LINE_DIVIDER = ' || '; 20 | 21 | /** 22 | * Returns shipping address from order. 23 | * 24 | * @param \WC_Order $order 25 | * 26 | * @return array 27 | */ 28 | protected function getOrderAddress($order) 29 | { 30 | if (!$order instanceof WC_Order) { 31 | return []; 32 | } 33 | 34 | return [ 35 | 'index' => $order->get_shipping_postcode(), 36 | 'city' => $order->get_shipping_city(), 37 | 'region' => $this->getRegion($order->get_shipping_country(), $order->get_shipping_state()), 38 | 'text' => $this->getText($order, 'order'), 39 | ]; 40 | } 41 | 42 | /** 43 | * Returns billing address from customer. 44 | * 45 | * @param \WC_Customer $customer 46 | * @param \WC_Order $order 47 | * 48 | * @return array 49 | */ 50 | protected function getCustomerAddress($customer, $order) 51 | { 52 | if (!$customer instanceof WC_Customer) { 53 | return []; 54 | } 55 | 56 | $customerBillingAddress = $customer->get_billing_address(); 57 | 58 | if ($order instanceof WC_Order && empty($customerBillingAddress)) { 59 | return [ 60 | 'index' => $order->get_billing_postcode(), 61 | 'countryIso' => $this->getCountryCode($order->get_billing_country()), 62 | 'region' => $this->getRegion($order->get_billing_country(), $order->get_billing_state()), 63 | 'city' => $order->get_billing_city(), 64 | 'text' => $this->getText($order), 65 | ]; 66 | } else { 67 | return [ 68 | 'index' => $customer->get_billing_postcode(), 69 | 'countryIso' => $this->getCountryCode($customer->get_billing_country()), 70 | 'region' => $this->getRegion($customer->get_billing_country(), $customer->get_billing_state()), 71 | 'city' => $customer->get_billing_city(), 72 | 'text' => $this->getText($customer), 73 | ]; 74 | } 75 | } 76 | 77 | /** 78 | * Glue two addresses 79 | * 80 | * @param string $address1 81 | * @param string $address2 82 | * 83 | * @return string 84 | */ 85 | protected function joinAddresses(string $address1 = '', string $address2 = '') 86 | { 87 | return implode(self::ADDRESS_LINE_DIVIDER, array_filter([$address1, $address2])); 88 | } 89 | 90 | /** 91 | * Validate countryIso. Check if a given code represents a valid ISO 3166-1 alpha-2 code. 92 | * 93 | * @param $countryCode 94 | * 95 | * @return string 96 | */ 97 | private function getCountryCode($countryCode) 98 | { 99 | $countries = new WC_Countries(); 100 | 101 | return $countries->country_exists($countryCode) ? $countryCode : ''; 102 | } 103 | 104 | /** 105 | * Returns state name by it's code 106 | * 107 | * @param string $countryCode 108 | * @param string $stateCode 109 | * 110 | * @return string 111 | */ 112 | protected function getRegion(string $countryCode, string $stateCode) 113 | { 114 | if (preg_match('/^[A-Z\-0-9]{0,5}$/', $stateCode) && !is_null($countryCode)) { 115 | $countriesProvider = new WC_Countries(); 116 | $states = $countriesProvider->get_states($countryCode); 117 | 118 | if (!empty($states) && array_key_exists($stateCode, $states)) { 119 | return (string) $states[$stateCode]; 120 | } 121 | } 122 | 123 | return $stateCode; 124 | } 125 | 126 | /** 127 | * Returns data for CRM field 'text'. 128 | * If type entity equals 'order', get address for order and use shipping address, 129 | * else get address for customer and use billing address. 130 | * 131 | * @return string 132 | */ 133 | protected function getText($wcEntity, $typeEntity = 'customer') 134 | { 135 | if ($typeEntity === 'order') { 136 | return empty($wcEntity->get_shipping_address_2()) 137 | ? $wcEntity->get_shipping_address_1() 138 | : $this->joinAddresses($wcEntity->get_shipping_address_1(), $wcEntity->get_shipping_address_2()); 139 | } else { 140 | return empty($wcEntity->get_billing_address_2()) 141 | ? $wcEntity->get_billing_address_1() 142 | : $this->joinAddresses($wcEntity->get_billing_address_1(), $wcEntity->get_billing_address_2()); 143 | } 144 | } 145 | } 146 | -------------------------------------------------------------------------------- /src/include/abstracts/class-wc-retailcrm-abstracts-data.php: -------------------------------------------------------------------------------- 1 | 10 | * @license http://retailcrm.ru Proprietary 11 | * @link http://retailcrm.ru 12 | * @see http://help.retailcrm.ru 13 | */ 14 | abstract class WC_Retailcrm_Abstracts_Data 15 | { 16 | /** @var array */ 17 | protected $data = []; 18 | 19 | /** 20 | * @param $data 21 | * 22 | * @return self 23 | */ 24 | abstract public function build($data); 25 | 26 | /** 27 | * @codeCoverageIgnore 28 | */ 29 | protected function setField($field, $value) 30 | { 31 | if (isset($this->data[$field]) && \gettype($value) !== \gettype($this->data[$field])) { 32 | return false; 33 | } 34 | 35 | $this->data[$field] = $value; 36 | 37 | return true; 38 | } 39 | 40 | /** 41 | * @param $fields 42 | */ 43 | protected function setDataFields($fields) 44 | { 45 | if (!empty($fields)) { 46 | foreach ($fields as $field => $value) { 47 | $this->setField($field, $value); 48 | } 49 | } 50 | } 51 | 52 | /** 53 | * @return array 54 | */ 55 | public function getData() 56 | { 57 | return $this->data; 58 | } 59 | } 60 | -------------------------------------------------------------------------------- /src/include/api/class-wc-retailcrm-exception-curl.php: -------------------------------------------------------------------------------- 1 | 11 | * @license https://opensource.org/licenses/MIT MIT License 12 | * @link http://retailcrm.ru/docs/Developers/ApiVersion5 13 | */ 14 | class WC_Retailcrm_Exception_Curl extends \RuntimeException 15 | { 16 | 17 | } 18 | 19 | -------------------------------------------------------------------------------- /src/include/api/class-wc-retailcrm-exception-json.php: -------------------------------------------------------------------------------- 1 | 11 | * @license https://opensource.org/licenses/MIT MIT License 12 | * @link http://retailcrm.ru/docs/Developers/ApiVersion5 13 | */ 14 | class WC_Retailcrm_Exception_Json extends \DomainException 15 | { 16 | 17 | } 18 | 19 | -------------------------------------------------------------------------------- /src/include/api/class-wc-retailcrm-proxy.php: -------------------------------------------------------------------------------- 1 | 12 | * @license https://opensource.org/licenses/MIT MIT License 13 | * @link http://retailcrm.ru/docs/Developers/ApiVersion5 14 | */ 15 | class WC_Retailcrm_Proxy 16 | { 17 | protected $retailcrm; 18 | protected $corporateEnabled; 19 | 20 | public function __construct($api_url, $api_key, $corporateEnabled = false) 21 | { 22 | $this->corporateEnabled = $corporateEnabled; 23 | 24 | if (!class_exists('WC_Retailcrm_Client_V5')) { 25 | include_once(WC_Integration_Retailcrm::checkCustomFile('include/api/class-wc-retailcrm-client-v5.php')); 26 | } 27 | 28 | $this->retailcrm = new WC_Retailcrm_Client_V5($api_url, $api_key); 29 | } 30 | 31 | public function getCorporateEnabled(): ?bool 32 | { 33 | return $this->corporateEnabled; 34 | } 35 | 36 | /** 37 | * Response will be omitted in logs for those methods 38 | * 39 | * @return string[] 40 | */ 41 | private function methodsWithoutFullLog(): array 42 | { 43 | $methodsList = [ 44 | 'statusesList', 45 | 'paymentTypesList', 46 | 'deliveryTypesList', 47 | 'orderMethodsList', 48 | 'storesList', 49 | ]; 50 | 51 | foreach ($methodsList as $key => $method) { 52 | $method = get_class($this->retailcrm) . '::' . $method; 53 | $methodsList[$key] = $method; 54 | } 55 | 56 | return $methodsList; 57 | } 58 | 59 | public function __call($method, $arguments) 60 | { 61 | $response = null; 62 | $called = sprintf('%s::%s', get_class($this->retailcrm), $method); 63 | 64 | try { 65 | $response = $this->getResponse($method, $arguments); 66 | 67 | if (is_string($response)) { 68 | WC_Retailcrm_Logger::info($method, $response, [], WC_Retailcrm_Logger::RESPONSE); 69 | 70 | return $response; 71 | } 72 | 73 | if (!$response instanceof WC_Retailcrm_Response) { 74 | WC_Retailcrm_Logger::error( 75 | $method, 76 | sprintf("[%s] null (no response whatsoever)", $called), 77 | [], 78 | WC_Retailcrm_Logger::RESPONSE 79 | ); 80 | 81 | return null; 82 | } 83 | 84 | $this->logResponse($response, $method, $called); 85 | } catch (WC_Retailcrm_Exception_Curl|WC_Retailcrm_Exception_Json|InvalidArgumentException $exception) { 86 | WC_Retailcrm_Logger::exception($method, $exception); 87 | } 88 | 89 | return $response instanceof WC_Retailcrm_Response ? $response : new WC_Retailcrm_Response(900, '{}'); 90 | } 91 | 92 | private function getResponse($method, $arguments) 93 | { 94 | WC_Retailcrm_Logger::info( 95 | $method, 96 | $arguments === [] ? '[no params]' : '[with params]', 97 | ['params' => $arguments], 98 | WC_Retailcrm_Logger::REQUEST 99 | ); 100 | 101 | return call_user_func_array(array($this->retailcrm, $method), $arguments); 102 | } 103 | 104 | private function logResponse(WC_Retailcrm_Response $response, $method, $called): void 105 | { 106 | if ($response->isSuccessful()) { 107 | if (in_array($called, $this->methodsWithoutFullLog())) { 108 | WC_Retailcrm_Logger::info( 109 | $method, 110 | 'Ok', 111 | ['body' => 'request was successful, but response is omitted'], 112 | WC_Retailcrm_Logger::RESPONSE 113 | ); 114 | } else { 115 | WC_Retailcrm_Logger::info( 116 | $method, 117 | 'Ok', 118 | ['body' => json_decode($response->getRawResponse(), true)], 119 | WC_Retailcrm_Logger::RESPONSE 120 | ); 121 | } 122 | } else { 123 | WC_Retailcrm_Logger::error( 124 | $method, 125 | sprintf( 126 | "Error: [HTTP-code %s] %s", 127 | $response->getStatusCode(), 128 | $response->getErrorString() 129 | ), 130 | ['response' => json_decode($response->getRawResponse(), true)], 131 | WC_Retailcrm_Logger::RESPONSE 132 | ); 133 | } 134 | } 135 | } 136 | endif; 137 | -------------------------------------------------------------------------------- /src/include/api/class-wc-retailcrm-request.php: -------------------------------------------------------------------------------- 1 | 19 | * @license https://opensource.org/licenses/MIT MIT License 20 | * @link http://retailcrm.ru/docs/Developers/ApiVersion5 21 | */ 22 | class WC_Retailcrm_Request 23 | { 24 | const METHOD_GET = 'GET'; 25 | const METHOD_POST = 'POST'; 26 | 27 | protected $url; 28 | protected $defaultParameters; 29 | 30 | /** 31 | * Client constructor. 32 | * 33 | * @param string $url api url 34 | * @param array $defaultParameters array of parameters 35 | * 36 | */ 37 | public function __construct($url, array $defaultParameters = []) 38 | { 39 | $this->url = $url; 40 | $this->defaultParameters = $defaultParameters; 41 | } 42 | 43 | /** 44 | * Make HTTP request 45 | * 46 | * @param string $path request url 47 | * @param string $method (default: 'GET') 48 | * @param array $parameters (default: array()) 49 | * 50 | * @SuppressWarnings(PHPMD.ExcessiveParameterList) 51 | * 52 | * @throws \InvalidArgumentException 53 | * @throws WC_Retailcrm_Exception_Curl 54 | * 55 | * @return WC_Retailcrm_Response 56 | */ 57 | public function makeRequest( 58 | $path, 59 | $method, 60 | array $parameters = [] 61 | ) { 62 | $allowedMethods = [self::METHOD_GET, self::METHOD_POST]; 63 | 64 | if (!in_array($method, $allowedMethods, false)) { 65 | throw new \InvalidArgumentException( 66 | sprintf( 67 | 'Method "%s" is not valid. Allowed methods are %s', 68 | $method, 69 | implode(', ', $allowedMethods) 70 | ) 71 | ); 72 | } 73 | 74 | $parameters = self::METHOD_GET === $method 75 | ? array_merge($this->defaultParameters, $parameters, [ 76 | 'cms_source' => 'WordPress', 77 | 'cms_version' => function_exists('get_bloginfo') ? get_bloginfo('version') : '', 78 | 'woo_version' => WC()->version ?? '', 79 | 'php_version' => function_exists('phpversion') ? phpversion() : '', 80 | 'module_version' => WC_Integration_Retailcrm::MODULE_VERSION, 81 | 'ga_option_is_active' => getOptionByCode('ua') === WC_Retailcrm_Abstracts_Settings::YES, 82 | ]) 83 | : array_merge($this->defaultParameters, $parameters); 84 | 85 | $url = $this->url . $path; 86 | 87 | if (self::METHOD_GET === $method && count($parameters)) { 88 | $url .= '?' . http_build_query($parameters, '', '&'); 89 | } 90 | 91 | $curlHandler = curl_init(); 92 | curl_setopt($curlHandler, CURLOPT_URL, $url); 93 | curl_setopt($curlHandler, CURLOPT_RETURNTRANSFER, 1); 94 | curl_setopt($curlHandler, CURLOPT_FAILONERROR, false); 95 | curl_setopt($curlHandler, CURLOPT_SSL_VERIFYPEER, false); 96 | curl_setopt($curlHandler, CURLOPT_SSL_VERIFYHOST, false); 97 | curl_setopt($curlHandler, CURLOPT_TIMEOUT, 30); 98 | curl_setopt($curlHandler, CURLOPT_CONNECTTIMEOUT, 30); 99 | 100 | if (self::METHOD_POST === $method) { 101 | curl_setopt($curlHandler, CURLOPT_POST, true); 102 | curl_setopt($curlHandler, CURLOPT_POSTFIELDS, $parameters); 103 | } 104 | 105 | $responseBody = curl_exec($curlHandler); 106 | $statusCode = curl_getinfo($curlHandler, CURLINFO_HTTP_CODE); 107 | $errno = curl_errno($curlHandler); 108 | $error = curl_error($curlHandler); 109 | 110 | curl_close($curlHandler); 111 | 112 | if ($errno) { 113 | throw new WC_Retailcrm_Exception_Curl($error, $errno); 114 | } 115 | 116 | return new WC_Retailcrm_Response($statusCode, $responseBody); 117 | } 118 | } 119 | -------------------------------------------------------------------------------- /src/include/api/index.php: -------------------------------------------------------------------------------- 1 | 11 | * @license http://retailcrm.ru Proprietary 12 | * @link http://retailcrm.ru 13 | * @see http://help.retailcrm.ru 14 | */ 15 | class WC_Retailcrm_Cart 16 | { 17 | protected $apiClient; 18 | protected $dateFormat; 19 | 20 | protected $settings; 21 | 22 | public function __construct($apiClient, $settings) 23 | { 24 | $this->apiClient = $apiClient; 25 | $this->settings = $settings; 26 | $this->dateFormat = 'Y-m-d H:i:sP'; 27 | } 28 | 29 | public function isCartExist($customerId, $site): bool 30 | { 31 | $getCart = $this->apiClient->cartGet($customerId, $site); 32 | 33 | return !empty($getCart['cart']['externalId']); 34 | } 35 | 36 | public function processCart($customerId, $cartItems, $site, $isCartExist): bool 37 | { 38 | $isSuccessful = false; 39 | 40 | try { 41 | $crmCart = [ 42 | 'customer' => ['externalId' => $customerId], 43 | 'clearAt' => null, 44 | 'updatedAt' => date($this->dateFormat), 45 | 'link' => wc_get_cart_url(), 46 | ]; 47 | 48 | if (isset($this->settings['daemon_collector']) && $this->settings['daemon_collector'] === 'no') { 49 | $crmCart['droppedAt'] = date($this->dateFormat); 50 | } 51 | 52 | // If new cart, need set createdAt and externalId 53 | if (!$isCartExist) { 54 | $crmCart['createdAt'] = date($this->dateFormat); 55 | $crmCart['externalId'] = $customerId . uniqid('_', true); 56 | } 57 | 58 | // If you delete one by one 59 | if (empty($cartItems)) { 60 | return $this->clearCart($customerId, $site, $isCartExist); 61 | } 62 | 63 | $useXmlId = isset($this->settings['bind_by_sku']) && $this->settings['bind_by_sku'] === WC_Retailcrm_Base::YES; 64 | 65 | foreach ($cartItems as $item) { 66 | $product = $item['data']; 67 | 68 | $crmCart['items'][] = [ 69 | 'offer' => $useXmlId ? ['xmlId' => $product->get_sku()] : ['externalId' => $product->get_id()], 70 | 'quantity' => $item['quantity'], 71 | 'createdAt' => $product->get_date_created()->date($this->dateFormat) ?? date($this->dateFormat), 72 | 'updatedAt' => $product->get_date_modified()->date($this->dateFormat) ?? date($this->dateFormat), 73 | 'price' => wc_get_price_including_tax($product), 74 | ]; 75 | } 76 | 77 | $crmCart = apply_filters( 78 | 'retailcrm_process_cart', 79 | WC_Retailcrm_Plugin::clearArray($crmCart), 80 | $cartItems 81 | ); 82 | 83 | $setResponse = $this->apiClient->cartSet($crmCart, $site); 84 | $isSuccessful = $setResponse->isSuccessful() && !empty($setResponse['success']); 85 | } catch (Throwable $exception) { 86 | WC_Retailcrm_Logger::exception(__METHOD__, $exception); 87 | } 88 | 89 | return $isSuccessful; 90 | } 91 | 92 | public function clearCart($customerId, $site, $isCartExist): bool 93 | { 94 | $isSuccessful = false; 95 | 96 | try { 97 | if ($isCartExist) { 98 | $crmCart = ['customer' => ['externalId' => $customerId], 'clearedAt' => date($this->dateFormat)]; 99 | $clearResponse = $this->apiClient->cartClear($crmCart, $site); 100 | $isSuccessful = $clearResponse->isSuccessful() && !empty($clearResponse['success']); 101 | } 102 | } catch (Throwable $exception) { 103 | WC_Retailcrm_Logger::exception(__METHOD__, $exception); 104 | } 105 | 106 | return $isSuccessful; 107 | } 108 | } 109 | endif; 110 | -------------------------------------------------------------------------------- /src/include/class-wc-retailcrm-daemon-collector.php: -------------------------------------------------------------------------------- 1 | 14 | * @license http://retailcrm.ru Proprietary 15 | * @link http://retailcrm.ru 16 | * @see http://help.retailcrm.ru 17 | */ 18 | class WC_Retailcrm_Daemon_Collector { 19 | /** @var self $instance */ 20 | private static $instance; 21 | 22 | /** @var array $options */ 23 | private $options; 24 | 25 | /** @var string $code */ 26 | private $code = ''; 27 | 28 | /** 29 | * @param array $options 30 | * 31 | * @return WC_Retailcrm_Daemon_Collector 32 | */ 33 | public static function getInstance($options = array()) 34 | { 35 | if (self::$instance === null) { 36 | self::$instance = new self($options); 37 | } 38 | 39 | return self::$instance; 40 | } 41 | 42 | /** 43 | * WC_Retailcrm_Daemon_Collector constructor. 44 | * 45 | * @param array $options 46 | */ 47 | private function __construct($options = array()) 48 | { 49 | $this->options = $options; 50 | } 51 | 52 | /** 53 | * @return string 54 | */ 55 | public function initialize_daemon_collector() { 56 | if (!$this->code) { 57 | $this->buildHeader() 58 | ->buildParams() 59 | ->buildFooter(); 60 | } 61 | 62 | return $this->code; 63 | } 64 | 65 | /** 66 | * @return $this 67 | */ 68 | private function buildHeader() { 69 | $header = << 71 | (function(_,r,e,t,a,i,l){_['retailCRMObject']=a;_[a]=_[a]||function(){(_[a].q=_[a].q||[]).push(arguments)};_[a].l=1*new Date();l=r.getElementsByTagName(e)[0];i=r.createElement(e);i.async=!0;i.src=t;l.parentNode.insertBefore(i,l)})(window,document,'script','https://collector.retailcrm.pro/w.js','_rc'); 72 | 73 | EOF; 74 | 75 | $this->code .= $header; 76 | 77 | return $this; 78 | } 79 | 80 | /** 81 | * @return $this 82 | */ 83 | private function buildParams() { 84 | $params = array(); 85 | 86 | if ( 87 | function_exists('WC') 88 | && WC()->customer !== null 89 | && WC()->customer->get_id() > 0 90 | ) { 91 | $params['customer']['externalId'] = WC()->customer->get_id(); 92 | } 93 | 94 | $this->code .= apply_filters('retailcrm_daemon_collector', '') . sprintf( 95 | "\t_rc('create', '%s', %s);\n", 96 | $this->options['daemon_collector_key'], 97 | json_encode((object) $params) 98 | ); 99 | 100 | return $this; 101 | } 102 | 103 | /** 104 | * @return $this 105 | */ 106 | private function buildFooter() { 107 | $footer = << 110 | 111 | EOF; 112 | 113 | $this->code .= $footer; 114 | 115 | return $this; 116 | } 117 | } 118 | -------------------------------------------------------------------------------- /src/include/class-wc-retailcrm-ga.php: -------------------------------------------------------------------------------- 1 | 11 | * @license http://retailcrm.ru Proprietary 12 | * @link http://retailcrm.ru 13 | * @see http://help.retailcrm.ru 14 | */ 15 | class WC_Retailcrm_Google_Analytics { 16 | private static $instance; 17 | private $options; 18 | 19 | /** 20 | * @param array $options 21 | * 22 | * @return WC_Retailcrm_Google_Analytics 23 | */ 24 | public static function getInstance($options = array()) 25 | { 26 | if (self::$instance === null) { 27 | self::$instance = new self($options); 28 | } 29 | 30 | return self::$instance; 31 | } 32 | 33 | /** 34 | * WC_Retailcrm_Google_Analytics constructor. 35 | * 36 | * @param array $options 37 | */ 38 | private function __construct($options = array()) 39 | { 40 | $this->options = $options; 41 | } 42 | 43 | /** 44 | * @return string 45 | */ 46 | public function initialize_analytics() { 47 | return apply_filters('retailcrm_initialize_analytics' ," 48 | 66 | "); 67 | } 68 | 69 | /** 70 | * @return string 71 | */ 72 | public function send_analytics() { 73 | $js = ''; 74 | 75 | if (!isset($_GET['key'])) { 76 | return $js; 77 | } 78 | 79 | $order_id = wc_get_order_id_by_order_key($_GET['key']); 80 | $order = wc_get_order($order_id); 81 | 82 | if (is_object($order) === false) { 83 | return $js; 84 | } 85 | 86 | foreach ($order->get_items() as $item) { 87 | $uid = ($item['variation_id'] > 0) ? $item['variation_id'] : $item['product_id']; 88 | $_product = wc_get_product($uid); 89 | 90 | if ($_product) { 91 | $order_item = array( 92 | 'id' => $uid, 93 | 'name' => $item['name'], 94 | 'price' => (float)$_product->get_price(), 95 | 'quantity' => $item['qty'], 96 | ); 97 | 98 | $order_items[] = $order_item; 99 | } 100 | } 101 | 102 | $url = parse_url(get_site_url()); 103 | $domain = $url['host']; 104 | 105 | $js .= " 106 | "; 131 | 132 | return apply_filters('retailcrm_send_analytics', $js); 133 | } 134 | } 135 | } 136 | -------------------------------------------------------------------------------- /src/include/customer/class-wc-retailcrm-customer-address.php: -------------------------------------------------------------------------------- 1 | 10 | * @license http://retailcrm.ru Proprietary 11 | * @link http://retailcrm.ru 12 | * @see http://help.retailcrm.ru 13 | */ 14 | class WC_Retailcrm_Customer_Address extends WC_Retailcrm_Abstracts_Address 15 | { 16 | /** 17 | * @param WC_Customer $customer 18 | * @param \WC_Order|null $order 19 | * 20 | * @return self 21 | */ 22 | public function build($customer, $order = null) 23 | { 24 | $address = $this->getCustomerAddress($customer, $order); 25 | 26 | if (!empty($address)) { 27 | $customerAddress = apply_filters( 28 | 'retailcrm_process_customer_address', 29 | WC_Retailcrm_Plugin::clearArray($address), 30 | $customer, 31 | $order 32 | ); 33 | 34 | $this->setDataFields($customerAddress); 35 | } else { 36 | WC_Retailcrm_Logger::error( 37 | __METHOD__, 38 | 'Error: Customer address is empty', 39 | ['wc_customer' => WC_Retailcrm_Logger::formatWcObject($customer)] 40 | ); 41 | } 42 | 43 | return $this; 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /src/include/customer/class-wc-retailcrm-customer-corporate-address.php: -------------------------------------------------------------------------------- 1 | 10 | * @license http://retailcrm.ru Proprietary 11 | * @link http://retailcrm.ru 12 | * @see http://help.retailcrm.ru 13 | */ 14 | class WC_Retailcrm_Customer_Corporate_Address extends WC_Retailcrm_Abstracts_Address 15 | { 16 | /** @var bool $isMain */ 17 | protected $isMain = true; 18 | 19 | /** 20 | * @param bool $isMain 21 | * 22 | * @return WC_Retailcrm_Customer_Corporate_Address 23 | */ 24 | public function setIsMain($isMain) 25 | { 26 | $this->isMain = $isMain; 27 | return $this; 28 | } 29 | 30 | /** 31 | * @param WC_Customer $customer 32 | * @param \WC_Order|null $order 33 | * 34 | * @return self 35 | */ 36 | public function build($customer, $order = null) 37 | { 38 | $address = $this->getCustomerAddress($customer, $order); 39 | 40 | if (!empty($address)) { 41 | $address['isMain'] = $this->isMain; 42 | 43 | $corporateCustomerAddress = apply_filters( 44 | 'retailcrm_process_customer_corporate_address', 45 | WC_Retailcrm_Plugin::clearArray(array_merge( 46 | $address, 47 | ['isMain' => $this->isMain] 48 | )), 49 | $customer 50 | ); 51 | 52 | $this->setDataFields($corporateCustomerAddress); 53 | } else { 54 | WC_Retailcrm_Logger::error( 55 | __METHOD__, 56 | 'Error: Corporate Customer address is empty.', 57 | ['wc_customer' => WC_Retailcrm_Logger::formatWcObject($customer)] 58 | ); 59 | } 60 | 61 | return $this; 62 | } 63 | } 64 | -------------------------------------------------------------------------------- /src/include/index.php: -------------------------------------------------------------------------------- 1 | 10 | * @license http://retailcrm.ru Proprietary 11 | * @link http://retailcrm.ru 12 | * @see http://help.retailcrm.ru 13 | */ 14 | interface WC_Retailcrm_Builder_Interface { 15 | /** 16 | * Sets data into builder 17 | * 18 | * @param array|mixed $data 19 | * 20 | * @return self 21 | */ 22 | public function setData($data); 23 | 24 | /** 25 | * Returns data present in the builder 26 | * 27 | * @return array|mixed 28 | */ 29 | public function getData(); 30 | 31 | /** 32 | * This method should build result with data present in the builder. 33 | * It should return builder instance in spite of actual building result. 34 | * Any exception can be thrown in case of error. It should be processed accordingly. 35 | * 36 | * @return self 37 | * @throws \Exception 38 | */ 39 | public function build(); 40 | 41 | /** 42 | * This method should reset builder state. 43 | * In other words, after calling reset() builder inner state should become identical to newly created builder's state. 44 | * 45 | * @return self 46 | */ 47 | public function reset(); 48 | 49 | /** 50 | * Returns builder result. Can be anything (depends on builder). 51 | * 52 | * @return mixed|null 53 | */ 54 | public function getResult(); 55 | } 56 | -------------------------------------------------------------------------------- /src/include/models/class-wc-retailcrm-customer-switcher-result.php: -------------------------------------------------------------------------------- 1 | 12 | * @license http://retailcrm.ru Proprietary 13 | * @link http://retailcrm.ru 14 | * @see http://help.retailcrm.ru 15 | */ 16 | class WC_Retailcrm_Customer_Switcher_Result 17 | { 18 | /** @var \WC_Customer|null */ 19 | private $wcCustomer; 20 | 21 | /** @var \WC_Order $wcOrder */ 22 | private $wcOrder; 23 | 24 | /** 25 | * WC_Retailcrm_Customer_Switcher_Result constructor. 26 | * 27 | * @param \WC_Customer|null $wcCustomer 28 | * @param \WC_Order $wcOrder 29 | */ 30 | public function __construct($wcCustomer, $wcOrder) 31 | { 32 | $this->wcCustomer = $wcCustomer; 33 | $this->wcOrder = $wcOrder; 34 | 35 | if ( 36 | (!is_null($this->wcCustomer) && !($this->wcCustomer instanceof WC_Customer)) 37 | || !($this->wcOrder instanceof WC_Order) 38 | ) { 39 | throw new \InvalidArgumentException(sprintf('Incorrect data provided to %s', __CLASS__)); 40 | } 41 | } 42 | 43 | /** 44 | * @return \WC_Customer|null 45 | */ 46 | public function getWcCustomer() 47 | { 48 | return $this->wcCustomer; 49 | } 50 | 51 | /** 52 | * @return \WC_Order 53 | */ 54 | public function getWcOrder() 55 | { 56 | return $this->wcOrder; 57 | } 58 | 59 | /** 60 | * Save customer (if exists) and order. 61 | * 62 | * @return $this 63 | */ 64 | public function save() 65 | { 66 | WC_Retailcrm_Logger::info( 67 | __METHOD__, 68 | 'Saving WC_Customer and WC_Order', 69 | [ 70 | 'wc_customer' => WC_Retailcrm_Logger::formatWcObject($this->wcCustomer), 71 | 'wc_order' => WC_Retailcrm_Logger::formatWcObject($this->wcOrder), 72 | ] 73 | ); 74 | 75 | if (!empty($this->wcCustomer) && $this->wcCustomer->get_id()) { 76 | $this->wcCustomer->save(); 77 | } 78 | 79 | if (!empty($this->wcOrder) && $this->wcOrder->get_id()) { 80 | $this->wcOrder->save(); 81 | } 82 | 83 | return $this; 84 | } 85 | } 86 | -------------------------------------------------------------------------------- /src/include/models/class-wc-retailcrm-customer-switcher-state.php: -------------------------------------------------------------------------------- 1 | 11 | * @license http://retailcrm.ru Proprietary 12 | * @link http://retailcrm.ru 13 | * @see http://help.retailcrm.ru 14 | */ 15 | class WC_Retailcrm_Customer_Switcher_State 16 | { 17 | /** @var \WC_Order $wcOrder */ 18 | private $wcOrder; 19 | 20 | /** @var array */ 21 | private $newCustomer; 22 | 23 | /** @var array */ 24 | private $newContact; 25 | 26 | /** @var string $newCompanyName */ 27 | private $newCompanyName; 28 | 29 | /** @var array $companyAddress */ 30 | private $companyAddress; 31 | 32 | /** 33 | * @return \WC_Order 34 | */ 35 | public function getWcOrder() 36 | { 37 | return $this->wcOrder; 38 | } 39 | 40 | /** 41 | * @param \WC_Order $wcOrder 42 | * 43 | * @return WC_Retailcrm_Customer_Switcher_State 44 | */ 45 | public function setWcOrder($wcOrder) 46 | { 47 | $this->wcOrder = $wcOrder; 48 | return $this; 49 | } 50 | 51 | /** 52 | * @return array 53 | */ 54 | public function getNewCustomer() 55 | { 56 | return $this->newCustomer; 57 | } 58 | 59 | /** 60 | * @param array $newCustomer 61 | * 62 | * @return WC_Retailcrm_Customer_Switcher_State 63 | */ 64 | public function setNewCustomer($newCustomer) 65 | { 66 | $this->newCustomer = $newCustomer; 67 | return $this; 68 | } 69 | 70 | /** 71 | * @return array 72 | */ 73 | public function getNewContact() 74 | { 75 | return $this->newContact; 76 | } 77 | 78 | /** 79 | * @param array $newContact 80 | * 81 | * @return WC_Retailcrm_Customer_Switcher_State 82 | */ 83 | public function setNewContact($newContact) 84 | { 85 | $this->newContact = $newContact; 86 | return $this; 87 | } 88 | 89 | /** 90 | * @return string 91 | */ 92 | public function getNewCompanyName() 93 | { 94 | return $this->newCompanyName; 95 | } 96 | 97 | /** 98 | * @param string $newCompanyName 99 | * 100 | * @return WC_Retailcrm_Customer_Switcher_State 101 | */ 102 | public function setNewCompanyName($newCompanyName) 103 | { 104 | $this->newCompanyName = $newCompanyName; 105 | return $this; 106 | } 107 | 108 | /** 109 | * @return array 110 | */ 111 | public function getCompanyAddress() 112 | { 113 | return $this->companyAddress; 114 | } 115 | 116 | /** 117 | * @param array $companyAddress 118 | * 119 | * @return WC_Retailcrm_Customer_Switcher_State 120 | */ 121 | public function setCompanyAddress($companyAddress) 122 | { 123 | $this->companyAddress = $companyAddress; 124 | return $this; 125 | } 126 | 127 | /** 128 | * @param array $newCompany 129 | * 130 | * @return WC_Retailcrm_Customer_Switcher_State 131 | */ 132 | public function setNewCompany($newCompany) 133 | { 134 | if (isset($newCompany['name'])) { 135 | $this->setNewCompanyName($newCompany['name']); 136 | } 137 | 138 | if (isset($newCompany['address']) && !empty($newCompany['address'])) { 139 | $this->setCompanyAddress($newCompany['address']); 140 | } 141 | 142 | return $this; 143 | } 144 | 145 | /** 146 | * Returns true if current state may be processable (e.g. when customer or related data was changed). 147 | * It doesn't guarantee state validity. 148 | * 149 | * @return bool 150 | */ 151 | public function feasible() 152 | { 153 | return !(empty($this->newCustomer) && empty($this->newContact) && empty($this->newCompanyName)); 154 | } 155 | 156 | /** 157 | * Throws an exception if state is not valid 158 | * 159 | * @throws \InvalidArgumentException 160 | * @return void 161 | */ 162 | public function validate() 163 | { 164 | if (empty($this->wcOrder)) { 165 | throw new \InvalidArgumentException('Empty WC_Order.'); 166 | } 167 | 168 | if (empty($this->newCustomer) && empty($this->newContact) && empty($this->newCompanyName)) { 169 | throw new \InvalidArgumentException('New customer, new contact and new company is empty.'); 170 | } 171 | 172 | if (!empty($this->newCustomer) && !empty($this->newContact)) { 173 | WC_Retailcrm_Logger::info( 174 | __METHOD__, 175 | 'State data - customer and contact', 176 | [ 177 | 'customer' => $this->getNewCustomer(), 178 | 'contact' => $this->getNewContact(), 179 | ] 180 | ); 181 | 182 | throw new \InvalidArgumentException( 183 | 'Too much data in state - cannot determine which customer should be used.' 184 | ); 185 | } 186 | } 187 | } 188 | -------------------------------------------------------------------------------- /src/include/order/class-wc-retailcrm-order-address.php: -------------------------------------------------------------------------------- 1 | 10 | * @license http://retailcrm.ru Proprietary 11 | * @link http://retailcrm.ru 12 | * @see http://help.retailcrm.ru 13 | */ 14 | class WC_Retailcrm_Order_Address extends WC_Retailcrm_Abstracts_Address 15 | { 16 | /** 17 | * @param WC_Order $order 18 | * 19 | * @return self 20 | */ 21 | public function build($order) 22 | { 23 | $address = $this->getOrderAddress($order); 24 | 25 | if (!empty($address)) { 26 | $orderAddress = apply_filters( 27 | 'retailcrm_process_order_address', 28 | WC_Retailcrm_Plugin::clearArray($address), 29 | $order 30 | ); 31 | 32 | $this->setDataFields($orderAddress); 33 | } else { 34 | WC_Retailcrm_Logger::error(__METHOD__, 'Error: Order address is empty'); 35 | } 36 | 37 | return $this; 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /src/include/order/class-wc-retailcrm-order-payment.php: -------------------------------------------------------------------------------- 1 | 10 | * @license http://retailcrm.ru Proprietary 11 | * @link http://retailcrm.ru 12 | * @see http://help.retailcrm.ru 13 | */ 14 | class WC_Retailcrm_Order_Payment extends WC_Retailcrm_Abstracts_Data 15 | { 16 | /** @var array */ 17 | protected $data = [ 18 | 'type' => '', 19 | 'order' => [], 20 | 'externalId' => '', 21 | ]; 22 | 23 | /** @var bool */ 24 | public $isNew = true; 25 | 26 | /** 27 | * @var array 28 | */ 29 | protected $settings = []; 30 | 31 | /** 32 | * WC_Retailcrm_Order_Item constructor. 33 | * 34 | * @param array $settings 35 | */ 36 | public function __construct($settings) 37 | { 38 | $this->settings = $settings; 39 | } 40 | 41 | /** 42 | * @param WC_Order $order 43 | * @param mixed $externalId 44 | * 45 | * @return self 46 | */ 47 | public function build($order, $externalId = false) 48 | { 49 | $this->resetData(); 50 | 51 | $paymentData = []; 52 | 53 | if (!$this->isNew) { 54 | $paymentData['externalId'] = $externalId; 55 | } else { 56 | $paymentData['externalId'] = uniqid($order->get_id() . '-'); 57 | } 58 | 59 | $paymentData['order'] = [ 60 | 'externalId' => $order->get_id() 61 | ]; 62 | 63 | if ($order->is_paid()) { 64 | if ($order->get_status() != 'completed' && $order->get_payment_method() == 'cod') { 65 | WC_Retailcrm_Logger::info( 66 | __METHOD__, 67 | sprintf('Payment for order: %s. ' . 68 | 'Payment status cannot be changed as it is cash (or other payment method) on delivery. ' . 69 | 'The status will be changed when the order is in status completed.', 70 | $order->get_id() 71 | ) 72 | ); 73 | } else { 74 | $paymentData['status'] = 'paid'; 75 | 76 | if (isset($this->settings[$order->get_payment_method()])) { 77 | $paymentData['type'] = $this->settings[$order->get_payment_method()]; 78 | } 79 | 80 | $paidAt = $order->get_date_paid(); 81 | 82 | if (!empty($paidAt)) { 83 | $paymentData['paidAt'] = $paidAt->date('Y-m-d H:i:s'); 84 | } 85 | } 86 | } 87 | 88 | if ($this->isNew) { 89 | if (isset($this->settings[$order->get_payment_method()])) { 90 | $paymentData['type'] = $this->settings[$order->get_payment_method()]; 91 | } else { 92 | $paymentData = []; 93 | } 94 | } 95 | 96 | $paymentData = apply_filters( 97 | 'retailcrm_process_payment', 98 | WC_Retailcrm_Plugin::clearArray($paymentData), 99 | $order 100 | ); 101 | 102 | $this->setDataFields($paymentData); 103 | 104 | return $this; 105 | } 106 | 107 | /** 108 | * Returns false if payment doesn't have method 109 | * 110 | * @return array 111 | */ 112 | public function getData() 113 | { 114 | $data = parent::getData(); 115 | 116 | if (empty($data['type'])) { 117 | return []; 118 | } 119 | 120 | // Need to clear the array from empty values 121 | return array_filter($data); 122 | } 123 | 124 | public function resetData() 125 | { 126 | $this->data = [ 127 | 'externalId' => '', 128 | 'type' => '', 129 | 'status' => '', 130 | 'paidAt' => '', 131 | 'order' => [] 132 | ]; 133 | } 134 | } 135 | -------------------------------------------------------------------------------- /src/include/order/class-wc-retailcrm-order.php: -------------------------------------------------------------------------------- 1 | 10 | * @license http://retailcrm.ru Proprietary 11 | * @link http://retailcrm.ru 12 | * @see http://help.retailcrm.ru 13 | */ 14 | class WC_Retailcrm_Order extends WC_Retailcrm_Abstracts_Data 15 | { 16 | /** @var bool */ 17 | public $is_new = true; 18 | 19 | protected $data = [ 20 | 'externalId' => 0, 21 | 'status' => '', 22 | 'number' => '', 23 | 'createdAt' => '', 24 | 'firstName' => '', 25 | 'lastName' => '', 26 | 'email' => '', 27 | 'paymentType' => '', 28 | 'customerComment' => '', 29 | 'paymentStatus' => '', 30 | 'phone' => '', 31 | 'countryIso' => '' 32 | ]; 33 | 34 | /** 35 | * @var array 36 | */ 37 | protected $settings = []; 38 | 39 | /** 40 | * WC_Retailcrm_Order constructor. 41 | * 42 | * @param array $settings 43 | */ 44 | public function __construct($settings) 45 | { 46 | $this->settings = $settings; 47 | } 48 | 49 | /** 50 | * @param WC_Order $order 51 | * 52 | * @return self 53 | */ 54 | public function build($order) 55 | { 56 | $firstName = $order->get_shipping_first_name(); 57 | $lastName = $order->get_shipping_last_name(); 58 | 59 | if (empty($firstName) && empty($lastName)) { 60 | $firstName = $order->get_billing_first_name(); 61 | $lastName = $order->get_billing_last_name(); 62 | } 63 | 64 | $dateCreate = $order->get_date_created(); 65 | 66 | $data = [ 67 | 'externalId' => $order->get_id(), 68 | 'createdAt' => !empty($dateCreate) ? $dateCreate->date('Y-m-d H:i:s') : date('Y-m-d H:i:s'), 69 | 'firstName' => $firstName, 70 | 'lastName' => $lastName, 71 | 'email' => strtolower($order->get_billing_email()), 72 | 'customerComment' => $order->get_customer_note(), 73 | 'phone' => $order->get_billing_phone(), 74 | 'countryIso' => $order->get_shipping_country() 75 | ]; 76 | 77 | if ($data['countryIso'] == '--' || empty($data['countryIso'])) { 78 | $countries = new WC_Countries(); 79 | $data['countryIso'] = $countries->get_base_country(); 80 | } 81 | 82 | $this->setDataFields($data); 83 | $this->setNumber($order); 84 | 85 | if (isset($this->settings[$order->get_status()]) && 'not-upload' !== $this->settings[$order->get_status()]) { 86 | $this->setField('status', $this->settings[$order->get_status()]); 87 | } 88 | 89 | return $this; 90 | } 91 | 92 | /** 93 | * @param WC_Order $order 94 | */ 95 | protected function setNumber($order) 96 | { 97 | if (isset($this->settings['update_number']) && $this->settings['update_number'] == WC_Retailcrm_Base::YES) { 98 | $this->setField('number', $order->get_order_number()); 99 | } else { 100 | unset($this->data['number']); 101 | } 102 | } 103 | } 104 | -------------------------------------------------------------------------------- /src/include/validators/class-wc-retailcrm-validator-exception.php: -------------------------------------------------------------------------------- 1 | 11 | * @license http://retailcrm.ru Proprietary 12 | * @link http://retailcrm.ru 13 | * @see http://help.retailcrm.ru 14 | */ 15 | class WC_Retailcrm_Loyalty_Constraint 16 | { 17 | public $notFoundCrmUser = 'User not found in the system'; 18 | 19 | public $errorFoundLoyalty = 'Error when searching for participation in loyalty programs'; 20 | 21 | public $notFoundActiveParticipation = 'No active participation in the loyalty program was detected'; 22 | 23 | public $notExistBonuses = 'No bonuses for debiting'; 24 | 25 | public $notFoundLoyalty = 'Loyalty program not found'; 26 | 27 | public $loyaltyInactive = 'Loyalty program is not active'; 28 | 29 | public $loyaltyBlocked = 'Loyalty program blocked'; 30 | 31 | public $isCorporateUser = 'This user is a corporate person'; 32 | } 33 | endif; 34 | -------------------------------------------------------------------------------- /src/include/validators/loyalty-validator/class-wc-retailcrm-loyalty-validator.php: -------------------------------------------------------------------------------- 1 | 11 | * @license http://retailcrm.ru Proprietary 12 | * @link http://retailcrm.ru 13 | * @see http://help.retailcrm.ru 14 | */ 15 | class WC_Retailcrm_Loyalty_Validator extends WC_Retailcrm_Loyalty_Constraint 16 | { 17 | /** @var WC_Retailcrm_Client_V5 */ 18 | protected $apiClient; 19 | 20 | protected $isActiveCorp; 21 | 22 | public $customer; 23 | 24 | public $loyaltyAccount; 25 | 26 | public function __construct($apiClient, $isActiveCorp) 27 | { 28 | $this->apiClient = $apiClient; 29 | $this->isActiveCorp = $isActiveCorp; 30 | } 31 | 32 | public function checkAccount(int $userId): bool 33 | { 34 | try { 35 | $this->checkUser($userId); 36 | $this->checkLoyaltyAccount($this->customer['id']); 37 | $this->checkActiveLoyalty($this->loyaltyAccount['loyalty']['id']); 38 | 39 | return true; 40 | } catch (ValidatorException $exception) { 41 | WC_Admin_Settings::add_error((esc_html__($exception->getMessage(), 'retailcrm')) . "userId: $userId"); 42 | } catch (Throwable $exception) { 43 | WC_Admin_Settings::add_error($exception->getMessage()); 44 | } 45 | 46 | return false; 47 | } 48 | 49 | /** 50 | * @throws ValidatorException 51 | */ 52 | private function checkUser($userId) 53 | { 54 | $responseUser = $this->apiClient->customersGet($userId); 55 | 56 | if (!isset($responseUser['customer']['id'])) { 57 | throw new ValidatorException($this->notFoundCrmUser, 400); 58 | } 59 | 60 | $customer = new WC_Customer($userId); 61 | 62 | if ($this->isActiveCorp && !empty($customer->get_billing_company())) { 63 | throw new ValidatorException($this->isCorporateUser, 400); 64 | } 65 | 66 | $this->customer = $responseUser['customer']; 67 | } 68 | 69 | /** 70 | * @throws ValidatorException 71 | */ 72 | private function checkLoyaltyAccount($customerId) 73 | { 74 | $filter['customerId'] = $customerId; 75 | $responseLoyalty = $this->apiClient->getLoyaltyAccountList($filter); 76 | 77 | if (!$responseLoyalty->isSuccessful() || !$responseLoyalty->offsetExists('loyaltyAccounts')) { 78 | throw new ValidatorException($this->errorFoundLoyalty, 400); 79 | } 80 | 81 | $actualAccount = null; 82 | 83 | foreach ($responseLoyalty['loyaltyAccounts'] as $loyaltyAccount) { 84 | if ($loyaltyAccount['active'] === true) { 85 | $actualAccount = $loyaltyAccount; 86 | } 87 | } 88 | 89 | if (!isset($actualAccount)) { 90 | throw new ValidatorException($this->notFoundActiveParticipation, 400); 91 | } 92 | 93 | if ($actualAccount['amount'] === 0 && $actualAccount['level']['type'] !== 'discount') { 94 | throw new ValidatorException($this->notExistBonuses, 400); 95 | } 96 | 97 | $this->loyaltyAccount = $actualAccount; 98 | } 99 | 100 | /** 101 | * @throws ValidatorException 102 | */ 103 | private function checkActiveLoyalty($idLoyalty) 104 | { 105 | $responseLoyalty = $this->apiClient->getLoyalty($idLoyalty); 106 | 107 | if (!$responseLoyalty->isSuccessful() || !$responseLoyalty->offsetExists('loyalty')) { 108 | throw new ValidatorException($this->notFoundLoyalty, 400); 109 | } 110 | 111 | if ($responseLoyalty['loyalty']['active'] !== true) { 112 | throw new ValidatorException($this->loyaltyInactive, 400); 113 | } 114 | 115 | if ($responseLoyalty['loyalty']['blocked'] === true) { 116 | throw new ValidatorException($this->loyaltyBlocked, 400); 117 | } 118 | } 119 | } 120 | endif; 121 | -------------------------------------------------------------------------------- /src/include/validators/url-validator/class-wc-retailcrm-url-constraint.php: -------------------------------------------------------------------------------- 1 | 11 | * @license http://retailcrm.ru Proprietary 12 | * @link http://retailcrm.ru 13 | * @see http://help.retailcrm.ru 14 | */ 15 | class WC_Retailcrm_Url_Constraint 16 | { 17 | /** 18 | * @var string 19 | */ 20 | public $schemeFail = 'Incorrect protocol. Only https is allowed.'; 21 | 22 | /** 23 | * @var string 24 | */ 25 | public $pathFail = 'The domain path must be empty.'; 26 | 27 | /** 28 | * @var string 29 | */ 30 | public $portFail = 'The port does not need to be specified.'; 31 | 32 | /** 33 | * @var string 34 | */ 35 | public $domainFail = 'An invalid domain is specified.'; 36 | 37 | /** 38 | * @var string 39 | */ 40 | public $noValidUrlHost = 'Incorrect Host URL.'; 41 | 42 | /** 43 | * @var string 44 | */ 45 | public $noValidUrl = 'Incorrect URL.'; 46 | 47 | /** 48 | * @var string 49 | */ 50 | public $queryFail = 'The query must be blank.'; 51 | 52 | /** 53 | * @var string 54 | */ 55 | public $fragmentFail = 'The fragment should be blank.'; 56 | 57 | /** 58 | * @var string 59 | */ 60 | public $authFail = 'No need to provide authorization data.'; 61 | 62 | /** 63 | * @var string 64 | */ 65 | public $getFileError = 'Bad Request, file not getted'; 66 | } 67 | endif; 68 | -------------------------------------------------------------------------------- /src/index.php: -------------------------------------------------------------------------------- 1 | query("DELETE FROM $wpdb->options WHERE option_name = {$option}"); 50 | } 51 | 52 | // Clear any cached data that has been removed 53 | wp_cache_flush(); 54 | // @codeCoverageIgnoreEnd 55 | -------------------------------------------------------------------------------- /tests/abstracts/test-wc-retailcrm-abstract-builder.php: -------------------------------------------------------------------------------- 1 | 10 | * @license http://retailcrm.ru Proprietary 11 | * @link http://retailcrm.ru 12 | * @see http://help.retailcrm.ru 13 | */ 14 | class WC_Retailcrm_Abstracts_Settings_Test extends WC_Retailcrm_Test_Case_Helper 15 | { 16 | /** 17 | * @var WC_Retailcrm_Base 18 | */ 19 | protected $base; 20 | 21 | public function setUp() 22 | { 23 | $this->base = new WC_Retailcrm_Base(); 24 | 25 | parent::setUp(); 26 | } 27 | 28 | /** 29 | * @param $checkout 30 | * 31 | * @dataProvider dataProviderAssistant 32 | */ 33 | public function test_validate_online_assistant_field($checkout) 34 | { 35 | $_POST['woocommerce_integration-retailcrm_online_assistant'] = $checkout; 36 | 37 | $onlineAssistant = $this->base->validate_online_assistant_field('', ''); 38 | 39 | $this->assertInternalType('string', $onlineAssistant); 40 | 41 | if (is_string($checkout)) { 42 | $this->assertEquals('c.retailcrm.tech/widget/loader.js', $onlineAssistant); 43 | } else { 44 | $this->assertEquals('', $onlineAssistant); 45 | } 46 | } 47 | 48 | public function dataProviderAssistant() 49 | { 50 | return array( 51 | array( 52 | 'checkout' => 'c.retailcrm.tech/widget/loader.js' 53 | ), 54 | array( 55 | 'checkout' => null 56 | ) 57 | ); 58 | } 59 | } 60 | -------------------------------------------------------------------------------- /tests/bin/install.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | # Example https://raw.githubusercontent.com/wp-cli/scaffold-command/master/templates/install-wp-tests.sh 3 | 4 | DB_NAME=$1 5 | DB_USER=$2 6 | DB_HOST=$3 7 | DB_PASS=$4 8 | WP_VERSION=$5 9 | WC_VERSION=$6 10 | 11 | WP_TESTS_DIR=${WP_TESTS_DIR-/tmp/wordpress-tests-lib} 12 | WP_CORE_DIR=${WP_CORE_DIR-/tmp/wordpress/} 13 | 14 | if [[ $WP_VERSION =~ [0-9]+\.[0-9]+(\.[0-9]+)? ]]; then 15 | WP_TESTS_TAG="tags/$WP_VERSION" 16 | elif [[ $WP_VERSION == 'nightly' || $WP_VERSION == 'trunk' ]]; then 17 | WP_TESTS_TAG="trunk" 18 | else 19 | # http serves a single offer, whereas https serves multiple. we only want one 20 | download http://api.wordpress.org/core/version-check/1.7/ /tmp/wp-latest.json 21 | grep '[0-9]+\.[0-9]+(\.[0-9]+)?' /tmp/wp-latest.json 22 | LATEST_VERSION=$(grep -o '"version":"[^"]*' /tmp/wp-latest.json | sed 's/"version":"//') 23 | if [[ -z "$LATEST_VERSION" ]]; then 24 | echo "Latest WordPress version could not be found" 25 | exit 1 26 | fi 27 | WP_TESTS_TAG="tags/$LATEST_VERSION" 28 | fi 29 | 30 | set -ex 31 | 32 | download() { 33 | if [ `which curl` ]; then 34 | curl -s "$1" > "$2"; 35 | elif [ `which wget` ]; then 36 | wget -nv -O "$2" "$1" 37 | fi 38 | } 39 | 40 | install_wp() { 41 | if [ -d $WP_CORE_DIR ]; then 42 | mkdir -p $WP_CORE_DIR/wp-content/plugins/woo-retailcrm/assets/default 43 | cp -n $PWD/src/assets/default/default_meta_fields.txt $WP_CORE_DIR/wp-content/plugins/woo-retailcrm/assets/default/ 44 | return; 45 | fi 46 | 47 | mkdir -p $WP_CORE_DIR 48 | local ARCHIVE_NAME="wordpress-$WP_VERSION" 49 | download https://wordpress.org/${ARCHIVE_NAME}.tar.gz /tmp/wordpress.tar.gz 50 | tar --strip-components=1 -zxmf /tmp/wordpress.tar.gz -C $WP_CORE_DIR 51 | download https://raw.github.com/markoheijnen/wp-mysqli/master/db.php $WP_CORE_DIR/wp-content/db.php 52 | mkdir -p $WP_CORE_DIR/wp-content/plugins/woo-retailcrm/assets/default 53 | cp -n $PWD/src/assets/default/default_meta_fields.txt $WP_CORE_DIR/wp-content/plugins/woo-retailcrm/assets/default/ 54 | } 55 | 56 | install_woocommerce() { 57 | if [[ ! -d "/tmp/woocommerce" ]] 58 | then 59 | echo $WC_VERSION; 60 | cd /tmp 61 | git clone https://github.com/woocommerce/woocommerce.git 62 | cd woocommerce 63 | git checkout $WC_VERSION 64 | 65 | # In 6.x.x versions WooCommerce changed structure project, for install need move to plugins/woocommerce directory 66 | if [[ "$WC_VERSION" =~ .*"6.".* ]]; then 67 | cd plugins/woocommerce 68 | fi; 69 | 70 | composer install --ignore-platform-reqs 71 | cd /tmp 72 | fi 73 | } 74 | 75 | install_test_suite() { 76 | # portable in-place argument for both GNU sed and Mac OSX sed 77 | if [[ $(uname -s) == 'Darwin' ]]; then 78 | local ioption='-i .bak' 79 | else 80 | local ioption='-i' 81 | fi 82 | 83 | # set up testing suite if it doesn't yet exist 84 | if [ ! -d $WP_TESTS_DIR ]; then 85 | # set up testing suite 86 | mkdir -p $WP_TESTS_DIR 87 | svn co --quiet --ignore-externals https://develop.svn.wordpress.org/${WP_TESTS_TAG}/tests/phpunit/includes/ $WP_TESTS_DIR/includes 88 | svn co --quiet --ignore-externals https://develop.svn.wordpress.org/${WP_TESTS_TAG}/tests/phpunit/data/ $WP_TESTS_DIR/data 89 | fi 90 | 91 | if [ ! -f wp-tests-config.php ]; then 92 | download https://develop.svn.wordpress.org/${WP_TESTS_TAG}/wp-tests-config-sample.php "$WP_TESTS_DIR"/wp-tests-config.php 93 | # remove all forward slashes in the end 94 | WP_CORE_DIR=$(echo $WP_CORE_DIR | sed "s:/\+$::") 95 | sed $ioption "s:dirname( __FILE__ ) . '/src/':'$WP_CORE_DIR/':" "$WP_TESTS_DIR"/wp-tests-config.php 96 | sed $ioption "s/youremptytestdbnamehere/$DB_NAME/" "$WP_TESTS_DIR"/wp-tests-config.php 97 | sed $ioption "s/yourusernamehere/$DB_USER/" "$WP_TESTS_DIR"/wp-tests-config.php 98 | sed $ioption "s/yourpasswordhere/$DB_PASS/" "$WP_TESTS_DIR"/wp-tests-config.php 99 | sed $ioption "s|localhost|${DB_HOST}|" "$WP_TESTS_DIR"/wp-tests-config.php 100 | fi 101 | } 102 | 103 | install_db() { 104 | if [ ${DB_HOST} == "localhost" ]; then 105 | mysqladmin create $DB_NAME --user="$DB_USER" --password="$DB_PASS" --host=$DB_HOST 106 | fi 107 | } 108 | 109 | install_wp 110 | install_test_suite 111 | install_woocommerce 112 | install_db 113 | -------------------------------------------------------------------------------- /tests/bootstrap.php: -------------------------------------------------------------------------------- 1 | 10 | * @license http://retailcrm.ru Proprietary 11 | * @link http://retailcrm.ru 12 | * @see http://help.retailcrm.ru 13 | */ 14 | class WC_Retailcrm_Customer_Address_Test extends WC_Retailcrm_Test_Case_Helper 15 | { 16 | protected $customer; 17 | 18 | public function setUp() 19 | { 20 | parent::setUp(); 21 | 22 | $this->customer = WC_Helper_Customer::create_customer(); 23 | 24 | $this->customer->set_billing_country('CO'); 25 | $this->customer->set_billing_postcode('000000'); 26 | $this->customer->set_billing_state('TestState'); 27 | $this->customer->set_billing_city('TestCity'); 28 | $this->customer->set_billing_address_1('TestAddress1'); 29 | $this->customer->set_billing_address_2('TestAddress2'); 30 | } 31 | 32 | public function test_build_address() 33 | { 34 | $customer_address = new WC_Retailcrm_Customer_Address(); 35 | $data = $customer_address->build($this->customer)->getData(); 36 | 37 | $this->assertArrayHasKey('index', $data); 38 | $this->assertArrayHasKey('city', $data); 39 | $this->assertArrayHasKey('region', $data); 40 | $this->assertArrayHasKey('text', $data); 41 | $this->assertArrayHasKey('countryIso', $data); 42 | $this->assertEquals('000000', $data['index']); 43 | $this->assertEquals('TestCity', $data['city']); 44 | $this->assertEquals('TestState', $data['region']); 45 | $this->assertEquals('TestAddress1 || TestAddress2', $data['text']); 46 | $this->assertEquals('CO', $data['countryIso']); 47 | } 48 | 49 | public function test_empty_address() 50 | { 51 | $customerAddress = new WC_Retailcrm_Customer_Address(); 52 | 53 | $addressData = $customerAddress 54 | ->build(null) 55 | ->getData(); 56 | 57 | $this->assertInternalType('array', $addressData); 58 | $this->assertEquals([], $addressData); 59 | } 60 | } 61 | 62 | -------------------------------------------------------------------------------- /tests/customer/test-wc-retailcrm-customer-corporate-address.php: -------------------------------------------------------------------------------- 1 | 10 | * @license http://retailcrm.ru Proprietary 11 | * @link http://retailcrm.ru 12 | * @see http://help.retailcrm.ru 13 | */ 14 | class WC_Retailcrm_Customer_Corporate_Address_Test extends WC_Retailcrm_Test_Case_Helper 15 | { 16 | protected $customer; 17 | 18 | public function setUp() 19 | { 20 | parent::setUp(); 21 | 22 | $this->customer = WC_Helper_Customer::create_customer(); 23 | 24 | $this->customer->set_billing_country('CO'); 25 | $this->customer->set_billing_postcode('000000'); 26 | $this->customer->set_billing_state('TestState'); 27 | $this->customer->set_billing_city('TestCity'); 28 | $this->customer->set_billing_address_1('TestAddress1'); 29 | $this->customer->set_billing_address_2('TestAddress2'); 30 | } 31 | 32 | public function test_build_address() 33 | { 34 | $customer_address = new WC_Retailcrm_Customer_Corporate_Address(); 35 | $data = $customer_address 36 | ->setIsMain(true) 37 | ->build($this->customer) 38 | ->getData(); 39 | 40 | $this->assertArrayHasKey('index', $data); 41 | $this->assertArrayHasKey('city', $data); 42 | $this->assertArrayHasKey('region', $data); 43 | $this->assertArrayHasKey('text', $data); 44 | $this->assertArrayHasKey('countryIso', $data); 45 | $this->assertArrayHasKey('isMain', $data); 46 | $this->assertEquals('000000', $data['index']); 47 | $this->assertEquals('TestCity', $data['city']); 48 | $this->assertEquals('TestState', $data['region']); 49 | $this->assertEquals('TestAddress1 || TestAddress2', $data['text']); 50 | $this->assertEquals('CO', $data['countryIso']); 51 | $this->assertEquals(true, $data['isMain']); 52 | } 53 | 54 | public function test_build_not_main_company() 55 | { 56 | $customer_address = new WC_Retailcrm_Customer_Corporate_Address(); 57 | $data = $customer_address 58 | ->setIsMain(false) 59 | ->build($this->customer) 60 | ->getData(); 61 | 62 | $this->assertArrayHasKey('index', $data); 63 | $this->assertArrayHasKey('city', $data); 64 | $this->assertArrayHasKey('region', $data); 65 | $this->assertArrayHasKey('text', $data); 66 | $this->assertArrayHasKey('countryIso', $data); 67 | $this->assertArrayHasKey('isMain', $data); 68 | $this->assertEquals('000000', $data['index']); 69 | $this->assertEquals('TestCity', $data['city']); 70 | $this->assertEquals('TestState', $data['region']); 71 | $this->assertEquals('TestAddress1 || TestAddress2', $data['text']); 72 | $this->assertEquals('CO', $data['countryIso']); 73 | $this->assertEquals(false, $data['isMain']); 74 | } 75 | 76 | 77 | public function test_empty_address() 78 | { 79 | $customerCorporateAddress = new WC_Retailcrm_Customer_Corporate_Address(); 80 | 81 | $addressData = $customerCorporateAddress 82 | ->setIsMain(false) 83 | ->build(null) 84 | ->getData(); 85 | 86 | $this->assertInternalType('array', $addressData); 87 | $this->assertEquals([], $addressData); 88 | } 89 | } 90 | 91 | -------------------------------------------------------------------------------- /tests/datasets/data-cart-retailcrm.php: -------------------------------------------------------------------------------- 1 | 12 | * @license http://retailcrm.ru Proprietary 13 | * @link http://retailcrm.ru 14 | * @see http://help.retailcrm.ru 15 | */ 16 | 17 | class DataCartRetailCrm 18 | { 19 | public static function dataGetCart() { 20 | return [ 21 | 'success' => true, 22 | 'cart' => [ 23 | 'clearedAt' => new \DateTime('now'), 24 | 'externalId' => '1', 25 | 'updateAt' => new \DateTime('now'), 26 | 'droppedAt' => new \DateTime('now'), 27 | 'link' => 'https:://link/cart/152', 28 | 'items' => [ 29 | 0 => [ 30 | 'quantity' => 3, 31 | 'price' => 1500, 32 | 'createdAt' => new \DateTime('now'), 33 | 'updatedAt' => new \DateTime('now'), 34 | 'offer' => [ 35 | 'id' => 1, 36 | 'externalId' => '1', 37 | 'name' => 'test product', 38 | 'properties' => [ 39 | 'prop1' => 'prop', 40 | ], 41 | 'unit' => [ 42 | 'code' => 'test code', 43 | 'name' => 'test unit name', 44 | 'sym' => 'sym', 45 | ], 46 | 'barcode' => '123456789', 47 | ], 48 | ], 49 | ], 50 | ], 51 | ]; 52 | } 53 | 54 | public static function dataSetCart() { 55 | return [ 56 | 'cart' => [ 57 | 'clearedAt' => new \DateTime('now'), 58 | 'externalId' => '1', 59 | 'updateAt' => new \DateTime('now'), 60 | 'droppedAt' => new \DateTime('now'), 61 | 'link' => 'https:://link/cart/152', 62 | 'customer' => [ 63 | 'id' => 1, 64 | 'externalId' => '1', 65 | 'browserId' => '145874', 66 | 'site' => 'test-site', 67 | ], 68 | 'items' => [ 69 | 0 => [ 70 | 'quantity' => 3, 71 | 'price' => 1500, 72 | 'createdAt' => new \DateTime('now'), 73 | 'updatedAt' => new \DateTime('now'), 74 | 'offer' => [ 75 | 'id' => 1, 76 | 'externalId' => '1', 77 | ], 78 | ], 79 | ], 80 | ], 81 | ]; 82 | } 83 | 84 | public static function dataClearCart() { 85 | return [ 86 | 'cart' => [ 87 | 'clearedAt' => new \DateTime('now'), 88 | 'customer' => [ 89 | 'id' => 1, 90 | 'externalId' => '1', 91 | 'browserId' => '145874', 92 | ], 93 | 'order' => [ 94 | 'id' => '1', 95 | 'externalId' => '1', 96 | 'number' => '152C', 97 | ], 98 | ], 99 | ]; 100 | } 101 | 102 | } 103 | -------------------------------------------------------------------------------- /tests/datasets/data-customers-retailcrm.php: -------------------------------------------------------------------------------- 1 | 12 | * @license http://retailcrm.ru Proprietary 13 | * @link http://retailcrm.ru 14 | * @see http://help.retailcrm.ru 15 | */ 16 | class DataCustomersRetailCrm 17 | { 18 | public static function getCustomerAddress() { 19 | return [ 20 | 'success' => true, 21 | 'addresses' => [[ 22 | 'index' => 123456, 23 | 'region' => 'test_state', 24 | 'city' => 'test_city', 25 | 'text' => 'test_address_line', 26 | 'isMain' => false 27 | ]] 28 | ]; 29 | } 30 | 31 | 32 | public static function getEmptyCustomersList() { 33 | return [ 34 | 'success' => true, 35 | 'pagination' => [ 36 | 'limit' => 20, 37 | 'totalCount' => 0, 38 | 'currentPage' => 1, 39 | 'totalPageCount' => 0 40 | ], 41 | 'customers' => [], 42 | ]; 43 | } 44 | 45 | public static function getCustomersList() { 46 | return [ 47 | 'success' => true, 48 | 'pagination' => [ 49 | 'limit' => 20, 50 | 'totalCount' => 0, 51 | 'currentPage' => 1, 52 | 'totalPageCount' => 0 53 | ], 54 | 'customers' => [ 55 | [ 56 | 'type' => 'customer', 57 | 'id' => 4228, 58 | 'externalId' => 2, 59 | 'isContact' => false, 60 | 'email' => 'madrid@mail.es', 61 | 'phones' => [['number' => '+3456234235']], 62 | 'addresses' => [ 63 | 'id' => 3503, 64 | 'index' => 144566, 65 | 'countryIso' => 'ES', 66 | 'region' => 'Region', 67 | 'city' => 'City', 68 | 'text' => 'street Test 777', 69 | ] 70 | ] 71 | 72 | ] 73 | ]; 74 | } 75 | } 76 | 77 | -------------------------------------------------------------------------------- /tests/datasets/data-inventories-retailcrm.php: -------------------------------------------------------------------------------- 1 | 12 | * @license http://retailcrm.ru Proprietary 13 | * @link http://retailcrm.ru 14 | * @see http://help.retailcrm.ru 15 | */ 16 | class DataInventoriesRetailCrm { 17 | public static function getResponseData() 18 | { 19 | return [ 20 | 'success' => true, 21 | 'pagination' => [ 22 | 'limit' => 250, 23 | 'totalCount' => 1, 24 | 'currentPage' => 1, 25 | 'totalPageCount' => 1 26 | ], 27 | 'offers' => [ 28 | [ 29 | 'id' => 1, 30 | 'xmlId' => 'xmlId', 31 | 'quantity' => 100, 32 | 'stores' => [ 33 | [ 34 | 'quantity' => 25, 35 | 'purchasePrice' => 0, 36 | 'store' => 'main' 37 | ], 38 | [ 39 | 'quantity' => 25, 40 | 'purchasePrice' => 0, 41 | 'store' => 'woocommerce' 42 | ], 43 | [ 44 | 'quantity' => 50, 45 | 'purchasePrice' => 0, 46 | 'store' => 'prestashop' 47 | ], 48 | 49 | ] 50 | ] 51 | ] 52 | ]; 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /tests/datasets/data-upload-price-retailcrm.php: -------------------------------------------------------------------------------- 1 | 12 | * @license http://retailcrm.ru Proprietary 13 | * @link http://retailcrm.ru 14 | * @see http://help.retailcrm.ru 15 | */ 16 | class DataUploadPriceRetailCrm 17 | { 18 | public static function dataGetPriceTypes() { 19 | return [ 20 | 'success' => true, 21 | 'priceTypes' => [ 22 | [ 23 | 'code' => 'test', 24 | 'name' => 'test', 25 | 'active' => true, 26 | 'description' => 'test', 27 | 'ordering' => 999, 28 | 'promo' => true, 29 | 'default' => false 30 | ], 31 | [ 32 | 'code' => 'default', 33 | 'name' => 'default', 34 | 'active' => true, 35 | 'description' => 'default', 36 | 'ordering' => 999, 37 | 'promo' => true, 38 | 'default' => true 39 | ], 40 | ] 41 | ]; 42 | } 43 | 44 | public static function willSendPriceType() { 45 | return [ 46 | 'code' => 'woo-promotion-lp', 47 | 'name' => 'Woocommerce promotional price', 48 | 'active' => true, 49 | 'description' => 'Promotional price type for Woocommerce store, generated automatically. 50 | Necessary for correct synchronization work when loyalty program is enabled 51 | (Do not delete. Do not deactivate)', 52 | 'ordering' => 999, 53 | 'promo' => true, 54 | ]; 55 | } 56 | } 57 | -------------------------------------------------------------------------------- /tests/helpers/class-wc-retailcrm-log-handler-stdout.php: -------------------------------------------------------------------------------- 1 | 14 | * @license http://retailcrm.ru Proprietary 15 | * @link http://retailcrm.ru 16 | * @see http://help.retailcrm.ru 17 | */ 18 | class WC_Retailcrm_Log_Handler_Stdout extends WC_Log_Handler 19 | { 20 | /** 21 | * @var array 22 | */ 23 | protected $cached_logs = array(); 24 | 25 | /** 26 | * Constructor for the logger. 27 | */ 28 | public function __construct() 29 | { 30 | add_action('plugins_loaded', array($this, 'write_cached_logs')); 31 | } 32 | 33 | /** 34 | * @param int $timestamp 35 | * @param string $level 36 | * @param string $message 37 | * @param array $context 38 | * 39 | * @return bool 40 | */ 41 | public function handle( $timestamp, $level, $message, $context ) 42 | { 43 | if (isset( $context['source'] ) && $context['source']) { 44 | $handle = $context['source']; 45 | } else { 46 | $handle = 'log'; 47 | } 48 | 49 | $entry = self::format_entry($timestamp, $level, $message, $context); 50 | 51 | return $this->add($entry, $handle); 52 | } 53 | 54 | /** 55 | * @param int $timestamp 56 | * @param string $level 57 | * @param string $message 58 | * @param array $context 59 | * 60 | * @return string 61 | */ 62 | protected static function format_entry($timestamp, $level, $message, $context) 63 | { 64 | if (isset($context['_legacy'] ) && true === $context['_legacy']) { 65 | if (isset($context['source']) && $context['source']) { 66 | $handle = $context['source']; 67 | } else { 68 | $handle = 'log'; 69 | } 70 | 71 | $message = apply_filters('woocommerce_logger_add_message', $message, $handle); 72 | $time = date_i18n('m-d-Y @ H:i:s'); 73 | $entry = sprintf('%s - %s', $time, $message); 74 | } else { 75 | $entry = parent::format_entry( $timestamp, $level, $message, $context ); 76 | } 77 | 78 | return $entry; 79 | } 80 | 81 | /** 82 | * @param string $entry Log entry text. 83 | * @param string $handle Log entry handle. 84 | * 85 | * @return bool True if write was successful. 86 | */ 87 | protected function add($entry, $handle) 88 | { 89 | $result = false; 90 | 91 | if (is_resource(STDOUT)) { 92 | $result = fwrite(STDOUT, $entry . PHP_EOL); 93 | } else { 94 | $this->cache_log($entry, $handle); 95 | } 96 | 97 | return false !== $result; 98 | } 99 | 100 | /** 101 | * Cache log to write later. 102 | * 103 | * @param string $entry Log entry text. 104 | * @param string $handle Log entry handle. 105 | */ 106 | protected function cache_log($entry, $handle) 107 | { 108 | $this->cached_logs[] = array( 109 | 'entry' => $entry, 110 | 'handle' => $handle, 111 | ); 112 | } 113 | 114 | /** 115 | * Write cached logs. 116 | */ 117 | public function write_cached_logs() 118 | { 119 | foreach ($this->cached_logs as $log) { 120 | $this->add($log['entry'], $log['handle']); 121 | } 122 | } 123 | } 124 | -------------------------------------------------------------------------------- /tests/helpers/class-wc-retailcrm-response-helper.php: -------------------------------------------------------------------------------- 1 | 14 | * @license http://retailcrm.ru Proprietary 15 | * @link http://retailcrm.ru 16 | * @see http://help.retailcrm.ru 17 | */ 18 | class WC_Retailcrm_Response_Helper extends WC_Retailcrm_Response 19 | { 20 | public function setResponse($response) 21 | { 22 | $this->response = $response; 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /tests/loyalty/test-wc-retailcrm-client-v5.php: -------------------------------------------------------------------------------- 1 | 14 | * @license http://retailcrm.ru Proprietary 15 | * @link http://retailcrm.ru 16 | * @see http://help.retailcrm.ru 17 | */ 18 | 19 | class WC_Retailcrm_Loyalty_Client_Test extends WC_Retailcrm_Test_Case_Helper 20 | { 21 | protected $responseMock; 22 | protected $apiMock; 23 | 24 | /** @var \WC_Retailcrm_Client_V5 */ 25 | protected $clientMock; 26 | 27 | public function setUp() 28 | { 29 | $this->responseMock = $this->getMockBuilder('\WC_Retailcrm_Response_Helper') 30 | ->disableOriginalConstructor() 31 | ->setMethods(['isSuccessful']) 32 | ->getMock() 33 | ; 34 | 35 | $this->responseMock->setResponse(['success' => true]); 36 | $this->setMockResponse($this->responseMock, 'isSuccessful', true); 37 | 38 | $this->apiMock = $this->getMockBuilder('\WC_Retailcrm_Request') 39 | ->disableOriginalConstructor() 40 | ->setMethods(['makeRequest']) 41 | ->getMock() 42 | ; 43 | 44 | $this->setMockResponse($this->apiMock, 'makeRequest', $this->responseMock); 45 | 46 | $this->clientMock = new \WC_Retailcrm_Client_V5('https://test@retailcrm.ru', 'test', 'test'); 47 | 48 | $reflection = new ReflectionClass($this->clientMock); 49 | $reflection_property = $reflection->getProperty('client'); 50 | $reflection_property->setAccessible(true); 51 | 52 | $reflection_property->setValue($this->clientMock, $this->apiMock); 53 | } 54 | 55 | /** 56 | * @dataProvider requestLoyaltyData 57 | */ 58 | public function testLoyaltyRequest($method, $parameters) 59 | { 60 | /** @var WC_Retailcrm_Response $test */ 61 | $test = call_user_func([$this->clientMock, $method], ...$parameters); 62 | 63 | $this->assertTrue($test->isSuccessful()); 64 | } 65 | 66 | public function requestLoyaltyData() 67 | { 68 | return [ 69 | [ 70 | 'method' => 'createLoyaltyAccount', 71 | 'parameters' => [['test'], 'testSite'] 72 | ], 73 | [ 74 | 'method' => 'getLoyaltyClientInfo', 75 | 'parameters' => [1] 76 | ], 77 | [ 78 | 'method' => 'activateLoyaltyAccount', 79 | 'parameters' => [1] 80 | ], 81 | [ 82 | 'method' => 'editLoyaltyAccount', 83 | 'parameters' => [1, ['test']] 84 | ], 85 | [ 86 | 'method' => 'getLoyaltyAccountList', 87 | 'parameters' => [['filter'], 20, 1] 88 | ], 89 | [ 90 | 'method' => 'getListLoyalty', 91 | 'parameters' => [['filter'], 20, 1] 92 | ], 93 | [ 94 | 'method' => 'getLoyalty', 95 | 'parameters' => [1] 96 | ], 97 | [ 98 | 'method' => 'chargeBonusLoyalty', 99 | 'parameters' => [1, 100, 'test'] 100 | ], 101 | [ 102 | 'method' => 'creditBonusLoyalty', 103 | 'parameters' => [1, ['amount' => 100]] 104 | ], 105 | [ 106 | 'method' => 'getClientBonusHistory', 107 | 'parameters' => [1, ['filter'], 20, 1] 108 | ], 109 | [ 110 | 'method' => 'getDetailClientBonus', 111 | 'parameters' => [1, 'status', ['filter'], 20, 1] 112 | ], 113 | [ 114 | 'method' => 'getBonusHistory', 115 | 'parameters' => ['cursor', ['filter'], 20] 116 | ], 117 | [ 118 | 'method' => 'calculateDiscountLoyalty', 119 | 'parameters' => ['site', ['order']] 120 | ], 121 | [ 122 | 'method' => 'applyBonusToOrder', 123 | 'parameters' => ['site', ['order'], 100] 124 | ], 125 | [ 126 | 'method' => 'cancelBonusOrder', 127 | 'parameters' => [['order']] 128 | ], 129 | ]; 130 | } 131 | } 132 | -------------------------------------------------------------------------------- /tests/models/test-wc-retailcrm-customer-switcher-result.php: -------------------------------------------------------------------------------- 1 | 10 | * @license http://retailcrm.ru Proprietary 11 | * @link http://retailcrm.ru 12 | * @see http://help.retailcrm.ru 13 | */ 14 | class WC_Retailcrm_Customer_Switcher_Result_Test extends WC_Retailcrm_Test_Case_Helper 15 | { 16 | protected $customer; 17 | 18 | public function setUp() 19 | { 20 | $this->customer = new WC_Customer(); 21 | 22 | $this->customer->set_first_name('Tester'); 23 | $this->customer->set_last_name('Tester'); 24 | $this->customer->set_email( uniqid(md5(date('Y-m-d H:i:s'))) . '@mail.com'); 25 | $this->customer->set_password('password'); 26 | $this->customer->set_billing_phone('89000000000'); 27 | $this->customer->set_date_created(date('Y-m-d H:i:s')); 28 | $this->customer->save(); 29 | } 30 | 31 | /** 32 | * @expectedException \InvalidArgumentException 33 | */ 34 | public function test_invalid_both() 35 | { 36 | new WC_Retailcrm_Customer_Switcher_Result(new stdClass(), new stdClass()); 37 | } 38 | 39 | /** 40 | * @expectedException \InvalidArgumentException 41 | */ 42 | public function test_invalid_customer() 43 | { 44 | new WC_Retailcrm_Customer_Switcher_Result(new stdClass(), new WC_Order()); 45 | } 46 | 47 | /** 48 | * @expectedException \InvalidArgumentException 49 | */ 50 | public function test_invalid_order() 51 | { 52 | new WC_Retailcrm_Customer_Switcher_Result(new WC_Customer(), new stdClass()); 53 | } 54 | 55 | public function test_valid() 56 | { 57 | $result = new WC_Retailcrm_Customer_Switcher_Result($this->customer, new WC_Order()); 58 | 59 | $this->assertInstanceOf('\WC_Customer', $result->getWcCustomer()); 60 | $this->assertInstanceOf('\WC_Order', $result->getWcOrder()); 61 | } 62 | 63 | public function test_valid_no_customer() 64 | { 65 | $result = new WC_Retailcrm_Customer_Switcher_Result(null, new WC_Order()); 66 | 67 | $this->assertEmpty($result->getWcCustomer()); 68 | $this->assertInstanceOf('\WC_Order', $result->getWcOrder()); 69 | } 70 | 71 | 72 | public function test_save() 73 | { 74 | $switcher = new WC_Retailcrm_Customer_Switcher_Result($this->customer, new WC_Order()); 75 | 76 | $switcher->save(); 77 | $this->assertInstanceOf('\WC_Customer', $switcher->getWcCustomer()); 78 | } 79 | } 80 | 81 | -------------------------------------------------------------------------------- /tests/models/test-wc-retailcrm-customer-switcher-state.php: -------------------------------------------------------------------------------- 1 | 10 | * @license http://retailcrm.ru Proprietary 11 | * @link http://retailcrm.ru 12 | * @see http://help.retailcrm.ru 13 | */ 14 | class WC_Retailcrm_Customer_Switcher_State_Test extends WC_Retailcrm_Test_Case_Helper 15 | { 16 | public function test_feasible() 17 | { 18 | $state = new WC_Retailcrm_Customer_Switcher_State(); 19 | 20 | $this->assertFalse($state->feasible()); 21 | 22 | $state->setNewCustomer(array()) 23 | ->setNewContact(array()) 24 | ->setNewCompanyName(''); 25 | $this->assertFalse($state->feasible()); 26 | 27 | $state->setNewCustomer(array('id' => 1)); 28 | $this->assertTrue($state->feasible()); 29 | 30 | $state->setNewCustomer(array()) 31 | ->setNewContact(array('id' => 1)); 32 | $this->assertTrue($state->feasible()); 33 | 34 | $state->setNewCustomer(array()) 35 | ->setNewContact(array()) 36 | ->setNewCompanyName('test'); 37 | $this->assertTrue($state->feasible()); 38 | 39 | $state->setNewCustomer(array()) 40 | ->setNewContact(array()) 41 | ->setNewCompany(array('name' => 'test', 'address' => 'address_test')); 42 | $this->assertTrue($state->feasible()); 43 | 44 | $state->setNewCustomer(array()) 45 | ->setNewContact(array()) 46 | ->setNewCompanyName(''); 47 | $this->assertFalse($state->feasible()); 48 | } 49 | 50 | /** 51 | * @expectedException \InvalidArgumentException 52 | */ 53 | public function test_validate_empty() 54 | { 55 | $state = new WC_Retailcrm_Customer_Switcher_State(); 56 | 57 | $state->validate(); 58 | } 59 | 60 | /** 61 | * @expectedException \InvalidArgumentException 62 | */ 63 | public function test_validate_order() 64 | { 65 | $state = new WC_Retailcrm_Customer_Switcher_State(); 66 | 67 | $state->setWcOrder(new WC_Order())->validate(); 68 | } 69 | 70 | /** 71 | * @expectedException \InvalidArgumentException 72 | */ 73 | public function test_validate_customer_and_contact_set() 74 | { 75 | $state = new WC_Retailcrm_Customer_Switcher_State(); 76 | 77 | $state->setWcOrder(new WC_Order()) 78 | ->setNewCustomer(array('id' => 1)) 79 | ->setNewContact(array('id' => 1)) 80 | ->validate(); 81 | } 82 | 83 | /** 84 | * @doesNotPerformAssertions 85 | */ 86 | public function test_validate_ok() 87 | { 88 | $state = new WC_Retailcrm_Customer_Switcher_State(); 89 | 90 | $state->setWcOrder(new WC_Order()) 91 | ->setNewCustomer(array('id' => 1)) 92 | ->validate(); 93 | } 94 | } 95 | 96 | -------------------------------------------------------------------------------- /tests/order/test-wc-retailcrm-order-address.php: -------------------------------------------------------------------------------- 1 | 10 | * @license http://retailcrm.ru Proprietary 11 | * @link http://retailcrm.ru 12 | * @see http://help.retailcrm.ru 13 | */ 14 | class WC_Retailcrm_Order_Address_Test extends WC_Retailcrm_Test_Case_Helper 15 | { 16 | /** @var WC_Order */ 17 | protected $order; 18 | 19 | public function setUp() 20 | { 21 | parent::setUp(); 22 | 23 | $this->order = WC_Helper_Order::create_order(); 24 | 25 | $this->order->set_shipping_postcode('000000'); 26 | $this->order->set_shipping_state('TestState'); 27 | $this->order->set_shipping_city('TestCity'); 28 | $this->order->set_shipping_address_1('TestAddress1'); 29 | $this->order->set_shipping_address_2('TestAddress2'); 30 | } 31 | 32 | public function test_build_address() 33 | { 34 | $order_address = new WC_Retailcrm_Order_Address(); 35 | $data = $order_address->build($this->order)->getData(); 36 | 37 | $this->assertArrayHasKey('index', $data); 38 | $this->assertArrayHasKey('city', $data); 39 | $this->assertArrayHasKey('region', $data); 40 | $this->assertArrayHasKey('text', $data); 41 | $this->assertEquals('000000', $data['index']); 42 | $this->assertEquals('TestCity', $data['city']); 43 | $this->assertEquals('TestState', $data['region']); 44 | $this->assertEquals('TestAddress1 || TestAddress2', $data['text']); 45 | } 46 | 47 | public function test_empty_address() 48 | { 49 | $orderAddress = new WC_Retailcrm_Order_Address(); 50 | 51 | $addressData = $orderAddress 52 | ->build(null) 53 | ->getData(); 54 | 55 | $this->assertInternalType('array', $addressData); 56 | $this->assertEquals([], $addressData); 57 | } 58 | } 59 | 60 | -------------------------------------------------------------------------------- /tests/order/test-wc-retailcrm-order-item.php: -------------------------------------------------------------------------------- 1 | 10 | * @license http://retailcrm.ru Proprietary 11 | * @link http://retailcrm.ru 12 | * @see http://help.retailcrm.ru 13 | */ 14 | class WC_Retailcrm_Order_Item_Test extends WC_Retailcrm_Test_Case_Helper 15 | { 16 | /** @var WC_Order */ 17 | protected $order; 18 | 19 | public function setUp() 20 | { 21 | parent::setUp(); 22 | 23 | $this->order = WC_Helper_Order::create_order(); 24 | } 25 | 26 | public function test_build() 27 | { 28 | $order_item = new WC_Retailcrm_Order_Item($this->getOptions()); 29 | 30 | /** @var WC_Order_Item_Product $item */ 31 | foreach ($this->order->get_items() as $item) { 32 | $data = $order_item->build($item)->getData(); 33 | 34 | $this->assertArrayHasKey('productName', $data); 35 | $this->assertArrayHasKey('initialPrice', $data); 36 | $this->assertArrayHasKey('quantity', $data); 37 | $this->assertArrayHasKey('offer', $data); 38 | } 39 | } 40 | 41 | public function test_bind_by_sku() 42 | { 43 | $order_item = new WC_Retailcrm_Order_Item(['bind_by_sku' => 'yes']); 44 | 45 | foreach ($this->order->get_items() as $item) { 46 | $data = $order_item->build($item)->getData(); 47 | 48 | $this->assertArrayHasKey('offer', $data); 49 | $this->assertArrayHasKey('xmlId', $data['offer']); 50 | } 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /tests/order/test-wc-retailcrm-order-payment.php: -------------------------------------------------------------------------------- 1 | 10 | * @license http://retailcrm.ru Proprietary 11 | * @link http://retailcrm.ru 12 | * @see http://help.retailcrm.ru 13 | */ 14 | class WC_Retailcrm_Order_Payment_Test extends WC_Retailcrm_Test_Case_Helper 15 | { 16 | /** @var WC_Order */ 17 | protected $order; 18 | 19 | public function setUp() 20 | { 21 | parent::setUp(); 22 | 23 | $this->order = WC_Helper_Order::create_order(); 24 | $this->setOptions(); 25 | } 26 | 27 | /** 28 | * @param mixed $externalId 29 | * 30 | * @dataProvider dataProvider 31 | */ 32 | public function test_build($externalId) 33 | { 34 | $settings = $this->getOptions(); 35 | $order_payment = new WC_Retailcrm_Order_Payment($settings); 36 | 37 | $data = $order_payment->build($this->order, $externalId)->getData(); 38 | 39 | $this->assertNotEmpty($data); 40 | 41 | if (!empty($externalId)) { 42 | $this->assertArrayHasKey('externalId', $data); 43 | } 44 | 45 | $this->assertArrayHasKey('type', $data); 46 | $this->assertArrayHasKey('order', $data); 47 | } 48 | 49 | /** 50 | * @param mixed $externalId 51 | * 52 | * @dataProvider dataProvider 53 | */ 54 | public function test_build_payment_type_not_exist($externalId) 55 | { 56 | $order_payment = new WC_Retailcrm_Order_Payment('test'); 57 | $data = $order_payment->build($this->order, $externalId)->getData(); 58 | 59 | $this->assertEmpty($data); 60 | } 61 | 62 | 63 | /** 64 | * @param mixed $externalId 65 | * 66 | * @dataProvider dataProvider 67 | */ 68 | public function test_not_new_payment($externalId) 69 | { 70 | $settings = $this->getOptions(); 71 | $order_payment = new WC_Retailcrm_Order_Payment($settings); 72 | $order_payment->isNew = false; 73 | 74 | $data = $order_payment->build($this->order, $externalId)->getData(); 75 | 76 | $this->assertEmpty($data); 77 | } 78 | 79 | 80 | /** 81 | * @param mixed $externalId 82 | * 83 | * @dataProvider dataProvider 84 | */ 85 | public function test_order_paid($externalId) 86 | { 87 | $settings = $this->getOptions(); 88 | $order_payment = new WC_Retailcrm_Order_Payment($settings); 89 | 90 | $this->order->update_status('completed'); 91 | 92 | $data = $order_payment->build($this->order, $externalId)->getData(); 93 | 94 | $this->assertNotEmpty($data); 95 | 96 | if (!empty($externalId)) { 97 | $this->assertArrayHasKey('externalId', $data); 98 | } 99 | 100 | $this->assertArrayHasKey('type', $data); 101 | $this->assertArrayHasKey('order', $data); 102 | } 103 | 104 | 105 | /** 106 | * @param mixed $externalId 107 | * 108 | * @dataProvider dataProvider 109 | */ 110 | public function test_build_with_amount($externalId) 111 | { 112 | $settings = $this->getOptions(); 113 | $order_payment = new WC_Retailcrm_Order_Payment($settings); 114 | 115 | $data = $order_payment->build($this->order, $externalId)->getData(); 116 | 117 | $this->assertNotEmpty($data); 118 | 119 | if (!empty($externalId)) { 120 | $this->assertArrayHasKey('externalId', $data); 121 | } 122 | 123 | $this->assertArrayHasKey('type', $data); 124 | $this->assertArrayHasKey('order', $data); 125 | } 126 | 127 | /** 128 | * @return array 129 | */ 130 | public function dataProvider() 131 | { 132 | return array( 133 | array( 134 | 'externalId' => false 135 | ), 136 | array( 137 | 'externalId' => uniqid() 138 | ) 139 | ); 140 | } 141 | } 142 | -------------------------------------------------------------------------------- /tests/order/test-wc-retailcrm-order.php: -------------------------------------------------------------------------------- 1 | 10 | * @license http://retailcrm.ru Proprietary 11 | * @link http://retailcrm.ru 12 | * @see http://help.retailcrm.ru 13 | */ 14 | class WC_Retailcrm_Order_Test extends WC_Retailcrm_Test_Case_Helper { 15 | /** @var WC_Order */ 16 | protected $order; 17 | 18 | public function setUp() 19 | { 20 | parent::setUp(); 21 | 22 | $this->order = WC_Helper_Order::create_order(); 23 | } 24 | 25 | public function test_empty_shipping_data() 26 | { 27 | $buildOrder = new WC_Retailcrm_Order($this->getOptions()); 28 | $data = $buildOrder->build($this->order)->getData(); 29 | 30 | $this->assertNotEmpty($data); 31 | $this->assertArrayHasKey('firstName', $data); 32 | $this->assertArrayHasKey('lastName', $data); 33 | $this->assertEquals($this->order->get_billing_first_name(), $data['firstName']); 34 | $this->assertEquals($this->order->get_billing_last_name(), $data['lastName']); 35 | } 36 | 37 | public function test_empty_country_iso() 38 | { 39 | $buildOrder = new WC_Retailcrm_Order($this->getOptions()); 40 | 41 | $this->order->set_shipping_country(''); 42 | 43 | $data = $buildOrder->build($this->order)->getData(); 44 | 45 | $this->assertNotEmpty($data); 46 | $this->assertArrayHasKey('countryIso', $data); 47 | $this->assertNotEquals('', $data['countryIso']); 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /tests/test-wc-retailcrm-cart.php: -------------------------------------------------------------------------------- 1 | 12 | * @license http://retailcrm.ru Proprietary 13 | * @link http://retailcrm.ru 14 | * @see http://help.retailcrm.ru 15 | */ 16 | class WC_Retailcrm_Cart_Test extends WC_Retailcrm_Test_Case_Helper 17 | { 18 | protected $cart; 19 | protected $apiMock; 20 | protected $responseMock; 21 | 22 | public function setUp() 23 | { 24 | $this->responseMock = $this->getMockBuilder('\WC_Retailcrm_Response_Helper') 25 | ->disableOriginalConstructor() 26 | ->setMethods(['isSuccessful']) 27 | ->getMock(); 28 | 29 | $this->apiMock = $this->getMockBuilder('\WC_Retailcrm_Client_V5') 30 | ->disableOriginalConstructor() 31 | ->setMethods(['cartGet', 'cartSet', 'cartClear']) 32 | ->getMock(); 33 | 34 | $this->responseMock->setResponse(['success' => true, ]); 35 | $this->setMockResponse($this->responseMock, 'isSuccessful', true); 36 | $this->setMockResponse($this->apiMock, 'cartGet', ['cart' => ['externalId' => 1]]); 37 | $this->setMockResponse($this->apiMock, 'cartSet', $this->responseMock); 38 | $this->setMockResponse($this->apiMock, 'cartClear', $this->responseMock); 39 | 40 | $this->cart = new WC_Retailcrm_Cart($this->apiMock, $this->getOptions()); 41 | } 42 | 43 | public function testApiGetCart() 44 | { 45 | $this->responseMock->setResponse(DataCartRetailCrm::dataGetCart()); 46 | 47 | $response = $this->apiMock->cartGet(1, 'test-site'); 48 | 49 | $this->assertNotEmpty($response['cart']); 50 | $this->assertNotEmpty($response['cart']['externalId']); 51 | $this->assertEquals(1, $response['cart']['externalId']); 52 | } 53 | 54 | public function testApiSetCart() 55 | { 56 | $response = $this->apiMock->cartSet(DataCartRetailCrm::dataSetCart(), 'test-site'); 57 | 58 | $this->assertNotEmpty($response['success']); 59 | $this->assertTrue($response['success']); 60 | } 61 | 62 | public function testApiClearCart() 63 | { 64 | $response = $this->apiMock->cartClear(DataCartRetailCrm::dataClearCart(), 'test-site'); 65 | 66 | $this->assertNotEmpty($response['success']); 67 | $this->assertTrue($response['success']); 68 | } 69 | 70 | public function testSetCart() 71 | { 72 | $wcCart = new WC_Cart(); 73 | $product = WC_Helper_Product::create_simple_product(); 74 | $customerId = wc_create_new_customer('mail_test@mail.es', 'test'); 75 | 76 | $wcCart->add_to_cart($product->get_id(), 1, 0, [], []); 77 | 78 | $this->assertTrue($this->cart->processCart($customerId, $wcCart->get_cart(), 'woo', true)); 79 | } 80 | 81 | public function testGetCart() 82 | { 83 | $this->assertTrue($this->cart->isCartExist(1, 'woo')); 84 | } 85 | 86 | public function testClearCart() 87 | { 88 | $this->assertTrue($this->cart->clearCart(1, 'woo', true)); 89 | } 90 | } 91 | -------------------------------------------------------------------------------- /tests/test-wc-retailcrm-daemon-collector.php: -------------------------------------------------------------------------------- 1 | 10 | * @license http://retailcrm.ru Proprietary 11 | * @link http://retailcrm.ru 12 | * @see http://help.retailcrm.ru 13 | */ 14 | class WC_Retailcrm_Daemon_Collector_Test extends WC_Retailcrm_Test_Case_Helper 15 | { 16 | private $daemonCollector; 17 | private $options; 18 | 19 | public function setUp() 20 | { 21 | $this->options = array( 22 | 'daemon_collector_key' => 'RC-XXXXXXXXXX-X' 23 | ); 24 | 25 | $this->daemonCollector = WC_Retailcrm_Daemon_Collector::getInstance($this->options); 26 | } 27 | 28 | public function test_initialize_daemon_collector() 29 | { 30 | $customerObject = WC_Helper_Customer::create_customer(); 31 | WC()->customer = $customerObject; 32 | 33 | $js = $this->daemonCollector->initialize_daemon_collector(); 34 | 35 | $this->assertContains('customer', $js); 36 | $this->assertContains($this->options['daemon_collector_key'], $js); 37 | $this->assertContains('assertContains('', $js); 39 | $this->assertContains('_rc(\'create\',', $js); 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /tests/test-wc-retailcrm-ga.php: -------------------------------------------------------------------------------- 1 | 10 | * @license http://retailcrm.ru Proprietary 11 | * @link http://retailcrm.ru 12 | * @see http://help.retailcrm.ru 13 | */ 14 | class WC_Retailcrm_Google_Analytics_Test extends WC_Retailcrm_Test_Case_Helper 15 | { 16 | private $ga; 17 | private $options; 18 | private $order; 19 | private $orderKey; 20 | 21 | public function setUp() 22 | { 23 | $this->order = WC_Helper_Order::create_order(0); 24 | $this->orderKey = $this->order->get_order_key(); 25 | $this->setOptions(); 26 | 27 | $this->options = get_option(WC_Retailcrm_Base::$option_key); 28 | $this->ga = WC_Retailcrm_Google_Analytics::getInstance($this->options); 29 | } 30 | 31 | public function test_initialize_analytics() 32 | { 33 | $js = $this->ga->initialize_analytics(); 34 | 35 | $this->assertContains($this->options['ua_code'], $js); 36 | $this->assertContains($this->options['ua_custom'], $js); 37 | } 38 | 39 | /** 40 | * @param $checkout 41 | * 42 | * @dataProvider dataProvider 43 | */ 44 | public function test_send_analytics($checkout) 45 | { 46 | if ($checkout === true) { 47 | $_GET['key'] = $this->orderKey; 48 | } elseif (is_null($checkout)) { 49 | $_GET['key'] = ''; 50 | } 51 | 52 | $js = $this->ga->send_analytics(); 53 | 54 | if ($checkout) { 55 | $this->assertContains((string)$this->order->get_id(), $js); 56 | $this->assertContains((string)$this->order->get_total(), $js); 57 | $this->assertContains((string)$this->order->get_total_tax(), $js); 58 | $this->assertContains((string)$this->order->get_shipping_total(), $js); 59 | } else { 60 | $this->assertEmpty($js); 61 | } 62 | } 63 | 64 | public function dataProvider() 65 | { 66 | return array( 67 | array( 68 | 'checkout' => false 69 | ), 70 | array( 71 | 'checkout' => null 72 | ), 73 | array( 74 | 'checkout' => true 75 | ) 76 | ); 77 | } 78 | } 79 | -------------------------------------------------------------------------------- /tests/test-wc-retailcrm-icml.php: -------------------------------------------------------------------------------- 1 | 10 | * @license http://retailcrm.ru Proprietary 11 | * @link http://retailcrm.ru 12 | * @see http://help.retailcrm.ru 13 | */ 14 | class WC_Retailcrm_Icml_Test extends WC_Retailcrm_Test_Case_Helper 15 | { 16 | public function setUp() 17 | { 18 | WC_Helper_Product::create_simple_product(); 19 | WC_Helper_Product::create_variation_product(); 20 | 21 | $this->createVirtualProduct(); 22 | $this->setOptions(); 23 | } 24 | 25 | public function testGenerate() 26 | { 27 | $icml = new WC_Retailcrm_Icml(); 28 | 29 | $icml->generate(); 30 | $this->assertFileExists(ABSPATH . 'simla.xml'); 31 | 32 | $xml = simplexml_load_file(ABSPATH . 'simla.xml'); 33 | 34 | $this->assertNotEmpty($xml); 35 | 36 | $xmlArray = json_decode(json_encode($xml), true); 37 | 38 | $this->assertNotEmpty($xmlArray['shop']['categories']['category']); 39 | $this->assertCount(2, $xmlArray['shop']['categories']['category']); 40 | $this->assertNotEmpty($xmlArray['shop']['offers']['offer']); 41 | $this->assertNotEmpty($xmlArray['shop']['offers']['offer'][0]); 42 | $this->assertNotEmpty($xmlArray['shop']['offers']['offer'][1]); 43 | $this->assertNotEmpty($xmlArray['shop']['offers']['offer'][2]); 44 | 45 | foreach ($xmlArray['shop']['offers']['offer'] as $product) { 46 | $this->assertNotEmpty($product['name']); 47 | $this->assertNotEmpty($product['productName']); 48 | $this->assertNotEmpty($product['price']); 49 | $this->assertNotEmpty($product['url']); 50 | $this->assertNotEmpty($product['param']); 51 | $this->assertNotEmpty($product['vatRate']); 52 | $this->assertEquals('none', $product['vatRate']); 53 | $this->assertContains('Dummy', $product['productName']); 54 | $this->assertNotEmpty($product['@attributes']['type']); 55 | } 56 | 57 | $attributesList = array_column($xmlArray['shop']['offers']['offer'], '@attributes'); 58 | $typeList = array_column($attributesList, 'type'); 59 | 60 | $this->assertContains('service', $typeList); 61 | } 62 | 63 | private function createVirtualProduct() 64 | { 65 | $product = wp_insert_post([ 66 | 'post_title' => 'Dummy Product', 67 | 'post_type' => 'product', 68 | 'post_status' => 'publish', 69 | ]); 70 | 71 | update_post_meta($product, '_price', '10'); 72 | update_post_meta($product, '_regular_price', '10'); 73 | update_post_meta($product, '_sale_price', ''); 74 | update_post_meta($product, '_sku', 'DUMMY SKU'); 75 | update_post_meta($product, '_manage_stock', 'no'); 76 | update_post_meta($product, '_tax_status', 'taxable'); 77 | update_post_meta($product, '_downloadable', 'no'); 78 | update_post_meta($product, '_virtual', 'yes'); 79 | update_post_meta($product, '_stock_status', 'instock'); 80 | update_post_meta($product, '_weight', '1.1'); 81 | wp_set_object_terms($product, 'simple', 'product_type'); 82 | 83 | return new WC_Product_Simple($product); 84 | } 85 | } 86 | -------------------------------------------------------------------------------- /tests/test-wc-retailcrm-inventories.php: -------------------------------------------------------------------------------- 1 | 12 | * @license http://retailcrm.ru Proprietary 13 | * @link http://retailcrm.ru 14 | * @see http://help.retailcrm.ru 15 | */ 16 | class WC_Retailcrm_Inventories_Test extends WC_Retailcrm_Test_Case_Helper 17 | { 18 | protected $apiMock; 19 | protected $responseMock; 20 | 21 | public function setUp() 22 | { 23 | $this->responseMock = $this->getMockBuilder('\WC_Retailcrm_Response_Helper') 24 | ->disableOriginalConstructor() 25 | ->setMethods(['isSuccessful']) 26 | ->getMock(); 27 | 28 | $this->setMockResponse($this->responseMock, 'isSuccessful', true); 29 | 30 | $this->apiMock = $this->getMockBuilder('\WC_Retailcrm_Proxy') 31 | ->disableOriginalConstructor() 32 | ->setMethods(['storeInventories']) 33 | ->getMock(); 34 | 35 | parent::setUp(); 36 | } 37 | 38 | /** 39 | * @param $retailcrm 40 | * @param $response 41 | * 42 | * @dataProvider dataProviderLoadStocks 43 | */ 44 | public function test_load_stocks_simple_product($retailcrm, $response) 45 | { 46 | $offer = WC_Helper_Product::create_simple_product(); 47 | $offer->save(); 48 | 49 | if (null !== $response) { 50 | $response['offers'][0]['externalId'] = $offer->get_id(); 51 | } 52 | 53 | $this->responseMock->setResponse($response); 54 | 55 | if ($retailcrm) { 56 | $this->setMockResponse($retailcrm, 'storeInventories', $this->responseMock); 57 | } 58 | 59 | $retailcrm_inventories = new WC_Retailcrm_Inventories($retailcrm); 60 | $retailcrm_inventories->updateQuantity(); 61 | 62 | $this->checkProductData($retailcrm, $response, $offer->get_id(), 'simple'); 63 | } 64 | 65 | /** 66 | * @param $retailcrm 67 | * @param $response 68 | * 69 | * @dataProvider dataProviderLoadStocks 70 | */ 71 | public function test_load_stocks_variation_product($retailcrm, $response) 72 | { 73 | $offer = WC_Helper_Product::create_variation_product(); 74 | $offer->save(); 75 | 76 | $childrens = $offer->get_children(); 77 | 78 | if (null !== $response) { 79 | $response['offers'][0]['externalId'] = $childrens[0]; 80 | } 81 | 82 | $this->responseMock->setResponse($response); 83 | 84 | if ($retailcrm) { 85 | $this->setMockResponse($retailcrm, 'storeInventories', $this->responseMock); 86 | } 87 | 88 | $retailcrm_inventories = new WC_Retailcrm_Inventories($retailcrm); 89 | $retailcrm_inventories->updateQuantity(); 90 | 91 | $this->checkProductData($retailcrm, $response, $childrens[0], 'variation'); 92 | } 93 | 94 | public function test_sync_off() 95 | { 96 | $options = $this->getOptions(); 97 | $options['sync'] = 'no'; 98 | 99 | update_option(WC_Retailcrm_Base::$option_key, $options); 100 | 101 | $retailcrm_inventories = new WC_Retailcrm_Inventories($this->apiMock); 102 | $result = $retailcrm_inventories->updateQuantity(); 103 | 104 | $this->assertEquals(false, $result); 105 | } 106 | 107 | private function checkProductData($retailcrm, $response, $offerId, $entity) 108 | { 109 | $product = wc_get_product($offerId); 110 | 111 | if ($retailcrm && null !== $response) { 112 | $this->assertInstanceOf('WC_Product', $product); 113 | $this->assertEquals($entity, $product->get_type()); 114 | $this->assertEquals(50, $product->get_stock_quantity()); 115 | } else { 116 | $this->assertNotEquals(50, $product->get_stock_quantity()); 117 | } 118 | } 119 | 120 | public function dataProviderLoadStocks() 121 | { 122 | $this->setUp(); 123 | 124 | $response = DataInventoriesRetailCrm::getResponseData(); 125 | 126 | return array( 127 | array( 128 | 'retailcrm' => $this->apiMock, 129 | 'response' => $response 130 | ), 131 | array( 132 | 'retailcrm' => false, 133 | 'response' => $response 134 | ), 135 | array( 136 | 'retailcrm' => $this->apiMock, 137 | 'response' => null 138 | ) 139 | ); 140 | } 141 | } 142 | -------------------------------------------------------------------------------- /tests/test-wc-retailcrm-plugin.php: -------------------------------------------------------------------------------- 1 | 10 | * @license http://retailcrm.ru Proprietary 11 | * @link http://retailcrm.ru 12 | * @see http://help.retailcrm.ru 13 | */ 14 | class WC_Retailcrm_Plugin_Test extends WC_Retailcrm_Test_Case_Helper 15 | { 16 | protected $apiMock; 17 | protected $responseMock; 18 | protected $plugin; 19 | private $path = __DIR__ . '/src/retailcrm.php'; 20 | 21 | public function setUp() 22 | { 23 | $this->responseMock = $this->getMockBuilder('\WC_Retailcrm_Response_Helper') 24 | ->disableOriginalConstructor() 25 | ->setMethods(array('isSuccessful')) 26 | ->getMock(); 27 | 28 | $this->apiMock = $this->getMockBuilder('\WC_Retailcrm_Proxy') 29 | ->disableOriginalConstructor() 30 | ->setMethods(array('integrationModulesEdit')) 31 | ->getMock(); 32 | 33 | $this->plugin = WC_Retailcrm_Plugin::getInstance($this->path); 34 | 35 | parent::setUp(); 36 | } 37 | 38 | /** 39 | * @param $retailcrm 40 | * @param $responseStatus 41 | * 42 | * @dataProvider dataProviderIntegrationModule 43 | */ 44 | public function test_integration_module($retailcrm, $responseStatus) 45 | { 46 | $this->setMockResponse($this->responseMock, 'isSuccessful', $responseStatus); 47 | 48 | $this->responseMock->setResponse(array('success' => $responseStatus)); 49 | 50 | if ($retailcrm) { 51 | $this->setMockResponse($retailcrm, 'integrationModulesEdit', $this->responseMock); 52 | } 53 | 54 | $client_id = uniqid(); 55 | $result = WC_Retailcrm_Plugin::integration_module($retailcrm, $client_id); 56 | 57 | if (!$retailcrm || !$responseStatus) { 58 | $this->assertEquals(false, $result); 59 | } else { 60 | $this->assertEquals(true, $result); 61 | } 62 | } 63 | 64 | public function test_filter_cron_schedules() 65 | { 66 | $schedules = $this->plugin->filter_cron_schedules(array()); 67 | 68 | $this->assertNotEmpty($schedules['five_minutes']); 69 | $this->assertEquals(300, $schedules['five_minutes']['interval']); 70 | $this->assertNotEmpty($schedules['three_hours']); 71 | $this->assertEquals(10800, $schedules['three_hours']['interval']); 72 | $this->assertNotEmpty($schedules['fiveteen_minutes']); 73 | $this->assertEquals(900, $schedules['fiveteen_minutes']['interval']); 74 | } 75 | 76 | public function test_deactivate() 77 | { 78 | wp_schedule_event(time(), 'three_hours', 'retailcrm_icml'); 79 | wp_schedule_event(time(), 'five_minutes', 'retailcrm_history'); 80 | wp_schedule_event(time(), 'fiveteen_minutes', 'retailcrm_inventories'); 81 | 82 | $this->plugin->deactivate(); 83 | 84 | $this->assertEquals(false, wp_next_scheduled('retailcrm_icml')); 85 | $this->assertEquals(false, wp_next_scheduled('retailcrm_history')); 86 | $this->assertEquals(false, wp_next_scheduled('retailcrm_inventories')); 87 | } 88 | 89 | public function test_register_deactivation_and_activation_hook() 90 | { 91 | global $wp_filter; 92 | 93 | $this->plugin->register_activation_hook(); 94 | $this->plugin->register_deactivation_hook(); 95 | 96 | $actions = array(); 97 | 98 | foreach (array_keys($wp_filter) as $key) { 99 | if (false !== strpos($key, 'retailcrm')) { 100 | if (false !== strpos($key, 'deactivate_')) { 101 | $actions['deactivate'] = $key; 102 | } 103 | 104 | if (false !== strpos($key, 'activate_')) { 105 | $actions['activate'] = $key; 106 | } 107 | } 108 | } 109 | 110 | $this->assertArrayHasKey('deactivate', $actions); 111 | $this->assertNotEmpty($actions['deactivate']); 112 | $this->assertArrayHasKey('activate', $actions); 113 | $this->assertNotEmpty($actions['activate']); 114 | } 115 | 116 | public function dataProviderIntegrationModule() 117 | { 118 | $this->setUp(); 119 | 120 | return array( 121 | array( 122 | 'retailcrm' => $this->apiMock, 123 | 'responseStatus' => true 124 | ), 125 | array( 126 | 'retailcrm' => false, 127 | 'responseStatus' => true 128 | ), 129 | array( 130 | 'retailcrm' => $this->apiMock, 131 | 'responseStatus' => false 132 | ), 133 | array( 134 | 'retailcrm' => false, 135 | 'responseStatus' => false 136 | ), 137 | ); 138 | } 139 | } 140 | 141 | -------------------------------------------------------------------------------- /tests/test-wc-retailcrm-upload-discount-price.php: -------------------------------------------------------------------------------- 1 | 12 | * @license http://retailcrm.ru Proprietary 13 | * @link http://retailcrm.ru 14 | * @see http://help.retailcrm.ru 15 | */ 16 | class WC_Retailcrm_Upload_Discount_Price_Test extends WC_Retailcrm_Test_Case_Helper 17 | { 18 | protected $apiMock; 19 | protected $responseMock; 20 | 21 | public function setUp() 22 | { 23 | WC_Helper_Product::create_simple_product(); 24 | WC_Helper_Product::create_variation_product(); 25 | 26 | $this->setOptions(); 27 | 28 | $this->responseMock = $this->getMockBuilder('\WC_Retailcrm_Response_Helper') 29 | ->disableOriginalConstructor() 30 | ->setMethods(['isSuccessful']) 31 | ->getMock() 32 | ; 33 | 34 | $this->apiMock = $this->getMockBuilder('\WC_Retailcrm_Client_V5') 35 | ->disableOriginalConstructor() 36 | ->setMethods(['storePricesUpload', 'getSingleSiteForKey', 'getPriceTypes', 'editPriceType']) 37 | ->getMock() 38 | ; 39 | 40 | $this->responseMock->setResponse(['success' => true]); 41 | $this->setMockResponse($this->responseMock, 'isSuccessful', true); 42 | $this->setMockResponse($this->apiMock, 'getSingleSiteForKey', 'woo'); 43 | 44 | $this->responseMock->setResponse(DataUploadPriceRetailCrm::dataGetPriceTypes()); 45 | $this->setMockResponse($this->apiMock, 'getPriceTypes', $this->responseMock); 46 | } 47 | 48 | public function testUpload() 49 | { 50 | $this->apiMock 51 | ->expects($this->exactly(1)) 52 | ->method('storePricesUpload') 53 | ->with($this->callback( 54 | function ($parameter) { 55 | if (is_array($parameter)) { 56 | return true; 57 | } 58 | 59 | return false; 60 | } 61 | ), $this->equalTo('woo')) 62 | ; 63 | 64 | $this->apiMock 65 | ->expects($this->exactly(1)) 66 | ->method('editPriceType') 67 | ->with($this->identicalTo(DataUploadPriceRetailCrm::willSendPriceType())) 68 | ->willReturn($this->responseMock) 69 | ; 70 | 71 | $uploadService = new WC_Retailcrm_Upload_Discount_Price($this->apiMock); 72 | $uploadService->upload(); 73 | } 74 | } 75 | --------------------------------------------------------------------------------