Commit 714f42c5382eefb0ad61faed986fb21b32e41475

Authored by Yarik
1 parent 72390645

Odoo completed

CHANGELOG.md 0 → 100755
  1 +# Change Log
  2 +All notable changes to this project will be documented in this file.
  3 +
  4 +## 1.0.0 - 2017-03-21
  5 +### Added
  6 +- This CHANGELOG file to hopefully serve as an evolving example of a standardized open source project CHANGELOG.
  7 +- Added initial Artbox Core extension.
0 8 \ No newline at end of file
... ...
LICENSE.md 0 → 100755
  1 +The Yii framework is free software. It is released under the terms of
  2 +the following BSD License.
  3 +
  4 +Copyright © 2008 by Yii Software LLC (http://www.yiisoft.com)
  5 +All rights reserved.
  6 +
  7 +Redistribution and use in source and binary forms, with or without
  8 +modification, are permitted provided that the following conditions
  9 +are met:
  10 +
  11 + * Redistributions of source code must retain the above copyright
  12 + notice, this list of conditions and the following disclaimer.
  13 + * Redistributions in binary form must reproduce the above copyright
  14 + notice, this list of conditions and the following disclaimer in
  15 + the documentation and/or other materials provided with the
  16 + distribution.
  17 + * Neither the name of Yii Software LLC nor the names of its
  18 + contributors may be used to endorse or promote products derived
  19 + from this software without specific prior written permission.
  20 +
  21 +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
  22 +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
  23 +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
  24 +FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
  25 +COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
  26 +INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
  27 +BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
  28 +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
  29 +CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
  30 +LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
  31 +ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
  32 +POSSIBILITY OF SUCH DAMAGE.
... ...
README.md 100644 → 100755
  1 +Artbox Core
  2 +===============================
  3 +
  4 +Artbox Core is a core extension of light-weight CMS developed by Artweb written with
  5 +[Yii 2 framework](http://www.yiiframework.com/).
  6 +
  7 +Core extension includes functionality for app Internationalization, basic SEO optimization,
  8 +feedback, image manipulation, user data and static pages control. Also provides events
  9 +notifications.
  10 +
  11 +This extension is enough to develop landings or small websites with static pages.
  12 +
  13 +To prepare your application you should run migrations:
  14 +
  15 + php yii migrate --migationPath=vendor/artweb/artbox-core/migrations
  16 +
  17 +DIRECTORY STRUCTURE
  18 +-------------------
  19 +
  20 +```
  21 +assets contains AssetBundles
  22 +behaviors contains Behaviors classes
  23 +components contains custom Classes, which don't belong to other groups
  24 +controllers contains controllers for core models
  25 +helpers contains helper classes to manipulate, for example static files
  26 + and HTML
  27 +messages contains translations for core strings
  28 +migrations contains migrations, which should be applied after extension
  29 + installation
  30 +models contains core models
  31 +views contains views files for core controllers
  32 +web contains assets and other files, which should be web available
  33 +widgets contains widgets
  34 +```
... ...
assets/ArtboxOdooAsset.php 0 → 100755
  1 +<?php
  2 +
  3 + namespace artbox\odoo\assets;
  4 +
  5 + use yii\web\AssetBundle;
  6 +
  7 + /**
  8 + * Asset class for artbox-core
  9 + */
  10 + class ArtboxOdooAsset extends AssetBundle
  11 + {
  12 + /**
  13 + * @inheritdoc
  14 + */
  15 + public $sourcePath = '@artbox/odoo/web';
  16 +
  17 + /**
  18 + * @inheritdoc
  19 + */
  20 + public $css = [];
  21 +
  22 + /**
  23 + * @inheritdoc
  24 + */
  25 + public $js = [
  26 + 'js/script.js',
  27 + ];
  28 +
  29 + public $depends = [
  30 + 'yii\web\JqueryAsset',
  31 + ];
  32 + }
... ...
components/Builder.php 0 → 100644
  1 +<?php
  2 +
  3 + namespace artbox\odoo\components;
  4 +
  5 + use yii\base\Object;
  6 +
  7 + class Builder extends Object
  8 + {
  9 + protected $db;
  10 +
  11 + protected $parameters = [
  12 + [],
  13 + [],
  14 + ];
  15 +
  16 + /**
  17 + * Builder constructor.
  18 + *
  19 + * @param \artbox\odoo\components\Connection $connection
  20 + * @param array $config
  21 + */
  22 + public function __construct(Connection $connection, array $config = [])
  23 + {
  24 + $this->db = $connection;
  25 + parent::__construct($config);
  26 + }
  27 +
  28 + /**
  29 + * Select ID of model, which will be modified or deleted
  30 + *
  31 + * @param int $id
  32 + *
  33 + * @return \artbox\odoo\components\Builder
  34 + */
  35 + public function addId(int $id): Builder
  36 + {
  37 + $this->parameters[ 0 ][] = $id;
  38 + return $this;
  39 + }
  40 +
  41 + /**
  42 + * Set $param of model, which will be created or modified
  43 + *
  44 + * @param string $param
  45 + * @param string|array $value
  46 + *
  47 + * @return \artbox\odoo\components\Builder
  48 + */
  49 + public function setParam(string $param, $value): Builder
  50 + {
  51 + $this->parameters[ 1 ][ $param ] = $value;
  52 + return $this;
  53 + }
  54 +
  55 + /**
  56 + * Generate create command
  57 + *
  58 + * @param string $model
  59 + *
  60 + * @return \artbox\odoo\components\Command
  61 + */
  62 + public function create(string $model): Command
  63 + {
  64 + return $this->db->createCommand($model, 'create', [ $this->parameters[ 1 ] ]);
  65 + }
  66 +
  67 + /**
  68 + * Generate update model
  69 + *
  70 + * @param string $model
  71 + *
  72 + * @return \artbox\odoo\components\Command
  73 + */
  74 + public function update(string $model): Command
  75 + {
  76 + return $this->db->createCommand($model, 'write', $this->parameters);
  77 + }
  78 +
  79 + /**
  80 + * Generate delete model
  81 + *
  82 + * @param string $model
  83 + *
  84 + * @return \artbox\odoo\components\Command
  85 + */
  86 + public function delete(string $model): Command
  87 + {
  88 + return $this->db->createCommand($model, 'unlink', [ $this->parameters[ 0 ] ]);
  89 + }
  90 +
  91 + /**
  92 + * Clear builder
  93 + *
  94 + * @return \artbox\odoo\components\Builder
  95 + */
  96 + public function clear(): Builder
  97 + {
  98 + $this->parameters = [
  99 + [],
  100 + [],
  101 + ];
  102 + return $this;
  103 + }
  104 + }
0 105 \ No newline at end of file
... ...
components/Command.php 0 → 100644
  1 +<?php
  2 +
  3 + namespace artbox\odoo\components;
  4 +
  5 + use RuntimeException;
  6 + use yii\base\Configurable;
  7 + use yii\base\InvalidConfigException;
  8 + use yii\base\Object;
  9 +
  10 + class Command extends Object implements Configurable
  11 + {
  12 + /**
  13 + * @var \artbox\odoo\components\Connection $db Odoo connection
  14 + */
  15 + public $db;
  16 +
  17 + /**
  18 + * @var string $model Model name
  19 + */
  20 + public $model;
  21 +
  22 + /**
  23 + * @var string $method Method name
  24 + */
  25 + public $method;
  26 +
  27 + /**
  28 + * @var array $parameters Parameters passed by position
  29 + */
  30 + public $parameters;
  31 +
  32 + /**
  33 + * @var array Mapping of parameters to pass by keyword
  34 + */
  35 + public $mapping = [];
  36 +
  37 + public function init()
  38 + {
  39 + parent::init();
  40 + if (!$this->db instanceof Connection) {
  41 + throw new InvalidConfigException('$db must be instance of ' . Connection::className());
  42 + }
  43 + }
  44 +
  45 + /**
  46 + * Executes this command.
  47 + *
  48 + * @return array result cursor.
  49 + * @throws Exception on failure.
  50 + */
  51 + public function execute()
  52 + {
  53 + $token = $this->log(
  54 + [
  55 + $this->model,
  56 + 'command',
  57 + ],
  58 + $this->parameters,
  59 + __METHOD__
  60 + );
  61 + try {
  62 + $this->beginProfile($token, __METHOD__);
  63 + $this->db->open();
  64 + $result = $this->internalExecute();
  65 + $this->endProfile($token, __METHOD__);
  66 + } catch (RuntimeException $e) {
  67 + $this->endProfile($token, __METHOD__);
  68 + throw new Exception($e->getMessage(), $e->getCode(), $e);
  69 + }
  70 + return $result;
  71 + }
  72 +
  73 + /**
  74 + * Executes this command as a Odoo query
  75 + *
  76 + * @param string $model model
  77 + * @param array $parameters
  78 + *
  79 + * @return array result cursor.
  80 + * @throws \artbox\odoo\components\Exception on failure
  81 + */
  82 + public function query($model, $parameters = [ [] ])
  83 + {
  84 + $this->model = $model;
  85 + $this->parameters = $parameters;
  86 + $token = $this->log(
  87 + 'find',
  88 + array_merge(
  89 + [
  90 + 'model' => $model,
  91 + ],
  92 + $parameters
  93 + ),
  94 + __METHOD__
  95 + );
  96 + try {
  97 + $this->beginProfile($token, __METHOD__);
  98 + $this->db->open();
  99 + $result = $this->internalExecute();
  100 + $this->endProfile($token, __METHOD__);
  101 + } catch (RuntimeException $e) {
  102 + $this->endProfile($token, __METHOD__);
  103 + throw new Exception($e->getMessage(), $e->getCode(), $e);
  104 + }
  105 + return $result;
  106 + }
  107 +
  108 + /**
  109 + * Executes this command as a Odoo find
  110 + *
  111 + * @param string $model model
  112 + * @param array $parameters
  113 + * @param array $mapping
  114 + *
  115 + * @return array result cursor.
  116 + * @throws \artbox\odoo\components\Exception on failure
  117 + */
  118 + public function find($model, $parameters = [ [] ], $mapping = [])
  119 + {
  120 + $this->model = $model;
  121 + $this->parameters = $parameters;
  122 + $this->mapping = $mapping;
  123 + $token = $this->log(
  124 + 'find',
  125 + array_merge(
  126 + [
  127 + 'model' => $model,
  128 + ],
  129 + $parameters,
  130 + $mapping
  131 + ),
  132 + __METHOD__
  133 + );
  134 + try {
  135 + $this->beginProfile($token, __METHOD__);
  136 + $this->db->open();
  137 + $result = $this->internalExecute();
  138 + $this->endProfile($token, __METHOD__);
  139 + } catch (RuntimeException $e) {
  140 + $this->endProfile($token, __METHOD__);
  141 + throw new Exception($e->getMessage(), $e->getCode(), $e);
  142 + }
  143 + return $result;
  144 + }
  145 +
  146 + /**
  147 + * Return ids from Odoo by filter parameters
  148 + *
  149 + * @param string $model model
  150 + * @param array $parameters
  151 + * @param array $options
  152 + *
  153 + * @return array result cursor.
  154 + * @throws \artbox\odoo\components\Exception on failure
  155 + */
  156 + public function search(string $model, array $parameters = [ [] ], array $options = [])
  157 + {
  158 + $token = $this->log(
  159 + 'search',
  160 + array_merge(
  161 + [
  162 + 'model' => $model,
  163 + ],
  164 + $parameters,
  165 + $options
  166 + ),
  167 + __METHOD__
  168 + );
  169 + try {
  170 + $this->beginProfile($token, __METHOD__);
  171 + $this->db->open();
  172 + $result = $this->internalRun($model, 'search', $parameters, $options);
  173 + $this->endProfile($token, __METHOD__);
  174 + } catch (RuntimeException $e) {
  175 + $this->endProfile($token, __METHOD__);
  176 + throw new Exception($e->getMessage(), $e->getCode(), $e);
  177 + }
  178 + return $result;
  179 + }
  180 +
  181 + /**
  182 + * Return count of records from Odoo by filter parameters
  183 + *
  184 + * @param string $model model
  185 + * @param array $parameters
  186 + * @param array $options
  187 + *
  188 + * @return integer result cursor.
  189 + * @throws \artbox\odoo\components\Exception on failure
  190 + */
  191 + public function count(string $model, array $parameters = [ [] ], array $options = [])
  192 + {
  193 + $token = $this->log(
  194 + 'count',
  195 + array_merge(
  196 + [
  197 + 'model' => $model,
  198 + ],
  199 + $parameters,
  200 + $options
  201 + ),
  202 + __METHOD__
  203 + );
  204 + try {
  205 + $this->beginProfile($token, __METHOD__);
  206 + $this->db->open();
  207 + $result = $this->internalRun($model, 'search_count', $parameters, $options);
  208 + $this->endProfile($token, __METHOD__);
  209 + } catch (RuntimeException $e) {
  210 + $this->endProfile($token, __METHOD__);
  211 + throw new Exception($e->getMessage(), $e->getCode(), $e);
  212 + }
  213 + return $result;
  214 + }
  215 +
  216 + /**
  217 + * Return records from Odoo by filter parameters
  218 + *
  219 + * @param string $model model
  220 + * @param array $ids
  221 + * @param array $fields
  222 + *
  223 + * @return array result cursor.
  224 + * @throws \artbox\odoo\components\Exception on failure
  225 + */
  226 + public function read(string $model, array $ids, array $fields = [])
  227 + {
  228 + $token = $this->log(
  229 + 'read',
  230 + array_merge(
  231 + [
  232 + 'model' => $model,
  233 + ],
  234 + $ids
  235 + ),
  236 + __METHOD__
  237 + );
  238 + try {
  239 + $this->beginProfile($token, __METHOD__);
  240 + $this->db->open();
  241 + $result = $this->internalRun($model, 'read', $ids, $fields);
  242 + $this->endProfile($token, __METHOD__);
  243 + } catch (RuntimeException $e) {
  244 + $this->endProfile($token, __METHOD__);
  245 + throw new Exception($e->getMessage(), $e->getCode(), $e);
  246 + }
  247 + return $result;
  248 + }
  249 +
  250 + /**
  251 + * Return fields from Odoo by filter parameters
  252 + *
  253 + * @param string $model model
  254 + * @param array $parameters
  255 + * @param array $options
  256 + *
  257 + * @return array result cursor.
  258 + * @throws \artbox\odoo\components\Exception on failure
  259 + */
  260 + public function fields(string $model, array $parameters = [], array $options = [])
  261 + {
  262 + $token = $this->log(
  263 + 'fields',
  264 + array_merge(
  265 + [
  266 + 'model' => $model,
  267 + ],
  268 + $parameters,
  269 + $options
  270 + ),
  271 + __METHOD__
  272 + );
  273 + try {
  274 + $this->beginProfile($token, __METHOD__);
  275 + $this->db->open();
  276 + $result = $this->internalRun($model, 'fields_get', $parameters, $options);
  277 + $this->endProfile($token, __METHOD__);
  278 + } catch (RuntimeException $e) {
  279 + $this->endProfile($token, __METHOD__);
  280 + throw new Exception($e->getMessage(), $e->getCode(), $e);
  281 + }
  282 + return $result;
  283 + }
  284 +
  285 + /**
  286 + * Search and read records from Odoo by filter parameters
  287 + *
  288 + * @param string $model model
  289 + * @param array $parameters
  290 + * @param array $options
  291 + *
  292 + * @return array result cursor.
  293 + * @throws \artbox\odoo\components\Exception on failure
  294 + */
  295 + public function searchRead(string $model, array $parameters = [], array $options = [])
  296 + {
  297 + $token = $this->log(
  298 + 'search_read',
  299 + array_merge(
  300 + [
  301 + 'model' => $model,
  302 + ],
  303 + $parameters,
  304 + $options
  305 + ),
  306 + __METHOD__
  307 + );
  308 + try {
  309 + $this->beginProfile($token, __METHOD__);
  310 + $this->db->open();
  311 + $result = $this->internalRun($model, 'search_read', $parameters, $options);
  312 + $this->endProfile($token, __METHOD__);
  313 + } catch (RuntimeException $e) {
  314 + $this->endProfile($token, __METHOD__);
  315 + throw new Exception($e->getMessage(), $e->getCode(), $e);
  316 + }
  317 + return $result;
  318 + }
  319 +
  320 + /**
  321 + * Create record in Odoo and return id
  322 + *
  323 + * @param string $model model
  324 + * @param array $parameters
  325 + *
  326 + * @return array result cursor.
  327 + * @throws \artbox\odoo\components\Exception on failure
  328 + * @internal param array $options
  329 + */
  330 + public function create(string $model, array $parameters = [])
  331 + {
  332 + $token = $this->log(
  333 + 'create',
  334 + array_merge(
  335 + [
  336 + 'model' => $model,
  337 + ],
  338 + $parameters
  339 + ),
  340 + __METHOD__
  341 + );
  342 + try {
  343 + $this->beginProfile($token, __METHOD__);
  344 + $this->db->open();
  345 + $result = $this->internalRun($model, 'create', $parameters);
  346 + $this->endProfile($token, __METHOD__);
  347 + } catch (RuntimeException $e) {
  348 + $this->endProfile($token, __METHOD__);
  349 + throw new Exception($e->getMessage(), $e->getCode(), $e);
  350 + }
  351 + return $result;
  352 + }
  353 +
  354 + /**
  355 + * Update record in Odoo
  356 + *
  357 + * @param string $model model
  358 + * @param array $parameters
  359 + *
  360 + * @return array result cursor.
  361 + * @throws \artbox\odoo\components\Exception on failure
  362 + * @internal param array $options
  363 + */
  364 + public function update(string $model, array $parameters = [])
  365 + {
  366 + $token = $this->log(
  367 + 'update',
  368 + array_merge(
  369 + [
  370 + 'model' => $model,
  371 + ],
  372 + $parameters
  373 + ),
  374 + __METHOD__
  375 + );
  376 + try {
  377 + $this->beginProfile($token, __METHOD__);
  378 + $this->db->open();
  379 + $result = $this->internalRun($model, 'write', $parameters);
  380 + $this->endProfile($token, __METHOD__);
  381 + } catch (RuntimeException $e) {
  382 + $this->endProfile($token, __METHOD__);
  383 + throw new Exception($e->getMessage(), $e->getCode(), $e);
  384 + }
  385 + return $result;
  386 + }
  387 +
  388 + /**
  389 + * Delete records from Odoo
  390 + *
  391 + * @param string $model model
  392 + * @param array $parameters
  393 + *
  394 + * @return array result cursor.
  395 + * @throws \artbox\odoo\components\Exception on failure
  396 + * @internal param array $options
  397 + */
  398 + public function delete(string $model, array $parameters = [])
  399 + {
  400 + $token = $this->log(
  401 + 'delete',
  402 + array_merge(
  403 + [
  404 + 'model' => $model,
  405 + ],
  406 + $parameters
  407 + ),
  408 + __METHOD__
  409 + );
  410 + try {
  411 + $this->beginProfile($token, __METHOD__);
  412 + $this->db->open();
  413 + $result = $this->internalRun($model, 'unlink', $parameters);
  414 + $this->endProfile($token, __METHOD__);
  415 + } catch (RuntimeException $e) {
  416 + $this->endProfile($token, __METHOD__);
  417 + throw new Exception($e->getMessage(), $e->getCode(), $e);
  418 + }
  419 + return $result;
  420 + }
  421 +
  422 + /**
  423 + * Logs the command data if logging is enabled at [[db]].
  424 + *
  425 + * @param array|string $namespace command namespace.
  426 + * @param array $data command data.
  427 + * @param string $category log category
  428 + *
  429 + * @return string|false log token, `false` if log is not enabled.
  430 + */
  431 + protected function log($namespace, $data, $category)
  432 + {
  433 + if ($this->db->enableLogging) {
  434 + $token = $this->db->getLogBuilder()
  435 + ->generateToken($namespace, $data);
  436 + \Yii::info($token, $category);
  437 + return $token;
  438 + }
  439 + return false;
  440 + }
  441 + /**
  442 + * Marks the beginning of a code block for profiling.
  443 + *
  444 + * @param string $token token for the code block
  445 + * @param string $category the category of this log message
  446 + *
  447 + * @see endProfile()
  448 + */
  449 + protected function beginProfile($token, $category)
  450 + {
  451 + if ($token !== false && $this->db->enableProfiling) {
  452 + \Yii::beginProfile($token, $category);
  453 + }
  454 + }
  455 + /**
  456 + * Marks the end of a code block for profiling.
  457 + *
  458 + * @param string $token token for the code block
  459 + * @param string $category the category of this log message
  460 + *
  461 + * @see beginProfile()
  462 + */
  463 + protected function endProfile($token, $category)
  464 + {
  465 + if ($token !== false && $this->db->enableProfiling) {
  466 + \Yii::endProfile($token, $category);
  467 + }
  468 + }
  469 +
  470 + protected function internalExecute()
  471 + {
  472 + $client = $this->db->getFetchClient();
  473 + return call_user_func_array(
  474 + [
  475 + $client,
  476 + 'execute_kw',
  477 + ],
  478 + [
  479 + $this->db->db,
  480 + $this->db->getUid(),
  481 + $this->db->password,
  482 + $this->model,
  483 + $this->method,
  484 + $this->parameters,
  485 + $this->mapping,
  486 + ]
  487 + );
  488 + }
  489 +
  490 + /**
  491 + * @param string $model
  492 + * @param string $method
  493 + * @param array $parameters
  494 + * @param array $mapping
  495 + *
  496 + * @return mixed
  497 + */
  498 + protected function internalRun(string $model, string $method, array $parameters, array $mapping = [])
  499 + {
  500 + $client = $this->db->getFetchClient();
  501 + return call_user_func_array(
  502 + [
  503 + $client,
  504 + 'execute_kw',
  505 + ],
  506 + [
  507 + $this->db->db,
  508 + $this->db->getUid(),
  509 + $this->db->password,
  510 + $model,
  511 + $method,
  512 + $parameters,
  513 + $mapping,
  514 + ]
  515 + );
  516 + }
  517 + }
0 518 \ No newline at end of file
... ...
components/Connection.php 0 → 100644
  1 +<?php
  2 +
  3 + namespace artbox\odoo\components;
  4 +
  5 + use Ripcord\Ripcord;
  6 + use yii\base\Component;
  7 + use yii\base\Configurable;
  8 + use yii\base\InvalidConfigException;
  9 +
  10 + /**
  11 + * Odoo connection class
  12 + *
  13 + * @property string $dsn Connection string
  14 + * @property null|integer $uid Authenticated user ID
  15 + * @property \Ripcord\Client\Client $fetchClient Fetch client
  16 + */
  17 + class Connection extends Component implements Configurable
  18 + {
  19 + /**
  20 + * @event Event an event that is triggered after a DB connection is established
  21 + */
  22 + const EVENT_AFTER_OPEN = 'afterOpen';
  23 +
  24 + /**
  25 + * @var string $url Odoo server URL
  26 + */
  27 + public $url;
  28 +
  29 + /**
  30 + * @var string $db Odoo db name
  31 + */
  32 + public $db;
  33 +
  34 + /**
  35 + * @var string $username Odoo username
  36 + */
  37 + public $username;
  38 +
  39 + /**
  40 + * @var string $password Odoo password
  41 + */
  42 + public $password;
  43 +
  44 + /**
  45 + * @var bool whether to log command and query executions.
  46 + * When enabled this option may reduce performance. Odoo commands may contain large data,
  47 + * consuming both CPU and memory.
  48 + * It makes sense to disable this option in the production environment.
  49 + */
  50 + public $enableLogging = true;
  51 +
  52 + /**
  53 + * @var bool whether to enable profiling the commands and queries being executed.
  54 + * This option will have no effect in case [[enableLogging]] is disabled.
  55 + */
  56 + public $enableProfiling = true;
  57 +
  58 + /**
  59 + * @var integer $uid Authenticated User ID
  60 + */
  61 + protected $uid;
  62 +
  63 + /**
  64 + * @var \Ripcord\Client\Client $client Odoo client
  65 + */
  66 + protected $client;
  67 +
  68 + /**
  69 + * @var \Ripcord\Client\Client $client Odoo fetch client
  70 + */
  71 + protected $fetchClient;
  72 +
  73 + /**
  74 + * @var QueryBuilder|array|string the query builder for this connection
  75 + */
  76 + private $_queryBuilder = 'artbox\odoo\components\QueryBuilder';
  77 + /**
  78 + * @var LogBuilder|array|string log entries builder used for this connecton.
  79 + */
  80 + private $_logBuilder = 'artbox\odoo\components\LogBuilder';
  81 +
  82 + public function __construct(array $config = [])
  83 + {
  84 + parent::__construct($config);
  85 + }
  86 +
  87 + /**
  88 + * Returns the query builder for the this Odoo connection.
  89 + *
  90 + * @return QueryBuilder the query builder for the this Odoo connection.
  91 + */
  92 + public function getQueryBuilder(): QueryBuilder
  93 + {
  94 + if (!is_object($this->_queryBuilder)) {
  95 + $this->_queryBuilder = \Yii::createObject($this->_queryBuilder, [ $this ]);
  96 + }
  97 + return $this->_queryBuilder;
  98 + }
  99 + /**
  100 + * Sets the query builder for the this Odoo connection.
  101 + *
  102 + * @param QueryBuilder|array|string|null $queryBuilder the query builder for this Odoo connection.
  103 + */
  104 + public function setQueryBuilder($queryBuilder)
  105 + {
  106 + $this->_queryBuilder = $queryBuilder;
  107 + }
  108 + /**
  109 + * Returns log builder for this connection.
  110 + *
  111 + * @return LogBuilder the log builder for this connection.
  112 + */
  113 + public function getLogBuilder(): LogBuilder
  114 + {
  115 + if (!is_object($this->_logBuilder)) {
  116 + $this->_logBuilder = \Yii::createObject($this->_logBuilder);
  117 + }
  118 + return $this->_logBuilder;
  119 + }
  120 + /**
  121 + * Sets log builder used for this connection.
  122 + *
  123 + * @param array|string|LogBuilder $logBuilder the log builder for this connection.
  124 + */
  125 + public function setLogBuilder($logBuilder)
  126 + {
  127 + $this->_logBuilder = $logBuilder;
  128 + }
  129 +
  130 + /**
  131 + * Establishes a Odoo connection.
  132 + * It does nothing if a Odoo connection has already been established.
  133 + *
  134 + * @throws Exception if connection fails
  135 + */
  136 + public function open()
  137 + {
  138 + if ($this->client === null) {
  139 + if (empty($this->username) || empty($this->db) || empty($this->password) || empty($this->url)) {
  140 + throw new InvalidConfigException(
  141 + '$username, $db, $password, $url must be set for ' . $this->className()
  142 + );
  143 + }
  144 + $token = "Opening Odoo connection: " . $this->getDsn();
  145 + try {
  146 + \Yii::trace($token, __METHOD__);
  147 + \Yii::beginProfile($token, __METHOD__);
  148 + $this->client = Ripcord::client("$this->url/xmlrpc/2/common");
  149 + $this->uid = call_user_func_array(
  150 + [
  151 + $this->client,
  152 + 'authenticate',
  153 + ],
  154 + [
  155 + $this->db,
  156 + $this->username,
  157 + $this->password,
  158 + [],
  159 + ]
  160 + );
  161 + if (!$this->uid) {
  162 + throw new InvalidConfigException("Incorrect authentication data.");
  163 + }
  164 + $this->fetchClient = Ripcord::client("$this->url/xmlrpc/2/object");
  165 + $this->initConnection();
  166 + \Yii::endProfile($token, __METHOD__);
  167 + } catch (\Exception $e) {
  168 + \Yii::endProfile($token, __METHOD__);
  169 + throw new Exception($e->getMessage(), (int) $e->getCode(), $e);
  170 + }
  171 + }
  172 + }
  173 +
  174 + /**
  175 + * Closes the currently active DB connection.
  176 + * It does nothing if the connection is already closed.
  177 + */
  178 + public function close()
  179 + {
  180 + if ($this->client !== null) {
  181 + \Yii::trace('Closing Odoo connection: ' . $this->getDsn(), __METHOD__);
  182 + $this->client = null;
  183 + $this->uid = null;
  184 + }
  185 + }
  186 +
  187 + /**
  188 + * Initializes the DB connection.
  189 + * This method is invoked right after the DB connection is established.
  190 + * The default implementation triggers an [[EVENT_AFTER_OPEN]] event.
  191 + */
  192 + protected function initConnection()
  193 + {
  194 + $this->trigger(self::EVENT_AFTER_OPEN);
  195 + }
  196 +
  197 + /**
  198 + * Creates Odoo command.
  199 + *
  200 + * @param string $model Model name
  201 + * @param string $method Method name
  202 + * @param array $parameters parameters passed by position
  203 + * @param array $mapping mapping of parameters to pass by keyword
  204 + *
  205 + * @return \artbox\odoo\components\Command command instance.
  206 + */
  207 + public function createCommand(string $model, string $method, array $parameters, array $mapping = []): Command
  208 + {
  209 + return new Command(
  210 + [
  211 + 'db' => $this,
  212 + 'model' => $model,
  213 + 'method' => $method,
  214 + 'parameters' => $parameters,
  215 + 'mapping' => $mapping,
  216 + ]
  217 + );
  218 + }
  219 +
  220 + /**
  221 + * Get dsn string
  222 + *
  223 + * @return string
  224 + */
  225 + public function getDsn()
  226 + {
  227 + return $this->url . ', ' . $this->db . ', ' . $this->username . ', ' . $this->password;
  228 + }
  229 +
  230 + /**
  231 + * Get authenticated user ID
  232 + */
  233 + public function getUid()
  234 + {
  235 + return $this->uid;
  236 + }
  237 +
  238 + /**
  239 + * Get fetch client
  240 + *
  241 + * @return \Ripcord\Client\Client
  242 + */
  243 + public function getFetchClient()
  244 + {
  245 + return $this->fetchClient;
  246 + }
  247 +
  248 + /**
  249 + * Get common client
  250 + *
  251 + * @return \Ripcord\Client\Client
  252 + */
  253 + public function getClient()
  254 + {
  255 + return $this->client;
  256 + }
  257 + }
0 258 \ No newline at end of file
... ...
components/Exception.php 0 → 100644
  1 +<?php
  2 +
  3 + namespace artbox\odoo\components;
  4 +
  5 + /**
  6 + * Exception represents an exception that is caused by some Odoo-related operations.
  7 + */
  8 + class Exception extends \yii\base\Exception
  9 + {
  10 + /**
  11 + * @return string the user-friendly name of this exception
  12 + */
  13 + public function getName()
  14 + {
  15 + return 'Odoo Exception';
  16 + }
  17 + }
0 18 \ No newline at end of file
... ...
components/LogBuilder.php 0 → 100644
  1 +<?php
  2 +
  3 + namespace artbox\odoo\components;
  4 +
  5 + use yii\base\Object;
  6 +
  7 + class LogBuilder extends Object
  8 + {
  9 + /**
  10 + * Generate log/profile token.
  11 + *
  12 + * @param string|array $namespace command namespace
  13 + * @param array $data command data.
  14 + *
  15 + * @return string token.
  16 + */
  17 + public function generateToken($namespace, $data = [])
  18 + {
  19 + if (is_array($namespace)) {
  20 + $namespace = implode('.', $namespace);
  21 + }
  22 + return $namespace . '(' . $this->encodeData($data) . ')';
  23 + }
  24 + /**
  25 + * Encodes complex log data into JSON format string.
  26 + *
  27 + * @param mixed $data raw data.
  28 + *
  29 + * @return string encoded data string.
  30 + */
  31 + public function encodeData($data)
  32 + {
  33 + return json_encode($this->processData($data));
  34 + }
  35 + /**
  36 + * Pre-processes the log data before sending it to `json_encode()`.
  37 + *
  38 + * @param mixed $data raw data.
  39 + *
  40 + * @return mixed the processed data.
  41 + */
  42 + protected function processData($data)
  43 + {
  44 + if (is_object($data)) {
  45 + $result = [];
  46 + foreach ($data as $name => $value) {
  47 + $result[ $name ] = $value;
  48 + }
  49 + $data = $result;
  50 + if ($data === []) {
  51 + return new \stdClass();
  52 + }
  53 + }
  54 + if (is_array($data)) {
  55 + foreach ($data as $key => $value) {
  56 + if (is_array($value) || is_object($value)) {
  57 + $data[ $key ] = $this->processData($value);
  58 + }
  59 + }
  60 + }
  61 + return $data;
  62 + }
  63 + }
0 64 \ No newline at end of file
... ...
components/OdooHelper.php 0 → 100644
  1 +<?php
  2 +
  3 + namespace artbox\odoo\components;
  4 +
  5 + use artbox\catalog\models\Category;
  6 + use artbox\catalog\models\Product;
  7 + use artbox\odoo\models\OdooToCategory;
  8 + use artbox\odoo\models\OdooToOrder;
  9 + use artbox\odoo\models\OdooToProduct;
  10 + use artbox\order\models\Order;
  11 + use yii\base\Object;
  12 + use yii\helpers\ArrayHelper;
  13 +
  14 + class OdooHelper extends Object
  15 + {
  16 + /**
  17 + * @var \artbox\odoo\components\Connection $db
  18 + */
  19 + protected $db;
  20 +
  21 + /**
  22 + * OdooHelper constructor.
  23 + *
  24 + * @param array $config
  25 + */
  26 + public function __construct(array $config = [])
  27 + {
  28 + $this->db = \Yii::$app->get('odoo');
  29 + parent::__construct($config);
  30 + }
  31 +
  32 + /**
  33 + * Create new partner for order
  34 + *
  35 + * @param \artbox\order\models\Order $order
  36 + *
  37 + * @return int
  38 + */
  39 + public function ensurePartner(Order $order): int
  40 + {
  41 + $builder = new Builder($this->db);
  42 + /**
  43 + * @var integer $result
  44 + */
  45 + $result = $builder->setParam('name', $order->name)
  46 + ->setParam('address', $order->address)
  47 + ->setParam('phone', $order->phone)
  48 + ->setParam('email', $order->email)
  49 + ->create('res.partner')
  50 + ->execute();
  51 + return $result;
  52 + }
  53 +
  54 + /**
  55 + * Return OdooToOrder for current Order
  56 + *
  57 + * @param \artbox\order\models\Order $order
  58 + *
  59 + * @return \artbox\odoo\models\OdooToOrder
  60 + */
  61 + public function ensureOrder(Order $order): OdooToOrder
  62 + {
  63 + /**
  64 + * @var OdooToOrder $result
  65 + */
  66 + $result = OdooToOrder::find()
  67 + ->where(
  68 + [
  69 + 'order_id' => $order->id,
  70 + ]
  71 + )
  72 + ->one();
  73 + if ($result && $this->validateOrder($result)) {
  74 + return $result;
  75 + }
  76 + $builder = new Builder($this->db);
  77 + $partnerId = $this->ensurePartner($order);
  78 + $builder->setParam('partner_id', $partnerId);
  79 + $orderId = $builder->create('sale.order')
  80 + ->execute();
  81 + $result = new OdooToOrder(
  82 + [
  83 + 'order_id' => $order->id,
  84 + 'remote_id' => $orderId,
  85 + ]
  86 + );
  87 + $result->save(false);
  88 + return $result;
  89 + }
  90 +
  91 + /**
  92 + * Return OdooToProduct for current Product
  93 + *
  94 + * @param \artbox\catalog\models\Product $product
  95 + *
  96 + * @return \artbox\odoo\models\OdooToProduct
  97 + */
  98 + public function ensureProduct(Product $product): OdooToProduct
  99 + {
  100 + /**
  101 + * @var OdooToProduct $result
  102 + */
  103 + $result = OdooToProduct::find()
  104 + ->where(
  105 + [
  106 + 'product_id' => $product->id,
  107 + ]
  108 + )
  109 + ->one();
  110 + if ($result && $this->validateProduct($result)) {
  111 + return $result;
  112 + }
  113 + $builder = new Builder($this->db);
  114 + $builder->setParam('name', $product->lang->title)
  115 + ->setParam('sale_ok', true)
  116 + ->setParam('purchase_ok', true)
  117 + ->setParam('list_price', $product->variant->price)
  118 + ->setParam('default_code', $product->variant->sku);
  119 + if (!empty($product->category)) {
  120 + $odooToCategory = $this->ensureCategory($product->category);
  121 + $categoryId = $odooToCategory->category_id;
  122 + $builder->setParam('categ_id[]', $categoryId);
  123 + } else {
  124 + $categoryId = null;
  125 + $builder->setParam('categ_id[]', 1);
  126 + }
  127 + $productId = $builder->create('product.template')
  128 + ->execute();
  129 + $result = new OdooToProduct(
  130 + [
  131 + 'product_id' => $product->id,
  132 + 'remote_id' => $productId,
  133 + ]
  134 + );
  135 + $result->save(false);
  136 + return $result;
  137 + }
  138 +
  139 + /**
  140 + * Return OdooToCategory for current Category
  141 + *
  142 + * @param \artbox\catalog\models\Category $category
  143 + *
  144 + * @return \artbox\odoo\models\OdooToCategory
  145 + */
  146 + public function ensureCategory(Category $category): OdooToCategory
  147 + {
  148 + /**
  149 + * @var OdooToCategory $result
  150 + */
  151 + $result = OdooToCategory::find()
  152 + ->where(
  153 + [
  154 + 'category_id' => $category->id,
  155 + ]
  156 + )
  157 + ->one();
  158 + if ($result && $this->validateCategory($result)) {
  159 + return $result;
  160 + }
  161 + $builder = new Builder($this->db);
  162 + $builder->setParam('name', $category->lang->title);
  163 + $categoryId = $builder->create('product.category')
  164 + ->execute();
  165 + $result = new OdooToCategory(
  166 + [
  167 + 'category_id' => $category->id,
  168 + 'remote_id' => $categoryId,
  169 + ]
  170 + );
  171 + $result->save(false);
  172 + return $result;
  173 + }
  174 +
  175 + /**
  176 + * Write products from current Order to sale.order.line
  177 + *
  178 + * @param \artbox\order\models\Order $order
  179 + */
  180 + public function ensureSaleOrderLine(Order $order)
  181 + {
  182 + $odooToOrder = $this->ensureOrder($order);
  183 + $orderProductIds = ArrayHelper::getColumn(
  184 + ( new Query() )->from('sale.order.line')
  185 + ->where(
  186 + [
  187 + 'order_id',
  188 + '=',
  189 + $odooToOrder->remote_id,
  190 + ]
  191 + )
  192 + ->all(),
  193 + 'id'
  194 + );
  195 + foreach ($orderProductIds as $orderProductId) {
  196 + $builder = new Builder($this->db);
  197 + $builder->addId($orderProductId)
  198 + ->delete('sale.order.line')
  199 + ->execute();
  200 + }
  201 + foreach ($order->orderProducts as $orderProduct) {
  202 + $builder = new Builder($this->db);
  203 + $odooToProduct = $this->ensureProduct($orderProduct->variant->product);
  204 + $productId = ( new Query() )->from('product.product')
  205 + ->where(
  206 + [
  207 + 'product_tmpl_id',
  208 + '=',
  209 + $odooToProduct->remote_id,
  210 + ]
  211 + )
  212 + ->one()[ 'id' ];
  213 + $builder->setParam('order_id', $odooToOrder->remote_id)
  214 + ->setParam(
  215 + 'product_id',
  216 + $productId
  217 + )// ->setParam('product_uom', 6)
  218 + ->setParam('product_uom_qty', $orderProduct->count)
  219 + ->setParam('price_unit', $orderProduct->price)
  220 + ->setParam('name', $orderProduct->variant->product->lang->title)
  221 + ->create('sale.order.line')
  222 + ->execute();
  223 + }
  224 + }
  225 +
  226 + /**
  227 + * Validate OdooToOrder in sale.order
  228 + *
  229 + * @param \artbox\odoo\models\OdooToOrder $order
  230 + * @param bool $delete
  231 + *
  232 + * @return bool
  233 + */
  234 + public function validateOrder(OdooToOrder $order, bool $delete = true): bool
  235 + {
  236 + $result = ( new Query() )->from('sale.order')
  237 + ->where(
  238 + [
  239 + 'id',
  240 + '=',
  241 + $order->remote_id,
  242 + ]
  243 + )
  244 + ->one();
  245 + if ($result) {
  246 + return true;
  247 + } else {
  248 + if ($delete) {
  249 + $order->delete();
  250 + }
  251 + return false;
  252 + }
  253 + }
  254 +
  255 + /**
  256 + * Validate OdooToProduct in product.template
  257 + *
  258 + * @param \artbox\odoo\models\OdooToProduct $product
  259 + * @param bool $delete
  260 + *
  261 + * @return bool
  262 + */
  263 + public function validateProduct(OdooToProduct $product, bool $delete = true): bool
  264 + {
  265 + $result = ( new Query() )->from('product.template')
  266 + ->where(
  267 + [
  268 + 'id',
  269 + '=',
  270 + $product->remote_id,
  271 + ]
  272 + )
  273 + ->one();
  274 + if ($result) {
  275 + return true;
  276 + } else {
  277 + if ($delete) {
  278 + $product->delete();
  279 + }
  280 + return false;
  281 + }
  282 + }
  283 +
  284 + /**
  285 + * Validate OdooToCategory in product.category
  286 + *
  287 + * @param \artbox\odoo\models\OdooToCategory $category
  288 + * @param bool $delete
  289 + *
  290 + * @return bool
  291 + */
  292 + public function validateCategory(OdooToCategory $category, bool $delete = true): bool
  293 + {
  294 + $result = ( new Query() )->from('product.category')
  295 + ->where(
  296 + [
  297 + 'id',
  298 + '=',
  299 + $category->remote_id,
  300 + ]
  301 + )
  302 + ->one();
  303 + if ($result) {
  304 + return true;
  305 + } else {
  306 + if ($delete) {
  307 + $category->delete();
  308 + }
  309 + return false;
  310 + }
  311 + }
  312 + }
0 313 \ No newline at end of file
... ...
components/OdooMapper.php 0 → 100644
  1 +<?php
  2 +
  3 + namespace artbox\odoo\components;
  4 +
  5 + use yii\base\Object;
  6 +
  7 + class OdooMapper extends Object
  8 + {
  9 + public $map = [];
  10 +
  11 + /**
  12 + * Map odoo array to artbox array
  13 + *
  14 + * @param array $odoo
  15 + *
  16 + * @return array
  17 + */
  18 + public function toArtbox(array $odoo): array
  19 + {
  20 + $result = [];
  21 + foreach ($this->map as $index => $value) {
  22 + if (isset($odoo[ $index ])) {
  23 + if (is_string($value)) {
  24 + $result[ $value ] = $odoo[ $index ];
  25 + } else {
  26 + $result[ $value[ 'attribute' ] ] = call_user_func($value[ 'artbox' ], $odoo[ $index ]);
  27 + }
  28 + }
  29 + }
  30 + return $result;
  31 + }
  32 +
  33 + /**
  34 + * Map artbox array to odoo array
  35 + *
  36 + * @param array $artbox
  37 + *
  38 + * @return array
  39 + */
  40 + public function toOdoo(array $artbox): array
  41 + {
  42 + $result = [];
  43 + foreach ($this->map as $index => $value) {
  44 + if (is_string($value)) {
  45 + if (isset($artbox[ $value ])) {
  46 + $result[ $index ] = $artbox[ $value ];
  47 + }
  48 + } else {
  49 + if (isset($artbox[ $value[ 'attribute' ] ])) {
  50 + $result[ $index ] = call_user_func($value[ 'odoo' ], $artbox[ $value[ 'attribute' ] ]);
  51 + }
  52 + }
  53 + }
  54 + return $result;
  55 + }
  56 + }
0 57 \ No newline at end of file
... ...
components/Query.php 0 → 100644
  1 +<?php
  2 +
  3 + namespace artbox\odoo\components;
  4 +
  5 + use yii\base\Component;
  6 + use yii\db\QueryInterface;
  7 + use yii\db\QueryTrait;
  8 +
  9 + class Query extends Component implements QueryInterface
  10 + {
  11 + use QueryTrait;
  12 +
  13 + /**
  14 + * @var string the model to be selected from.
  15 + * @see from()
  16 + */
  17 + public $from;
  18 +
  19 + /**
  20 + * @var array the mapping of query
  21 + */
  22 + public $mapping = [];
  23 +
  24 + public $options = [ [] ];
  25 +
  26 + /**
  27 + * Executes the query and returns all results as an array.
  28 + *
  29 + * @param Connection $db the database connection used to execute the query.
  30 + * If this parameter is not given, the `db` application component will be used.
  31 + *
  32 + * @return array the query results. If the query results in nothing, an empty array will be returned.
  33 + */
  34 + public function all($db = null)
  35 + {
  36 + /**
  37 + * @var Connection $db
  38 + */
  39 + if (empty($db)) {
  40 + $db = \Yii::$app->get('odoo');
  41 + }
  42 + return $db->createCommand($this->from, 'search_read', $this->options, $this->mapping)
  43 + ->execute();
  44 + }
  45 + /**
  46 + * Executes the query and returns a single row of result.
  47 + *
  48 + * @param Connection $db the database connection used to execute the query.
  49 + * If this parameter is not given, the `db` application component will be used.
  50 + *
  51 + * @return array|bool the first row (in terms of an array) of the query result. False is returned if the query
  52 + * results in nothing.
  53 + */
  54 + public function one($db = null)
  55 + {
  56 + $result = $this->all($db);
  57 + if (empty($result)) {
  58 + return null;
  59 + } else {
  60 + return $result[ 0 ];
  61 + }
  62 + }
  63 + /**
  64 + * Returns the number of records.
  65 + *
  66 + * @param string $q the COUNT expression. Defaults to '*'.
  67 + * @param Connection $db the database connection used to execute the query.
  68 + * If this parameter is not given, the `db` application component will be used.
  69 + *
  70 + * @return int number of records.
  71 + */
  72 + public function count($q = '*', $db = null)
  73 + {
  74 + /**
  75 + * @var Connection $db
  76 + */
  77 + if (empty($db)) {
  78 + $db = \Yii::$app->get('odoo');
  79 + }
  80 + return ( new Command(
  81 + [
  82 + 'db' => $db,
  83 + ]
  84 + ) )->count('product.product', $this->options, $this->mapping);
  85 + }
  86 + /**
  87 + * Returns a value indicating whether the query result contains any row of data.
  88 + *
  89 + * @param Connection $db the database connection used to execute the query.
  90 + * If this parameter is not given, the `db` application component will be used.
  91 + *
  92 + * @return bool whether the query result contains any row of data.
  93 + */
  94 + public function exists($db = null)
  95 + {
  96 + if ($this->count('*', $db)) {
  97 + return true;
  98 + } else {
  99 + return false;
  100 + }
  101 + }
  102 + /**
  103 + * @param array $fields
  104 + *
  105 + * @return \artbox\odoo\components\Query
  106 + */
  107 + public function select(array $fields)
  108 + {
  109 + $this->mapping[ 'fields' ] = $fields;
  110 +
  111 + return $this;
  112 + }
  113 +
  114 + /**
  115 + * @param $model
  116 + *
  117 + * @return \artbox\odoo\components\Query
  118 + */
  119 + public function from($model)
  120 + {
  121 + $this->from = $model;
  122 +
  123 + return $this;
  124 + }
  125 +
  126 + /**
  127 + * @param array $mapping
  128 + *
  129 + * @return \artbox\odoo\components\Query
  130 + */
  131 + public function mapping(array $mapping)
  132 + {
  133 + $this->mapping = $mapping;
  134 +
  135 + return $this;
  136 + }
  137 +
  138 + /**
  139 + * @param array $mapping
  140 + *
  141 + * @return \artbox\odoo\components\Query
  142 + */
  143 + public function addMapping(array $mapping)
  144 + {
  145 + if (is_array($this->mapping)) {
  146 + $this->mapping = array_merge($this->mapping, $mapping);
  147 + } else {
  148 + $this->mapping = $mapping;
  149 + }
  150 + return $this;
  151 + }
  152 +
  153 + /**
  154 + * @param int|null $offset
  155 + *
  156 + * @return \artbox\odoo\components\Query
  157 + */
  158 + public function offset($offset)
  159 + {
  160 + $this->mapping[ 'offset' ] = $offset;
  161 +
  162 + return $this;
  163 + }
  164 +
  165 + /**
  166 + * @param int|null $limit
  167 + *
  168 + * @return \artbox\odoo\components\Query
  169 + */
  170 + public function limit($limit)
  171 + {
  172 + $this->mapping[ 'limit' ] = $limit;
  173 +
  174 + return $this;
  175 + }
  176 +
  177 + /**
  178 + * @param array|string $condition
  179 + *
  180 + * @return \artbox\odoo\components\Query
  181 + */
  182 + public function where($condition)
  183 + {
  184 + $this->options[ 0 ][] = $condition;
  185 +
  186 + return $this;
  187 + }
  188 +
  189 + /**
  190 + * @param array|string $condition
  191 + *
  192 + * @return \artbox\odoo\components\Query
  193 + */
  194 + public function andWhere($condition)
  195 + {
  196 + return $this->where($condition);
  197 + }
  198 + }
0 199 \ No newline at end of file
... ...
components/QueryBuilder.php 0 → 100644
  1 +<?php
  2 +
  3 + namespace artbox\odoo\components;
  4 +
  5 + use yii\base\Configurable;
  6 + use yii\base\Object;
  7 +
  8 + class QueryBuilder extends Object implements Configurable
  9 + {
  10 + protected $db;
  11 +
  12 + public function __construct(Connection $connection, array $config = [])
  13 + {
  14 + $this->db = $connection;
  15 + parent::__construct($config);
  16 + }
  17 +
  18 + public function setConnection(Connection $connection)
  19 + {
  20 + $this->db = $connection;
  21 + }
  22 +
  23 + public function getConnection(): Connection
  24 + {
  25 + return $this->db;
  26 + }
  27 +
  28 + }
0 29 \ No newline at end of file
... ...
composer.json 0 → 100755
  1 +{
  2 + "name": "artweb/artbox-odoo",
  3 + "description": "Artbox Odoo extension",
  4 + "license": "BSD-3-Clause",
  5 + "minimum-stability": "dev",
  6 + "type": "yii2-extension",
  7 + "require": {
  8 + "php": ">=7.0",
  9 + "yiisoft/yii2": "~2.0",
  10 + "artweb/artbox-core": "~0.0.1",
  11 + "darkaonline/ripcord": "~0.1"
  12 + },
  13 + "autoload": {
  14 + "psr-4": {
  15 + "artbox\\odoo\\": ""
  16 + }
  17 + }
  18 +}
0 19 \ No newline at end of file
... ...
controllers/OdooController.php 0 → 100644
  1 +<?php
  2 +
  3 + namespace artbox\odoo\controllers;
  4 +
  5 + use artbox\catalog\models\Category;
  6 + use artbox\catalog\models\Product;
  7 + use artbox\catalog\models\ProductToCategory;
  8 + use artbox\catalog\models\Variant;
  9 + use artbox\odoo\components\OdooHelper;
  10 + use artbox\odoo\components\Query;
  11 + use artbox\odoo\models\OdooToCategory;
  12 + use artbox\odoo\models\OdooToOrder;
  13 + use artbox\odoo\models\OdooToProduct;
  14 + use artbox\odoo\models\Order;
  15 + use artbox\order\models\OrderProduct;
  16 + use yii\data\ActiveDataProvider;
  17 + use yii\filters\AccessControl;
  18 + use yii\filters\VerbFilter;
  19 + use yii\helpers\Json;
  20 + use yii\web\Controller;
  21 +
  22 + class OdooController extends Controller
  23 + {
  24 + /**
  25 + * @return bool|string
  26 + */
  27 + public function getViewPath()
  28 + {
  29 + return \Yii::getAlias('@artbox/odoo/views/odoo');
  30 + }
  31 + /**
  32 + * @inheritdoc
  33 + */
  34 + public function behaviors()
  35 + {
  36 + return [
  37 + 'access' => [
  38 + 'class' => AccessControl::className(),
  39 + 'rules' => [
  40 + [
  41 + 'actions' => [
  42 + 'login',
  43 + 'error',
  44 + ],
  45 + 'allow' => true,
  46 + ],
  47 + [
  48 + 'allow' => true,
  49 + 'roles' => [ '@' ],
  50 + ],
  51 + ],
  52 + ],
  53 + 'verbs' => [
  54 + 'class' => VerbFilter::className(),
  55 + 'actions' => [],
  56 + ],
  57 + ];
  58 + }
  59 +
  60 + public function actionIndex()
  61 + {
  62 + $products = OdooToProduct::find()
  63 + ->all();
  64 + $orders = OdooToOrder::find()
  65 + ->all();
  66 + $categories = OdooToCategory::find()
  67 + ->all();
  68 + return $this->render(
  69 + 'index',
  70 + [
  71 + 'products' => $products,
  72 + 'orders' => $orders,
  73 + 'categories' => $categories,
  74 + ]
  75 + );
  76 + }
  77 +
  78 + public function actionImport()
  79 + {
  80 + return $this->render('import');
  81 + }
  82 +
  83 + public function actionProcess($from = 0, $limit = 100)
  84 + {
  85 + /**
  86 + * @var \artbox\odoo\components\OdooMapper $mapper
  87 + * @var \artbox\odoo\components\Connection $odoo
  88 + */
  89 + $mapper = \Yii::$app->get('odooMapper');
  90 + $odoo = \Yii::$app->get('odoo');
  91 + $count = $odoo->createCommand('product.template', 'search_count', [ [] ])
  92 + ->execute();
  93 + $response = \Yii::$app->response;
  94 + $response->format = $response::FORMAT_JSON;
  95 + $products = ( new Query() )->from('product.template')
  96 + ->offset(intval($from))
  97 + ->limit(intval($limit))
  98 + ->all();
  99 + foreach ($products as $product) {
  100 + $category = null;
  101 + $artbox = $mapper->toArtbox($product);
  102 + if (!empty($artbox[ 'category' ])) {
  103 + $category = $this->processCategory($artbox[ 'category' ]);
  104 + }
  105 + $this->processProduct($artbox, $category);
  106 + }
  107 + if (count($products) < $limit) {
  108 + $end = true;
  109 + } else {
  110 + $end = false;
  111 + }
  112 + $percent = round(( $from + $limit ) / $count * 100, 2);
  113 + if ($percent > 100) {
  114 + $percent = 100;
  115 + }
  116 + return [
  117 + 'from' => $from,
  118 + 'limit' => $limit,
  119 + 'end' => $end,
  120 + 'percent' => $percent,
  121 + ];
  122 + }
  123 +
  124 + public function actionProcessOrder($from = 0, $limit = 100)
  125 + {
  126 + /**
  127 + * @var \artbox\odoo\components\OdooMapper $mapper
  128 + * @var \artbox\odoo\components\Connection $odoo
  129 + */
  130 + $mapper = \Yii::$app->get('odooMapper');
  131 + $odoo = \Yii::$app->get('odoo');
  132 + $count = $odoo->createCommand('product.template', 'search_count', [ [] ])
  133 + ->execute();
  134 + $response = \Yii::$app->response;
  135 + $response->format = $response::FORMAT_JSON;
  136 + $orders = ( new Query() )->from('sale.order')
  137 + ->offset(intval($from))
  138 + ->limit(intval($limit))
  139 + ->all();
  140 + foreach ($orders as $order) {
  141 + $artbox = array_merge(
  142 + $mapper->toArtbox(
  143 + ( new Query() )->from('res.partner')
  144 + ->where(
  145 + [
  146 + 'id',
  147 + '=',
  148 + $order[ 'partner_id' ][ 0 ],
  149 + ]
  150 + )
  151 + ->one()
  152 + ),
  153 + $mapper->toArtbox($order)
  154 + );
  155 + $products = ( new Query() )->from('sale.order.line')
  156 + ->where(
  157 + [
  158 + 'order_id',
  159 + '=',
  160 + $artbox[ 'remote_id' ],
  161 + ]
  162 + )
  163 + ->all();
  164 + $artboxProducts = [];
  165 + foreach ($products as $product) {
  166 + $artboxProducts[] = $mapper->toArtbox($product);
  167 + }
  168 + $this->processOrder($artbox, $artboxProducts);
  169 + }
  170 + if (count($orders) < $limit) {
  171 + $end = true;
  172 + } else {
  173 + $end = false;
  174 + }
  175 + $percent = round(( $from + $limit ) / $count * 100, 2);
  176 + if ($percent > 100) {
  177 + $percent = 100;
  178 + }
  179 + return [
  180 + 'from' => $from,
  181 + 'limit' => $limit,
  182 + 'end' => $end,
  183 + 'percent' => $percent,
  184 + ];
  185 + }
  186 +
  187 + public function actionOrders()
  188 + {
  189 + $dataProvider = new ActiveDataProvider(
  190 + [
  191 + 'query' => Order::find()
  192 + ->with('odooToOrder'),
  193 + ]
  194 + );
  195 + return $this->render(
  196 + 'orders',
  197 + [
  198 + 'dataProvider' => $dataProvider,
  199 + ]
  200 + );
  201 + }
  202 +
  203 + public function actionSendOrders($ids)
  204 + {
  205 + $response = \Yii::$app->response;
  206 + $response->format = $response::FORMAT_JSON;
  207 + $ids = Json::decode($ids);
  208 + $counter = 0;
  209 + if (!empty($ids)) {
  210 + /**
  211 + * @var \artbox\odoo\components\Connection $odoo
  212 + */
  213 + $helper = new OdooHelper();
  214 + /**
  215 + * @var Order[] $orders
  216 + */
  217 + $orders = Order::find()
  218 + ->where([ 'id' => $ids ])
  219 + ->all();
  220 + foreach ($orders as $order) {
  221 + $helper->ensureSaleOrderLine($order);
  222 + $counter++;
  223 + }
  224 + }
  225 + return $counter;
  226 + }
  227 +
  228 + protected function processCategory(array $category): Category
  229 + {
  230 + $categoryId = $category[ 0 ];
  231 + $categoryName = $category[ 1 ];
  232 + /**
  233 + * @var Category $category
  234 + */
  235 + $category = Category::find()
  236 + ->innerJoin('odoo_to_category', 'category.id = odoo_to_category.category_id')
  237 + ->where(
  238 + [
  239 + 'remote_id' => $categoryId,
  240 + ]
  241 + )
  242 + ->with('categoryLangs')
  243 + ->one();
  244 + if ($category) {
  245 + foreach ($category->categoryLangs as $categoryLang) {
  246 + $categoryLang->title = $categoryName;
  247 + $categoryLang->save();
  248 + }
  249 + return $category;
  250 + } else {
  251 + $category = new Category(
  252 + [
  253 + 'status' => true,
  254 + ]
  255 + );
  256 + $category->generateLangs();
  257 + foreach ($category->modelLangs as $categoryLang) {
  258 + $categoryLang->title = $categoryName;
  259 + }
  260 + $category->saveWithLangs();
  261 + $categoryLink = new OdooToCategory(
  262 + [
  263 + 'category_id' => $category->id,
  264 + 'remote_id' => $categoryId,
  265 + ]
  266 + );
  267 + $categoryLink->save();
  268 + return $category;
  269 + }
  270 + }
  271 +
  272 + /**
  273 + * @param array $artbox
  274 + * @param Category|null $category
  275 + *
  276 + * @return \artbox\catalog\models\Product
  277 + */
  278 + protected function processProduct(array $artbox, $category = null): Product
  279 + {
  280 + $productModel = Product::find()
  281 + ->innerJoin('odoo_to_product', 'product.id = odoo_to_product.product_id')
  282 + ->where(
  283 + [
  284 + 'remote_id' => $artbox[ 'remote_id' ],
  285 + ]
  286 + )
  287 + ->with('productLangs')
  288 + ->with('variant')
  289 + ->one();
  290 + if ($productModel) {
  291 + foreach ($productModel->productLangs as $productLang) {
  292 + $productLang->load($artbox, '');
  293 + $productLang->save();
  294 + }
  295 + $productModel->load($artbox, '');
  296 + $productModel->save();
  297 + $productModel->variant->load($artbox, '');
  298 + $productModel->variant->save();
  299 + } else {
  300 + $productModel = new Product();
  301 + $productModel->detachBehavior('defaultVariant');
  302 + $productModel->load($artbox, '');
  303 + $productModel->generateLangs();
  304 + foreach ($productModel->modelLangs as $productLang) {
  305 + $productLang->load($artbox, '');
  306 + }
  307 + $productModel->saveWithLangs();
  308 + $productLink = new OdooToProduct(
  309 + [
  310 + 'product_id' => $productModel->id,
  311 + 'remote_id' => $artbox[ 'remote_id' ],
  312 + ]
  313 + );
  314 + $productLink->save();
  315 + $variant = new Variant();
  316 + $variant->product_id = $productModel->id;
  317 + $variant->load($artbox, '');
  318 + $lang = $productModel->modelLangs[ array_keys($productModel->modelLangs)[ 0 ] ];
  319 + if (empty($variant->sku)) {
  320 + $variant->sku = $lang->title;
  321 + }
  322 + $variant->generateLangs();
  323 + foreach ($variant->modelLangs as $variantLang) {
  324 + $variantLang->title = $lang->title;
  325 + }
  326 + $variant->saveWithLangs();
  327 + }
  328 + if (!empty($category)) {
  329 + new ProductToCategory(
  330 + [
  331 + 'category_id' => $category->id,
  332 + 'product_id' => $productModel->id,
  333 + ]
  334 + );
  335 + }
  336 + return $productModel;
  337 + }
  338 +
  339 + /**
  340 + * @param array $artbox
  341 + * @param array $products
  342 + *
  343 + * @return \artbox\catalog\models\Product|\artbox\odoo\models\Order
  344 + * @internal param \artbox\catalog\models\Category|null $category
  345 + */
  346 + protected function processOrder(array $artbox, array $products): Order
  347 + {
  348 + $orderModel = Order::find()
  349 + ->innerJoin('odoo_to_order', '[[order]].id = odoo_to_order.order_id')
  350 + ->where(
  351 + [
  352 + 'remote_id' => $artbox[ 'remote_id' ],
  353 + ]
  354 + )
  355 + ->one();
  356 + if ($orderModel) {
  357 + $orderModel->load($artbox, '');
  358 + $orderModel->save();
  359 + } else {
  360 + $orderModel = new Order(
  361 + [
  362 + 'label_id' => 1,
  363 + 'payment_id' => 1,
  364 + 'delivery_id' => 1,
  365 + ]
  366 + );
  367 + $orderModel->load($artbox, '');
  368 + $orderModel->save();
  369 + ( new OdooToOrder(
  370 + [
  371 + 'order_id' => $orderModel->id,
  372 + 'remote_id' => $artbox[ 'remote_id' ],
  373 + ]
  374 + ) )->save();
  375 + }
  376 + $orderModel->unlinkAll('orderProducts', true);
  377 + foreach ($products as $product) {
  378 + $productModel = $this->processProduct($product);
  379 + $orderProduct = new OrderProduct(
  380 + [
  381 + 'order_id' => $orderModel->id,
  382 + 'sku' => $productModel->variant->sku,
  383 + ]
  384 + );
  385 + $orderProduct->load($product, '');
  386 + $orderProduct->variant_id = $productModel->variant->id;
  387 + $orderProduct->save();
  388 + }
  389 + return $orderModel;
  390 + }
  391 + }
0 392 \ No newline at end of file
... ...
instruction 0 → 100644
  1 +Установка:
  2 +1. Добавляем в компоненты:
  3 +'odooMapper' => [
  4 + 'class' => OdooMapper::className(),
  5 + 'map' => [
  6 + 'id' => 'remote_id',
  7 + 'active' => 'status',
  8 + 'create_date' => [
  9 + 'attribute' => 'created_at',
  10 + 'artbox' => function ($field) {
  11 + return strtotime($field);
  12 + },
  13 + 'odoo' => function ($field) {
  14 + return date('Y-m-d H:i:s', $field);
  15 + },
  16 + ],
  17 + '__last_update' => [
  18 + 'attribute' => 'updated_at',
  19 + 'artbox' => function ($field) {
  20 + return strtotime($field);
  21 + },
  22 + 'odoo' => function ($field) {
  23 + return date('Y-m-d H:i:s', $field);
  24 + },
  25 + ],
  26 + 'name' => 'title',
  27 + 'default_code' => 'sku',
  28 + 'list_price' => 'price',
  29 + 'product_id' => [
  30 + 'attribute' => 'variant_id',
  31 + 'artbox' => function ($field) {
  32 + return $field[ 0 ];
  33 + },
  34 + 'odoo' => function ($field) {
  35 + return [ $field ];
  36 + },
  37 + ],
  38 + 'price_unit' => 'price',
  39 + 'product_uom_qty' => 'count',
  40 + 'categ_id' => 'category',
  41 + 'contact_address' => [
  42 + 'attribute' => 'address',
  43 + 'artbox' => function ($field) {
  44 + return strval($field);
  45 + },
  46 + 'odoo' => function ($field) {
  47 + return boolval($field);
  48 + },
  49 + ],
  50 + 'phone' => [
  51 + 'attribute' => 'phone',
  52 + 'artbox' => function ($field) {
  53 + return strval($field);
  54 + },
  55 + 'odoo' => function ($field) {
  56 + return boolval($field);
  57 + },
  58 + ],
  59 + 'email' => [
  60 + 'attribute' => 'email',
  61 + 'artbox' => function ($field) {
  62 + return strval($field);
  63 + },
  64 + 'odoo' => function ($field) {
  65 + return boolval($field);
  66 + },
  67 + ],
  68 + 'city' => [
  69 + 'attribute' => 'city',
  70 + 'artbox' => function ($field) {
  71 + return strval($field);
  72 + },
  73 + 'odoo' => function ($field) {
  74 + return boolval($field);
  75 + },
  76 + ],
  77 + 'comment' => [
  78 + 'attribute' => 'comment',
  79 + 'artbox' => function ($field) {
  80 + return strval($field);
  81 + },
  82 + 'odoo' => function ($field) {
  83 + return boolval($field);
  84 + },
  85 + ],
  86 + ],
  87 +],
  88 +2. Добавляем в компоненты доступы:
  89 +'odoo' => [
  90 + 'class' => Connection::className(),
  91 + 'url' => 'https://demo.cloudbank.biz',
  92 + 'db' => 'odoo',
  93 + 'username' => 'admin',
  94 + 'password' => 'htcge,kbrf',
  95 +]
0 96 \ No newline at end of file
... ...
messages/en/odoo.php 0 → 100755
  1 +<?php
  2 + return [];
0 3 \ No newline at end of file
... ...
messages/ru/odoo.php 0 → 100755
  1 +<?php
  2 + return [
  3 + 'Count' => 'Количество',
  4 + 'Products' => 'Товары',
  5 + 'Products loaded from Odoo' => 'Товары загруженные с Odoo',
  6 + 'Orders' => 'Заказы',
  7 + 'Orders loaded from Odoo' => 'Заказы загруженные с Odoo',
  8 + 'Categories' => 'Категории',
  9 + 'Categories loaded from Odoo' => 'Категории загруженные с Odoo',
  10 + ];
0 11 \ No newline at end of file
... ...
migrations/m170615_080920_odoo_remote_tables.php 0 → 100644
  1 +<?php
  2 +
  3 + use yii\db\Migration;
  4 +
  5 + class m170615_080920_odoo_remote_tables extends Migration
  6 + {
  7 + public function safeUp()
  8 + {
  9 + $this->createTable(
  10 + 'odoo_to_product',
  11 + [
  12 + 'product_id' => $this->integer()
  13 + ->notNull(),
  14 + 'remote_id' => $this->integer()
  15 + ->notNull(),
  16 + ]
  17 + );
  18 +
  19 + $this->createIndex(
  20 + 'odoo_to_product_uindex',
  21 + 'odoo_to_product',
  22 + [
  23 + 'product_id',
  24 + 'remote_id',
  25 + ],
  26 + true
  27 + );
  28 +
  29 + $this->addForeignKey(
  30 + 'odoo_to_product_product_fkey',
  31 + 'odoo_to_product',
  32 + 'product_id',
  33 + 'product',
  34 + 'id',
  35 + 'CASCADE',
  36 + 'CASCADE'
  37 + );
  38 +
  39 + $this->createTable(
  40 + 'odoo_to_order',
  41 + [
  42 + 'order_id' => $this->integer()
  43 + ->notNull(),
  44 + 'remote_id' => $this->integer()
  45 + ->notNull(),
  46 + ]
  47 + );
  48 +
  49 + $this->createIndex(
  50 + 'odoo_to_order_uindex',
  51 + 'odoo_to_order',
  52 + [
  53 + 'order_id',
  54 + 'remote_id',
  55 + ],
  56 + true
  57 + );
  58 +
  59 + $this->addForeignKey(
  60 + 'odoo_to_order_order_fkey',
  61 + 'odoo_to_order',
  62 + 'order_id',
  63 + 'order',
  64 + 'id',
  65 + 'CASCADE',
  66 + 'CASCADE'
  67 + );
  68 +
  69 + $this->createTable(
  70 + 'odoo_to_category',
  71 + [
  72 + 'category_id' => $this->integer()
  73 + ->notNull(),
  74 + 'remote_id' => $this->integer()
  75 + ->notNull(),
  76 + ]
  77 + );
  78 +
  79 + $this->createIndex(
  80 + 'odoo_to_category_uindex',
  81 + 'odoo_to_category',
  82 + [
  83 + 'category_id',
  84 + 'remote_id',
  85 + ],
  86 + true
  87 + );
  88 +
  89 + $this->addForeignKey(
  90 + 'odoo_to_category_category_fkey',
  91 + 'odoo_to_category',
  92 + 'category_id',
  93 + 'category',
  94 + 'id',
  95 + 'CASCADE',
  96 + 'CASCADE'
  97 + );
  98 +
  99 + $this->createTable(
  100 + 'odoo_to_customer',
  101 + [
  102 + 'customer_id' => $this->integer()
  103 + ->notNull(),
  104 + 'remote_id' => $this->integer()
  105 + ->notNull(),
  106 + ]
  107 + );
  108 +
  109 + $this->createIndex(
  110 + 'odoo_to_customer_uindex',
  111 + 'odoo_to_customer',
  112 + [
  113 + 'customer_id',
  114 + 'remote_id',
  115 + ],
  116 + true
  117 + );
  118 +
  119 + $this->addForeignKey(
  120 + 'odoo_to_customer_customer_fkey',
  121 + 'odoo_to_customer',
  122 + 'customer_id',
  123 + 'customer',
  124 + 'id',
  125 + 'CASCADE',
  126 + 'CASCADE'
  127 + );
  128 + }
  129 +
  130 + public function safeDown()
  131 + {
  132 + $this->dropTable('odoo_to_order');
  133 + $this->dropTable('odoo_to_product');
  134 + $this->dropTable('odoo_to_category');
  135 + $this->dropTable('odoo_to_customer');
  136 + }
  137 + }
... ...
models/OdooToCategory.php 0 → 100644
  1 +<?php
  2 +
  3 + namespace artbox\odoo\models;
  4 +
  5 + use artbox\catalog\models\Category;
  6 + use Yii;
  7 + use yii\db\ActiveRecord;
  8 +
  9 + /**
  10 + * This is the model class for table "odoo_to_category".
  11 + *
  12 + * @property integer $category_id
  13 + * @property integer $remote_id
  14 + * @property Category $category
  15 + */
  16 + class OdooToCategory extends ActiveRecord
  17 + {
  18 + /**
  19 + * @inheritdoc
  20 + */
  21 + public static function primaryKey()
  22 + {
  23 + return [
  24 + 'category_id',
  25 + 'remote_id',
  26 + ];
  27 + }
  28 +
  29 + /**
  30 + * @inheritdoc
  31 + */
  32 + public static function tableName()
  33 + {
  34 + return 'odoo_to_category';
  35 + }
  36 +
  37 + /**
  38 + * @inheritdoc
  39 + */
  40 + public function rules()
  41 + {
  42 + return [
  43 + [
  44 + [
  45 + 'category_id',
  46 + 'remote_id',
  47 + ],
  48 + 'required',
  49 + ],
  50 + [
  51 + [
  52 + 'category_id',
  53 + 'remote_id',
  54 + ],
  55 + 'integer',
  56 + ],
  57 + [
  58 + [
  59 + 'category_id',
  60 + 'remote_id',
  61 + ],
  62 + 'unique',
  63 + 'targetAttribute' => [
  64 + 'category_id',
  65 + 'remote_id',
  66 + ],
  67 + 'message' => 'The combination of Category ID and Remote ID has already been taken.',
  68 + ],
  69 + [
  70 + [ 'category_id' ],
  71 + 'exist',
  72 + 'skipOnError' => true,
  73 + 'targetClass' => Category::className(),
  74 + 'targetAttribute' => [ 'category_id' => 'id' ],
  75 + ],
  76 + ];
  77 + }
  78 +
  79 + /**
  80 + * @inheritdoc
  81 + */
  82 + public function attributeLabels()
  83 + {
  84 + return [
  85 + 'category_id' => Yii::t('odoo', 'Category ID'),
  86 + 'remote_id' => Yii::t('odoo', 'Remote ID'),
  87 + ];
  88 + }
  89 +
  90 + /**
  91 + * @return \yii\db\ActiveQuery
  92 + */
  93 + public function getCategory()
  94 + {
  95 + return $this->hasOne(Category::className(), [ 'id' => 'category_id' ]);
  96 + }
  97 + }
... ...
models/OdooToCustomer.php 0 → 100644
  1 +<?php
  2 +
  3 + namespace common\models;
  4 +
  5 + use artbox\order\models\Customer;
  6 + use Yii;
  7 + use yii\db\ActiveRecord;
  8 +
  9 + /**
  10 + * This is the model class for table "odoo_to_customer".
  11 + *
  12 + * @property integer $customer_id
  13 + * @property integer $remote_id
  14 + * @property Customer $customer
  15 + */
  16 + class OdooToCustomer extends ActiveRecord
  17 + {
  18 +
  19 + /**
  20 + * @inheritdoc
  21 + */
  22 + public static function primaryKey()
  23 + {
  24 + return [
  25 + 'customer_id',
  26 + 'remote_id',
  27 + ];
  28 + }
  29 +
  30 + /**
  31 + * @inheritdoc
  32 + */
  33 + public static function tableName()
  34 + {
  35 + return 'odoo_to_customer';
  36 + }
  37 +
  38 + /**
  39 + * @inheritdoc
  40 + */
  41 + public function rules()
  42 + {
  43 + return [
  44 + [
  45 + [
  46 + 'customer_id',
  47 + 'remote_id',
  48 + ],
  49 + 'required',
  50 + ],
  51 + [
  52 + [
  53 + 'customer_id',
  54 + 'remote_id',
  55 + ],
  56 + 'integer',
  57 + ],
  58 + [
  59 + [
  60 + 'customer_id',
  61 + 'remote_id',
  62 + ],
  63 + 'unique',
  64 + 'targetAttribute' => [
  65 + 'customer_id',
  66 + 'remote_id',
  67 + ],
  68 + 'message' => 'The combination of Customer ID and Remote ID has already been taken.',
  69 + ],
  70 + [
  71 + [ 'customer_id' ],
  72 + 'exist',
  73 + 'skipOnError' => true,
  74 + 'targetClass' => Customer::className(),
  75 + 'targetAttribute' => [ 'customer_id' => 'id' ],
  76 + ],
  77 + ];
  78 + }
  79 +
  80 + /**
  81 + * @inheritdoc
  82 + */
  83 + public function attributeLabels()
  84 + {
  85 + return [
  86 + 'customer_id' => Yii::t('odoo', 'Customer ID'),
  87 + 'remote_id' => Yii::t('odoo', 'Remote ID'),
  88 + ];
  89 + }
  90 +
  91 + /**
  92 + * @return \yii\db\ActiveQuery
  93 + */
  94 + public function getCustomer()
  95 + {
  96 + return $this->hasOne(Customer::className(), [ 'id' => 'customer_id' ]);
  97 + }
  98 + }
... ...
models/OdooToOrder.php 0 → 100644
  1 +<?php
  2 +
  3 + namespace artbox\odoo\models;
  4 +
  5 + use Yii;
  6 + use yii\db\ActiveRecord;
  7 +
  8 + /**
  9 + * This is the model class for table "odoo_to_order".
  10 + *
  11 + * @property integer $order_id
  12 + * @property integer $remote_id
  13 + * @property Order $order
  14 + */
  15 + class OdooToOrder extends ActiveRecord
  16 + {
  17 + /**
  18 + * @inheritdoc
  19 + */
  20 + public static function primaryKey()
  21 + {
  22 + return [
  23 + 'order_id',
  24 + 'remote_id',
  25 + ];
  26 + }
  27 +
  28 + /**
  29 + * @inheritdoc
  30 + */
  31 + public static function tableName()
  32 + {
  33 + return 'odoo_to_order';
  34 + }
  35 +
  36 + /**
  37 + * @inheritdoc
  38 + */
  39 + public function rules()
  40 + {
  41 + return [
  42 + [
  43 + [
  44 + 'order_id',
  45 + 'remote_id',
  46 + ],
  47 + 'required',
  48 + ],
  49 + [
  50 + [
  51 + 'order_id',
  52 + 'remote_id',
  53 + ],
  54 + 'integer',
  55 + ],
  56 + [
  57 + [
  58 + 'order_id',
  59 + 'remote_id',
  60 + ],
  61 + 'unique',
  62 + 'targetAttribute' => [
  63 + 'order_id',
  64 + 'remote_id',
  65 + ],
  66 + 'message' => 'The combination of Order ID and Remote ID has already been taken.',
  67 + ],
  68 + [
  69 + [ 'order_id' ],
  70 + 'exist',
  71 + 'skipOnError' => true,
  72 + 'targetClass' => Order::className(),
  73 + 'targetAttribute' => [ 'order_id' => 'id' ],
  74 + ],
  75 + ];
  76 + }
  77 +
  78 + /**
  79 + * @inheritdoc
  80 + */
  81 + public function attributeLabels()
  82 + {
  83 + return [
  84 + 'order_id' => Yii::t('odoo', 'Order ID'),
  85 + 'remote_id' => Yii::t('odoo', 'Remote ID'),
  86 + ];
  87 + }
  88 +
  89 + /**
  90 + * @return \yii\db\ActiveQuery
  91 + */
  92 + public function getOrder()
  93 + {
  94 + return $this->hasOne(Order::className(), [ 'id' => 'order_id' ])
  95 + ->inverseOf('odooToOrder');
  96 + }
  97 + }
... ...
models/OdooToProduct.php 0 → 100644
  1 +<?php
  2 +
  3 + namespace artbox\odoo\models;
  4 +
  5 + use artbox\catalog\models\Product;
  6 + use Yii;
  7 + use yii\db\ActiveRecord;
  8 +
  9 + /**
  10 + * This is the model class for table "odoo_to_product".
  11 + *
  12 + * @property integer $product_id
  13 + * @property integer $remote_id
  14 + * @property Product $product
  15 + */
  16 + class OdooToProduct extends ActiveRecord
  17 + {
  18 + /**
  19 + * @inheritdoc
  20 + */
  21 + public static function primaryKey()
  22 + {
  23 + return [
  24 + 'product_id',
  25 + 'remote_id',
  26 + ];
  27 + }
  28 +
  29 + /**
  30 + * @inheritdoc
  31 + */
  32 + public static function tableName()
  33 + {
  34 + return 'odoo_to_product';
  35 + }
  36 +
  37 + /**
  38 + * @inheritdoc
  39 + */
  40 + public function rules()
  41 + {
  42 + return [
  43 + [
  44 + [
  45 + 'product_id',
  46 + 'remote_id',
  47 + ],
  48 + 'required',
  49 + ],
  50 + [
  51 + [
  52 + 'product_id',
  53 + 'remote_id',
  54 + ],
  55 + 'integer',
  56 + ],
  57 + [
  58 + [
  59 + 'product_id',
  60 + 'remote_id',
  61 + ],
  62 + 'unique',
  63 + 'targetAttribute' => [
  64 + 'product_id',
  65 + 'remote_id',
  66 + ],
  67 + 'message' => 'The combination of Product ID and Remote ID has already been taken.',
  68 + ],
  69 + [
  70 + [ 'product_id' ],
  71 + 'exist',
  72 + 'skipOnError' => true,
  73 + 'targetClass' => Product::className(),
  74 + 'targetAttribute' => [ 'product_id' => 'id' ],
  75 + ],
  76 + ];
  77 + }
  78 +
  79 + /**
  80 + * @inheritdoc
  81 + */
  82 + public function attributeLabels()
  83 + {
  84 + return [
  85 + 'product_id' => Yii::t('odoo', 'Product ID'),
  86 + 'remote_id' => Yii::t('odoo', 'Remote ID'),
  87 + ];
  88 + }
  89 +
  90 + /**
  91 + * @return \yii\db\ActiveQuery
  92 + */
  93 + public function getProduct()
  94 + {
  95 + return $this->hasOne(Product::className(), [ 'id' => 'product_id' ]);
  96 + }
  97 + }
... ...
models/Order.php 0 → 100644
  1 +<?php
  2 +
  3 + namespace artbox\odoo\models;
  4 +
  5 + /**
  6 + * Order class to work with Odoo
  7 + *
  8 + * @property \artbox\odoo\models\OdooToOrder $odooToOrder
  9 + */
  10 + class Order extends \artbox\order\models\Order
  11 + {
  12 + /**
  13 + * @return \yii\db\ActiveQuery
  14 + */
  15 + public function getOdooToOrder()
  16 + {
  17 + return $this->hasOne(OdooToOrder::className(), [ 'order_id' => 'id' ])
  18 + ->inverseOf('order');
  19 + }
  20 + }
... ...
views/odoo/import.php 0 → 100644
  1 +<?php
  2 + /**
  3 + * @var \yii\web\View $this
  4 + */
  5 + use artbox\odoo\assets\ArtboxOdooAsset;
  6 + use yii\bootstrap\Html;
  7 + use yii\helpers\Url;
  8 + use yiister\gentelella\widgets\Panel;
  9 +
  10 + ArtboxOdooAsset::register($this);
  11 + $this->title = \Yii::t('odoo', 'Odoo import');
  12 + $this->params[ 'breadcrumbs' ][] = $this->title;
  13 +?>
  14 +<div class="odoo-import">
  15 + <?php
  16 + $xPanel = Panel::begin(
  17 + [
  18 + 'header' => $this->title,
  19 + ]
  20 + );
  21 + ?>
  22 + <div class="col-lg-3 col-md-3 col-sm-6 col-xs-12">
  23 + <?php
  24 + echo Html::a(
  25 + \Yii::t('odoo', 'Import products'),
  26 + '#',
  27 + [
  28 + 'class' => 'odoo-import-product',
  29 + 'data' => [
  30 + 'url' => Url::to([ 'process' ]),
  31 + ],
  32 + ]
  33 + );
  34 + ?>
  35 + </div>
  36 + <div class="col-lg-3 col-md-3 col-sm-6 col-xs-12">
  37 + <?php
  38 + echo Html::a(
  39 + \Yii::t('odoo', 'Import orders'),
  40 + '#',
  41 + [
  42 + 'class' => 'odoo-import-order',
  43 + 'data' => [
  44 + 'url' => Url::to([ 'process-order' ]),
  45 + ],
  46 + ]
  47 + );
  48 + ?>
  49 + </div>
  50 + <div class="clearfix"></div>
  51 + <div id="odoo-progress">
  52 + <div class="progress">
  53 + <div class="progress-bar progress-bar-striped" role="progressbar" aria-valuenow="0" aria-valuemin="0" aria-valuemax="100" style="width: 0"></div>
  54 + </div>
  55 + </div>
  56 + <?php
  57 + $xPanel::end();
  58 + ?>
  59 +</div>
... ...
views/odoo/index.php 0 → 100644
  1 +<?php
  2 + /**
  3 + * @var \yii\web\View $this
  4 + * @var \artbox\odoo\models\OdooToProduct[] $products
  5 + * @var \artbox\odoo\models\OdooToOrder[] $orders
  6 + * @var \artbox\odoo\models\OdooToCategory[] $categories
  7 + */
  8 + use yii\bootstrap\Html;
  9 + use yiister\gentelella\widgets\Panel;
  10 +
  11 + $this->title = \Yii::t('odoo', 'Odoo');
  12 + $this->params[ 'breadcrumbs' ][] = $this->title;
  13 +?>
  14 +<div class="odoo-index">
  15 + <?php
  16 + $xPanel = Panel::begin(
  17 + [
  18 + 'header' => $this->title,
  19 + ]
  20 + );
  21 + ?>
  22 + <div class="col-lg-3 col-md-3 col-sm-6 col-xs-12">
  23 + <div class="tile-stats">
  24 + <div class="icon">
  25 + <div class="fa fa-caret-square-o-right"></div>
  26 + </div>
  27 + <div class="count"><?php echo count($products); ?></div>
  28 + <h3><?php echo \Yii::t('odoo', 'Products'); ?></h3>
  29 + <p><?php echo \Yii::t('odoo', 'Products loaded from Odoo'); ?></p>
  30 + </div>
  31 + </div>
  32 + <div class="col-lg-3 col-md-3 col-sm-6 col-xs-12">
  33 + <div class="tile-stats">
  34 + <div class="icon">
  35 + <div class="fa fa-caret-square-o-right"></div>
  36 + </div>
  37 + <div class="count"><?php echo count($orders); ?></div>
  38 + <h3><?php echo \Yii::t('odoo', 'Orders'); ?></h3>
  39 + <p><?php echo \Yii::t('odoo', 'Orders loaded from Odoo'); ?></p>
  40 + </div>
  41 + </div>
  42 + <div class="col-lg-3 col-md-3 col-sm-6 col-xs-12">
  43 + <div class="tile-stats">
  44 + <div class="icon">
  45 + <div class="fa fa-caret-square-o-right"></div>
  46 + </div>
  47 + <div class="count"><?php echo count($categories); ?></div>
  48 + <h3><?php echo \Yii::t('odoo', 'Categories'); ?></h3>
  49 + <p><?php echo \Yii::t('odoo', 'Categories loaded from Odoo'); ?></p>
  50 + </div>
  51 + </div>
  52 + <div class="clearfix"></div>
  53 + <div class="col-xs-12">
  54 + <div class="list-group">
  55 + <?php
  56 + echo Html::a(
  57 + \Yii::t('odoo', 'Import'),
  58 + [ 'import' ],
  59 + [
  60 + 'class' => 'list-group-item',
  61 + ]
  62 + );
  63 + echo Html::a(
  64 + \Yii::t('odoo', 'Orders'),
  65 + [ 'orders' ],
  66 + [
  67 + 'class' => 'list-group-item',
  68 + ]
  69 + );
  70 + ?>
  71 + </div>
  72 + </div>
  73 + <?php
  74 + $xPanel::end();
  75 + ?>
  76 +</div>
... ...
views/odoo/orders.php 0 → 100644
  1 +<?php
  2 + /**
  3 + * @var \yii\data\ActiveDataProvider $dataProvider
  4 + * @var \yii\web\View $this
  5 + */
  6 +
  7 + use yii\grid\ActionColumn;
  8 + use yii\grid\CheckboxColumn;
  9 + use yii\grid\Column;
  10 + use yii\grid\GridView;
  11 + use yii\helpers\Html;
  12 + use yii\helpers\Json;
  13 + use yii\helpers\Url;
  14 + use yiister\gentelella\widgets\Panel;
  15 +
  16 + $this->title = \Yii::t('odoo', 'Orders');
  17 + $this->params[ 'breadcrumbs' ][] = [
  18 + 'label' => \Yii::t('odoo', 'Odoo'),
  19 + 'url' => [ 'index' ],
  20 + ];
  21 + $this->params[ 'breadcrumbs' ][] = $this->title;
  22 +?>
  23 +<div class="odoo-index">
  24 + <?php
  25 + $xPanel = Panel::begin(
  26 + [
  27 + 'header' => $this->title,
  28 + ]
  29 + );
  30 + echo Html::button(
  31 + \Yii::t('odoo', 'Send'),
  32 + [
  33 + 'class' => 'btn btn-primary pull-right odoo-order-send',
  34 + 'data' => [
  35 + 'conf' => \Yii::t('odoo', 'Are you sure to send checked orders to Odoo?'),
  36 + 'url' => Url::to([ 'send-orders' ]),
  37 + ],
  38 + ]
  39 + );
  40 + echo GridView::widget(
  41 + [
  42 + 'id' => 'odoo-order-grid',
  43 + 'dataProvider' => $dataProvider,
  44 + 'columns' => [
  45 + [
  46 + 'class' => CheckboxColumn::className(),
  47 + ],
  48 + [
  49 + 'attribute' => 'id',
  50 + 'value' => function ($model) {
  51 + /**
  52 + * @var \artbox\odoo\models\Order $model
  53 + */
  54 + return Html::a(
  55 + $model->id,
  56 + [
  57 + '/order/view',
  58 + 'id' => $model->id,
  59 + ]
  60 + );
  61 + },
  62 + 'format' => 'html',
  63 + ],
  64 + [
  65 + 'class' => Column::className(),
  66 + 'content' => function ($model) {
  67 + /**
  68 + * @var \artbox\odoo\models\Order $model
  69 + */
  70 + return Html::tag(
  71 + 'i',
  72 + '',
  73 + [
  74 + 'class' => $model->odooToOrder ? 'glyphicon glyphicon-ok' : 'glyphicon glyphicon-remove',
  75 + ]
  76 + );
  77 + },
  78 + 'header' => \Yii::t('odoo', 'Status'),
  79 + ],
  80 + [
  81 + 'class' => ActionColumn::className(),
  82 + 'template' => '{send-order}',
  83 + 'buttons' => [
  84 + 'send-order' => function ($url, $model) {
  85 + /**
  86 + * @var \artbox\odoo\models\Order $model
  87 + */
  88 + return Html::a(
  89 + Html::tag(
  90 + 'i',
  91 + '',
  92 + [
  93 + 'class' => 'glyphicon glyphicon-upload',
  94 + ]
  95 + ),
  96 + [
  97 + 'send-orders',
  98 + 'ids' => Json::encode([ $model->id ]),
  99 + ],
  100 + [
  101 + 'class' => 'odoo-order-send-one',
  102 + 'data' => [
  103 + 'conf' => \Yii::t('odoo', 'Are you sure to send order to Odoo?'),
  104 + ],
  105 + ]
  106 + );
  107 + },
  108 + ],
  109 + ],
  110 + ],
  111 + ]
  112 + );
  113 + $xPanel::end();
  114 + ?>
  115 +</div>
  116 +<?php
  117 + $js = <<<JS
  118 + $('.odoo-order-send')
  119 + .on('click', function() {
  120 + if (confirm($(this)
  121 + .data('conf'))) {
  122 + var selector = '#odoo-order-grid';
  123 + var selected = $(selector)
  124 + .yiiGridView('getSelectedRows');
  125 + $(selector)
  126 + .prepend('<div class="loader-wrapper"></div>');
  127 + if(selected.length) {
  128 + $.post($(this).data('url') + '?ids=' + JSON.stringify(selected), function(data) {
  129 + console.log(data);
  130 + $.pjax.reload(selector, {
  131 + timeout: 5000,
  132 + fragment: selector
  133 + });
  134 + });
  135 + }
  136 + }
  137 + });
  138 + $('.odoo-order-send-one').on('click', function(e) {
  139 + e.preventDefault();
  140 + if(confirm($(this).data('conf'))) {
  141 + var selector = '#odoo-order-grid';
  142 + $(selector)
  143 + .prepend('<div class="loader-wrapper"></div>');
  144 + $.post($(this).attr('href'), function(data) {
  145 + console.log(data);
  146 + $.pjax.reload(selector, {
  147 + timeout: 5000,
  148 + fragment: selector
  149 + });
  150 + });
  151 + }
  152 + });
  153 +JS;
  154 + $this->registerJs($js);
  155 +?>
... ...
web/js/script.js 0 → 100644
  1 +$(function() {
  2 + $(document)
  3 + .on('click', '.odoo-import-product, .odoo-import-order', function(e) {
  4 + e.preventDefault();
  5 + var url = $(this)
  6 + .data('url');
  7 + postData(url, 0, 100);
  8 + });
  9 +});
  10 +function postData(url, from, limit) {
  11 + $.post(url + '?from=' + from + '&limit=' + limit, function(data) {
  12 + show(data.percent);
  13 + if (!data.end) {
  14 + postData(url, from + limit, limit);
  15 + }
  16 + });
  17 +}
  18 +function show(percent) {
  19 + var odoo_progress = $('#odoo-progress');
  20 + var progress_bar = odoo_progress.find('.progress-bar')
  21 + .first();
  22 + progress_bar.width(percent + '%');
  23 + progress_bar.attr('aria-valuenow', percent);
  24 + if (percent < 100) {
  25 + progress_bar.addClass('active');
  26 + } else {
  27 + progress_bar.removeClass('active');
  28 + }
  29 +}
0 30 \ No newline at end of file
... ...