setChannel(SentryLogger::CHANNEL_SYNC); class AbraBase { use DatabaseCommunication; protected $client; protected $batch_size = 1000; protected $separator = '~|~'; protected $settings = []; protected $abraNull = '0000000000'; protected $paginateByCode = false; public static $ABRA_UIDS; public static $PRODUCT_FIELDS; public static $SECTION_FIELDS; public static $PRODUCER_FIELDS; /** @var LoggerInterface */ protected $logger; protected ContextManager $contextManager; public function __construct($settings = null) { if (is_null($settings)) { $settings = Config::get()['Modules']['abra']; } $this->settings = array_merge($this->settings, $settings); // In development/review use test Abra server if (isDevelopment()) { if (isset($this->settings['test_server'])) { $this->settings['server'] = $this->settings['test_server']; } } ini_set('default_socket_timeout', 300); $this->client = $this->getSoapClient(); $this->logger = ServiceContainer::getService('logger'); $this->contextManager = ServiceContainer::getService(ContextManager::class); } public function getSoapClient($server = 'server') { // Allow incorrect SSL certificate $context = stream_context_create([ 'ssl' => [ // set some SSL/TLS specific options 'verify_peer' => false, 'verify_peer_name' => false, 'allow_self_signed' => true, ], ]); $options = [ 'trace' => 1, 'features' => SOAP_SINGLE_ELEMENT_ARRAYS, 'connection_timeout' => 300, 'location' => str_replace('/wsdl/', '/soap/', $this->settings[$server]), 'stream_context' => $context, ]; if (!empty($this->settings['basic_auth_login'])) { $options['login'] = $this->settings['basic_auth_login']; } if (!empty($this->settings['basic_auth_password'])) { $options['password'] = $this->settings['basic_auth_password']; } return new SoapClient($this->settings[$server], $options); } public function prettyXml($string) { if (!$string) { return; } $domxml = new DOMDocument('1.0'); $domxml->preserveWhiteSpace = false; $domxml->formatOutput = true; /* @var $xml SimpleXMLElement */ $domxml->loadXML($string); return $domxml->saveXML(); } public function printTrace() { $requestHeaders = $this->client->__getLastRequestHeaders(); $request = $this->prettyXml($this->client->__getLastRequest()); $responseHeaders = $this->client->__getLastResponseHeaders(); $response = $this->prettyXml($this->client->__getLastResponse()); echo ''.nl2br(htmlspecialchars($requestHeaders, true)).''; echo highlight_string($request, true)."
\n"; echo ''.nl2br(htmlspecialchars($responseHeaders, true)).''."
\n"; echo highlight_string($response, true)."
\n"; } public function logTrace($type) { $requestHeaders = $this->client->__getLastRequestHeaders(); $request = $this->prettyXml($this->client->__getLastRequest()); $responseHeaders = $this->client->__getLastResponseHeaders(); $response = $this->prettyXml($this->client->__getLastResponse()); $this->log(["Trace send {$type}", $requestHeaders, $request]); $this->log(["Trace receive {$type}", $responseHeaders, $response]); } public static $GET_ALL_TYPES = [ '42P1E2VUANDL342Q01C0CX3FCC' => [ 'batch' => 100, ], 'C3V5QDVZ5BDL342M01C0CX3FCC' => [ 'batch' => 100, ], 'GAWVAN4GFNDL342T01C0CX3FCC' => [ 'batch' => 100, ], 'GHYLVQXQ3FE13DQC01C0CX3F40' => [ 'batch' => 100, ], '05CPMINJW3DL342X01C0CX3FCC' => [ 'batch' => 100, ], 'G2WVAN4GFNDL342T01C0CX3FCC' => [ 'batch' => 100, ], '4K3EXM5PQBCL35CH000ILPWJF4' => [ 'batch' => 100, ], 'G5YLVQXQ3FE13DQC01C0CX3F40' => [ 'batch' => 100, ], ]; protected $type; public function getAllData($type = null, $params = []) { if (!empty($params['transaction'])) { sqlStartTransaction(); } while (ob_get_level()) { ob_end_flush(); } $defaults = [ 'start' => $this->abraNull, 'batch' => null, 'count' => null, ]; $types = static::$GET_ALL_TYPES; if ($type) { if (substr($type, -1) == '+' || substr($type, -1) == ' ') { $type = substr($type, 0, -1); $types = array_slice($types, array_search($type, array_keys($types))); } else { $types = [$type => ($types[$type] ?? [])]; } } foreach ($types as $type => $options) { $this->type = $type; $options = array_merge($defaults, $options, $params); echo "
Starting {$type}
"; flush(); if (empty(static::$ABRA_UIDS[$type]) || static::$ABRA_UIDS[$type] == 'ignore') { echo "ignored.\n"; continue; } $index = $options['start']; $options['start'] = $this->abraNull; $old_index = null; $retry_count = 3; $total_count = $options['count']; $count = 0; do { $old_index = $index; echo "
Fetching {$index} ..."; logError(__FILE__, __LINE__, "Fetching {$type} : {$index} ..."); flush(); $timestamp = getScriptTime(); try { $items = $this->getOneDataBlock($type, $index, $options['batch']); } catch (Exception $e) { $this->printTrace(); if (--$retry_count > 0) { $old_index = null; continue; } throw $e; } if (!empty($params['test'])) { $this->printTrace(); } $delay = number_format(getScriptTime() - $timestamp, 2); $timestamp = getScriptTime(); echo "Took {$delay} s.
Processing ...."; flush(); foreach ($items as $change) { $index = $this->processChange(join($this->separator, [0, substr($change, 4)]), $index); } $count += count($items); $delay = number_format(getScriptTime() - $timestamp, 2); $timestamp = getScriptTime(); echo "Took {$delay} s.
Updating delayed ...."; try { $this->updateScheduledProducts(); } catch (Exception $e) { var_dump(['updateScheduledProducts exception', $e->getMessage()]); } $delay = number_format(getScriptTime() - $timestamp, 2); echo "Took {$delay} s"; $retry_count = 3; } while ($old_index != $index && (is_null($total_count) || $total_count > $count)); echo "
Ended {$type}
"; flush(); } $this->finalizeSync(); } public function getOneDataBlock($type, $index, $batch) { // detect CLSID if (strlen($type) == 26) { return $this->client->weGetAll($type, $index, $batch); } $selection = null; $filter = null; if (isset($_GET['filter']) && !empty($_GET['filter'])) { $filter = explode(',', $_GET['filter']); $filter = array_map(function ($value) { return "'".$value."'"; }, $filter); $filter = join(',', $filter); $selection = 'SELECTION'; } $this->paginateByCode = true; return $this->client->weGetObjectType($type, $index, $batch, $selection, $filter); } public function updateScheduledProducts() { } public function sync() { $this->syncOrders(); $this->syncProducts(); $this->finalizeSync(); } // Utility functions public function getLastChangeID() { return intval(sqlFetchAssoc($this->selectSQL('abra_settings', ['name' => 'last_change'], ['value']))['value'] ?? 0); } public function updateLastChangeID($newID) { $this->updateSQL('abra_settings', ['value' => $newID], ['name' => 'last_change']); } public function updateOrderStatuses() { } public function authorizeOrders($spec, $message) { $orders = sqlQueryBuilder()->select('o.id')->from('orders', 'o') ->join('o', 'abra_orders', 'ao', 'ao.id_order = o.id') ->join('o', 'delivery_type', 'dt', 'dt.id = o.id_delivery') // ->andWhere(\Query\Operator::not(\Query\Operator::findInSet(['D'], 'o.flags'))) ->andWhere($spec)->execute(); foreach ($orders as $order) { $this->changeOrderStatus($order['id'], 1, $message); } } public function changeOrderStatus($id_order, $status, $comment, $service_note = null, $emailType = null) { try { $order = new Order(); $order->createFromDB($id_order); $this->log(['Change order status', 'id' => $id_order, 'code' => $order->order_no, 'status' => $status, 'comment' => $comment]); $ret = $this->client->__call('weSetReceivedOrderSimpleUpdate', [$this->getWebSource($order), $this->getOrderNumber($order), ["Status~|~{$status}"]]); $this->printTrace(); if ($service_note) { $service_note = $this->prepareFieldLengthForOrder($this->prepareTextForOrder($service_note), 200); $this->client->__call('weSetReceivedOrderSimpleUpdate', [$this->getWebSource($order), $this->getOrderNumber($order), ["CustomerServiceNote~|~{$service_note}"]]); } if ($ret[0] != 'True') { throw new Exception("{$ret[1]} - {$ret[2]}"); } $order->changeStatus($status, $comment, isset($emailType), null, $emailType); } catch (Exception $e) { $this->log(['Change order status', 'id' => $id_order, 'exception' => $e->getMessage()]); $this->logException($e); } } protected $mappingCache = ['id_abra' => null, 'id' => null]; public function getMappingFromAbra($type, $code) { if ($this->mappingCache['id_abra'] == $type.$code) { return $this->mappingCache['id']; } $id = returnSQLResult("SELECT id_{$type} FROM abra_{$type}s WHERE id_abra=:code", ['code' => $code]); if ($id) { $this->mappingCache = ['id_abra' => $type.$code, 'id' => $id]; } return $id; } public function getMappingFromAbraMultiple($type, $code) { $ids = sqlFetchAll(sqlQuery("SELECT id_{$type} as id FROM abra_{$type}s WHERE id_abra=:code ORDER BY id_{$type}", ['code' => $code]), ['id' => 'id']); return array_values($ids); } public function setMappingFromAbra($type, $id_kupshop, $code_abra) { sqlQuery("INSERT INTO abra_{$type}s (id_abra, id_{$type}) VALUES (:code, :id)", ['code' => $code_abra, 'id' => $id_kupshop]); } public function getMappingToAbra($type, $id) { $table = "abra_{$type}s"; if ($type == 'delivery') { $table = 'abra_deliveries'; } return returnSQLResult("SELECT id_abra FROM {$table} WHERE id_{$type}=:id", ['id' => $id]); } public function preparePriceToAbra($price) { return str_replace('.', ',', $price); } public function preparePriceFromAbra($price) { $value = str_replace(',', '.', $price); if ($value[0] === '.') { $value = '0'.$value; } if ($value[-1] === '.') { $value .= '0'; } return $value; } public function parseBool($value) { return $value == 'Ano'; } public function parseNullable($value) { if ($value == '' || ((string) $value) === $this->abraNull) { return null; } return $value; } public function updateProductFlag($data, $enable) { if ($enable) { sqlQuery('UPDATE products SET campaign = ADD_TO_SET(:flag, campaign) WHERE id=:id_product', $data); } else { sqlQuery('UPDATE products SET campaign = REMOVE_FROM_SET(:flag, campaign) WHERE id=:id_product', $data); } } public function updateVariationFlag($data, $enable) { if ($enable) { sqlQuery('UPDATE products_variations SET campaign = ADD_TO_SET(:flag, campaign) WHERE id=:id_variation', $data); } else { sqlQuery('UPDATE products_variations SET campaign = REMOVE_FROM_SET(:flag, campaign) WHERE id=:id_variation', $data); } } // Find functions protected $listProducer; public function findProducer($name) { if ($name == '') { return null; } if (is_null($this->listProducer)) { $this->listProducer = sqlFetchAll(sqlQuery('SELECT id, LOWER(name) AS name FROM producers'), ['id' => 'name']); } if (($index = array_search(mb_strtolower($name), $this->listProducer)) !== false) { return $index; } $max_position = returnSQLResult('SELECT MAX(position) FROM producers'); $this->insertSQL('producers', ['name' => $name, 'position' => $max_position + 1]); $index = sqlInsertId(); $this->listProducer[$index] = mb_strtolower($name); return $index; } protected $listVAT; protected $listVATDefault = 1; public function findVAT($percent) { if (is_null($this->listVAT)) { foreach (sqlQuery('SELECT id, vat, is_default FROM vats') as $row) { $this->listVAT[$row['id']] = $row['vat']; if ($row['is_default'] == 'Y') { $this->listVATDefault = $row['id']; } } } if ($index = array_search($percent, $this->listVAT)) { return $index; } $this->insertSQL('vats', ['descr' => "Daň {$percent}%", 'vat' => $percent]); $index = sqlInsertId(); $this->listVAT[$index] = $percent; return $index; } protected $listParameter = []; public function findParameterValue($parameter_id, $value) { if (!isset($this->listParameter[$parameter_id])) { $this->listParameter[$parameter_id] = sqlFetchAll( sqlQuery('SELECT id, LOWER(value) AS value FROM parameters_list WHERE id_parameter=:id_parameter', ['id_parameter' => $parameter_id]), ['id' => 'value']); } $params = &$this->listParameter[$parameter_id]; if ($index = array_search(mb_strtolower($value), $params)) { return $index; } try { $this->insertSQL('parameters_list', ['value' => $value, 'id_parameter' => $parameter_id]); $index = sqlInsertId(); } catch (Exception $e) { var_dump($e->getMessage()); $index = sqlFetchAssoc($this->selectSQL('parameters_list', ['value' => $value, 'id_parameter' => $parameter_id], ['id']))['id']; } $params[$index] = mb_strtolower($value); return $index; } public function findCategory(&$categories, &$parentCat = null) { // Get next category part $categoryName = array_shift($categories); if ($categoryName === null) { // Terminate recurse return $parentCat['id']; } if (empty($categoryName)) { return $this->findCategory($categories, $parentCat); } // Get parent ID and menu $parentId = null; if ($parentCat) { $menu = &$parentCat['submenu']; $parentId = $parentCat['id']; } else { $menu = ServiceContainer::getService(SectionTree::class)->getTree(); } // Find matching section $found = null; if (!empty($menu)) { foreach ($menu as &$category) { if (mb_strtolower($category['title']) == mb_strtolower($categoryName)) { $found = &$category; break; } } } // If not found, create one if (empty($found)) { $this->insertSQL('sections', ['name' => $categoryName, 'lead_figure' => 'N', 'behaviour' => 2]); $catId = sqlInsertId(); $this->insertSQL('sections_relation', ['id_section' => $catId, 'id_topsection' => $parentId, 'position' => 99]); $found = [ 'id' => $catId, 'title' => mb_strtolower($categoryName), ]; $menu[] = &$found; } return $this->findCategory($categories, $found); } // Sync functions public function getOrdersToSync(): QueryBuilder { return sqlQueryBuilder() ->select('o.id') ->from('orders', 'o') ->leftJoin('o', 'abra_orders', 'ao', 'o.id=ao.id_order') ->where('ao.id_abra IS NULL AND o.date_created < DATE_SUB(NOW(), INTERVAL 30 SECOND)'); } public function syncOrders() { $orders = $this->getOrdersToSync()->setMaxResults(10)->execute(); foreach ($orders as $order) { $this->syncOrder($order['id']); } $this->updateOrderStatuses(); } public function syncProducts() { $last_change = $this->getLastChangeID(); echo "
last_change start: {$last_change}
"; $startTime = getScriptTime(); do { $this->logger->notice("Abra fetching: start:{$last_change}, size:{$this->batch_size}"); $changes = $this->client->weGetChanges($last_change, $this->batch_size); $this->logger->notice('Abra fetched from:'.reset($changes).' to:'.end($changes)); if (getVal('test')) { $this->printTrace(); } if (empty($changes)) { return false; } if ($last_change > 0) { $first_change = explode($this->separator, reset($changes))[0]; if ($first_change != $last_change + 1) { $this->log(['Přeskočená synchronizace', $first_change, $last_change]); } } // logError(__FILE__, __LINE__, 'Abra sync: '.print_r($changes, true)); foreach ($changes as $change) { $this->logger->notice("Abra sync: {$change}", ['change' => $change]); $this->processChange($change); } $last_change = explode($this->separator, end($changes))[0]; echo "
last_change stop: {$last_change}
"; $this->logger->notice('Abra update last change: '.$last_change); $this->updateLastChangeID($last_change); // Clear changes if (!isLocalDevelopment()) { var_dump($this->client->weClearChanges($last_change)); } } while (count($changes) == $this->batch_size && (getScriptTime() - $startTime) < (3 * 60)); return true; } public function processChange($change, $index = null) { $change = explode($this->separator, $change); $type = static::$ABRA_UIDS[$change[1]]; $method = "sync_{$type}"; $last_change = $change[0]; if (!$last_change) { if ($this->paginateByCode) { $last_change = $change[3]; } else { // Index by position static $last_id = null; $last_change = $index; if ($last_id != $change[3]) { $last_change++; } $last_id = $change[3]; } } if ($change[2] !== 'LastID') { $this->$method($change[3], $change[2], $change[4], $change[5] ?? null); } return $last_change; } public function sync_product($code, $field, $value, $value2 = null) { $id = $this->getMappingFromAbra('product', $code); if (!$id) { // Create Product $vat_id = null; getVat($vat_id); $this->insertSQL('products', ['code' => $code, 'vat' => $vat_id, 'figure' => 'N']); $id = sqlInsertId(); $this->setMappingFromAbra('product', $id, $code); } $column = static::$PRODUCT_FIELDS[$field]; if (!$column) { throw new Exception("Nedefinovaná položka synchronizace: {$field}"); } $this->sync_products_switch($code, $column, $value, $value2, $id); } public function sync_products_switch($code, $column, $value, $value2, $id) { switch ($column) { case 'id_category': $id_section = $this->getMappingFromAbra('section', $value); if (!$id_section) { if ($value > 0) { echo 'Unknown section ID: '.$value; } // throw new Exception('Unknown section ID: '.$value); return; } $this->deleteSQL('products_in_sections', ['id_product' => $id]); sqlQuery('REPLACE INTO '.getTableName('products_in_sections').' (id_product, id_section) VALUES (:id_product, :id_section)', ['id_product' => $id, 'id_section' => $id_section]); return; case 'unit': $data = ['id_product' => $id, 'id_parameter' => 89]; $this->deleteSQL('parameters_products', $data); if ($value != 'ks') { $data['value_list'] = $this->findParameterValue(89, $value); $this->insertSQL('parameters_products', $data); } return; case 'price': $value = $this->preparePriceFromAbra($value) / ((100 + getVat()) / 100); break; case 'price_common': case 'priceWithoutVat': $value = $this->preparePriceFromAbra($value); break; case 'figure': $value = ($this->parseBool($value) || $value > 0) ? 'Y' : 'O'; break; case 'weight': case 'width': case 'height': case 'depth': $value = $this->preparePriceFromAbra($value) ?: null; break; case 'main_unit_code': $id_parameter_value = returnSQLResult('SELECT id FROM parameters_list WHERE id_parameter=89 AND value=:value', ['value' => $value]); if (!$value) { $this->insertSQL('parameters_list', ['value' => $value, 'id_parameter' => 89]); $id_parameter_value = sqlInsertId(); } $this->deleteSQL('parameters_products', ['id_product' => $id, 'id_parameter' => 89]); $this->insertSQL('parameters_products', ['id_product' => $id, 'id_parameter' => 89, 'value_list' => $id_parameter_value]); return; case 'code': try { $this->updateSQL('products', [$column => $value], ['id' => $id]); } catch (UniqueConstraintViolationException $e) { echo '
ERROR duplicate code: '.$value.'
'; } return; case 'vat': $value = $this->findVAT($value); break; case 'suppliers_code': // Parametr "Objednávkové číslo" $this->updateParameterValue($id, $value, 131); return; case 'delete': $this->updateSQL('products', ['figure' => 'O'], ['id' => $id]); return; case 'measure_unit': $unitId = $this->selectSQL('products_units', ['short_name_admin' => $value], ['id'])->fetchOne(); $value = $unitId ?: null; break; case 'measure_quantity': $value = $this->preparePriceFromAbra($value); break; case 'ignore': return; } $this->updateSQL('products', [$column => $value], ['id' => $id]); } public function sync_section($code, $field, $value) { $id = $this->getMappingFromAbra('section', $code); if (!$id) { // Create Section $this->insertSQL('sections', ['name' => $value]); $id = sqlInsertId(); $this->insertSQL('sections_relation', ['id_section' => $id, 'id_topsection' => null, 'position' => 999]); $this->setMappingFromAbra('section', $id, $code); } $column = static::$SECTION_FIELDS[$field]; switch ($column) { case 'parent_id': $parent_id = $this->getMappingFromAbra('section', $value); if (!$parent_id) { if ($value > 0) { echo 'Unknown section ID: '.$value; } // throw new Exception('Unknown section ID: '.$value); break; } sqlQuery('DELETE FROM sections_relation WHERE id_section=:id', ['id' => $id]); sqlQuery('INSERT INTO sections_relation (id_section, id_topsection) VALUES (:id, :parent_id)', ['id' => $id, 'parent_id' => $parent_id, 'position' => 999]); MenuSectionTree::invalidateCache(); break; case 'position': sqlQuery('UPDATE '.getTableName('sections_relation').' SET position=:position WHERE id_section=:id', ['id' => $id, 'position' => $value]); break; case 'description': $section = $this->selectSQL('sections', ['id' => $id])->fetch(); if (isset($section['id_block'])) { $block = sqlQueryBuilder()->select('id')->from('blocks') ->where('id_root=:id_block AND id_parent=:id_block') ->setParameter('id_block', $section['id_block']) ->orderBy('position', 'ASC')->setMaxResults(1) ->execute()->fetch(); $this->updateSQL('blocks', ['content' => $value], ['id' => $block['id']]); } else { $this->insertSQL('blocks', []); $rootBlockID = sqlInsertId(); $this->insertSQL('blocks', [ 'id_root' => $rootBlockID, 'id_parent' => $rootBlockID, 'position' => 1, 'name' => $section['name'], 'content' => $value, ]); $this->updateSQL('sections', ['id_block' => $rootBlockID], ['id' => $id]); } break; case 'figure': $value = $this->parseBool($value); $value = $value ? 'Y' : 'N'; $this->updateSQL('sections', [$column => $value], ['id' => $id]); break; case 'delete': $this->deleteSQL('sections', ['id' => $id]); return; case 'name_first': // Aktualizovat název sekce jen když je název roven Abra ID - aktualizovat jen poprvé, pak už ne $this->updateSQL('sections', ['name' => $value], ['id' => $id, 'name' => $code]); return; case 'ignore': break; default: $this->updateSQL('sections', [$column => $value], ['id' => $id]); } } public function sync_producer($code, $field, $value) { $id = $this->getMappingFromAbra('producer', $code); if (!$id) { // Create Product $vat_id = null; getVat($vat_id); $this->insertSQL('producers', ['name' => $value]); $id = sqlInsertId(); $this->setMappingFromAbra('producer', $id, $code); } $column = static::$PRODUCER_FIELDS[$field]; switch ($column) { case 'ignore': break; case 'position': sqlQuery('UPDATE producers SET position=:position WHERE id=:id', ['id' => $id, 'position' => $value]); break; default: $this->updateSQL('producers', [$column => $value], ['id' => $id]); } } public function sync_ignore($code, $field, $value) { // Ignore } public function syncOrder($id) { // sqlQuery('SELECT COUNT(*) FROM abra_orders FOR UPDATE'); // Protected by flock now $sync_order = $this->getOrder($id); $code = getVal('DocumentNumber', $sync_order); echo "

synchronizuji objednavku {$code}

"; try { $ret = $this->client->__call('weSetReceivedOrder', $sync_order); } catch (Exception $e) { $this->printTrace(); throw $e; } $this->printTrace(); $this->logTrace("order {$id} - {$code}"); if ($ret[0] == 'True') { $this->setMappingFromAbra('order', $id, $ret[2]); $order = new Order(); $order->createFromDB($id); $order->logHistory(translate('abra_order_send', 'abra')); } else { $error = $ret[1]; if ($error == 'Objednávka již v ABŘE existuje' || $error == 'ERROR:WEB009') { $this->setMappingFromAbra('order', $id, $ret[2]); } else { throw new RuntimeException("Synchronizace objednávky {$code} selhala: ".join(', ', $ret)); } } } public function prepareTextForOrder($text) { return str_replace(["\r\n", "\n"], '', $text); } public function prepareFieldLengthForOrder($text, $size) { if (is_null($text)) { return $text; } $text = mb_substr($text, 0, $size); if ($text === false) { return ''; } return $text; } public function getOrder($id) { $order = new Order(); $order->createFromDB($id, true, true, true); return $this->contextManager->activateOrder($order, function () use ($order) { $delivery = $order->getDeliveryType($order->getDeliveryId()); $sync_order = [ // Header 'roID_WPJ' => $order->id, 'roDocumentNumber' => $order->order_no, 'roWEBnote' => str_replace(["\r\n", "\n"], '', $order->note_user), 'roCurrency' => findModule(Modules::CURRENCIES) ? $order->currency : 'CZK', 'roPaymentCode' => $this->getMappingToAbra('payment', $delivery->id_payment), 'roTransportCode' => $this->getMappingToAbra('delivery', $delivery->id_delivery), // Delivery address 'adID_WPJ' => $order->id_user, 'adNameCompany' => $order->delivery_firm, 'adFirstName' => $order->delivery_name, 'adLastName' => $order->delivery_surname, 'adOrgIdentNumber' => null, 'adVATIdentNumber' => null, 'adStreet' => $order->delivery_street, 'adCity' => $order->delivery_city, 'adPostCode' => $order->delivery_zip, 'adCountryCode' => $order->delivery_country, 'adPhoneNumber1' => $order->delivery_phone, 'adPhoneNumber2' => null, 'adEmail' => null, // Invoicing address 'afNameCompany' => $order->invoice_firm, 'afFirstName' => $order->invoice_name, 'afLastName' => $order->invoice_surname, 'afOrgIdentNumber' => $order->invoice_ico, 'afVATIdentNumber' => $order->invoice_dic, 'afStreet' => $order->invoice_street, 'afCity' => $order->invoice_city, 'afPostCode' => $order->invoice_zip, 'afCountryCode' => $order->invoice_country, 'afPhoneNumber1' => $order->invoice_phone, 'afPhoneNumber2' => null, 'afEmail' => $order->invoice_email, 'roRows' => $this->getOrderRows($order), ]; return $sync_order; }); } /** * @param $order Order * * @return array */ public function getOrderRows($order) { $order->fetchItems(); $rows = []; $rows_text = []; foreach ($order->items as $item) { if ($item['id_product']) { $code = $this->getMappingToAbra('product', $item['id_product']); } else { $code = ''; } if ($code) { $type = 3; $name = ''; $price = $this->preparePriceToAbra($item['piece_price']['value_without_vat_no_rounding']); $quantity = $item['pieces']; } else { $type = 1; $name = $item['descr']; $price = $this->preparePriceToAbra($item['total_price']['value_without_vat_no_rounding']); $quantity = 1; } // "3~|~002002~|~1~|~168,5~|~", $row = join($this->separator, [ $type, $code, $quantity, $price, $name, ]); if ($code) { $rows[] = $row; } else { $rows_text[] = $row; } } return array_merge($rows, $rows_text); } public function finalizeSync() { } public function getClient(): SoapClient { return $this->client; } public function convertUnits($column, $unit, int $value): int { if ($column == 'weight') { switch ($unit) { case 'g': $value /= 1000; break; case 't': $value *= 1000; } } else { switch ($unit) { case 'm': $value *= 100; break; case 'dm': $value *= 10; break; case 'mm': $value /= 10; break; } } return $value; } protected function createAbraDataObject($name, $value, $type) { $data = [ 'FieldValueAsString' => '', 'FieldValueAsFloat' => '', 'FieldValueAsDateTime' => '', 'FieldValueAsBoolean' => '', 'FieldValueAsInteger' => '', 'FieldDataKind' => 'adkData', 'BusinessObject' => '', 'Collection' => '', ]; return array_merge($data, [ 'FieldName' => $name, 'FieldValueAs'.$type => $value, 'FieldDataType' => 'dt'.$type, ]); } public function log($data) { // Do not log in development if (isLocalDevelopment()) { return; } /** @var Symfony\Bridge\Monolog\Logger $logger */ $logger = ServiceContainer::getService('logger'); $logger->notice('Abra Log: '.print_r($data, true)); } public function logToActivityLog($message, $data = []) { addActivityLog(ActivityLog::SEVERITY_ERROR, ActivityLog::TYPE_SYNC, 'Abra: '.$message, $data); } public function logException(Exception $exception) { $sentry = getRaven(); $sentry->captureException($exception); } public function getWebSource(Order $order) { return $this->settings['web']; } public function getOrderNumber(Order $order) { return $order->order_no; } public function getInvoice(Order $order) { $result = $this->client->weGetInvoice($this->getWebSource($order), $this->getOrderNumber($order)); if ($result[0] === 'True') { return base64_decode($result[2]); } else { if (isDevelopment()) { $this->printTrace(); } throw new NotFoundHttpException('Order does not have invoice: '.print_r($result, true)); } } public function loopSychronization($timeout, $endOnEmptyQueue = false) { $startTime = getScriptTime(); $flock_file = fopen('data/tmp/abra_sync.lock', 'wb'); while (!flock($flock_file, LOCK_EX | LOCK_NB)) { if ((getScriptTime() - $startTime) > $timeout) { exit('Cannot get lock'); } var_dump('Waiting for lock'); flush(); sleep(1); } // Sync only for 10 seconds to loop more often $this->syncTimeout = 10; do { try { var_dump('Loop....'); flush(); $tmp = (getScriptTime() - $startTime); // For Sentry $this->syncOrders(); $moreMessages = $this->syncProducts(); // Update scheduled products only if no more messages waiting // Try to deplete queue and send pending orders ASAP if ($moreMessages !== true) { $this->updateScheduledProducts(); if ($endOnEmptyQueue) { break; } if ((getScriptTime() - $startTime) < $timeout) { sleep(max(1, min(30, $timeout - (getScriptTime() - $startTime) + 1))); } } } catch (Exception $e) { exception_handler($e); sleep(max(1, min(60, $timeout - (getScriptTime() - $startTime) + 1))); } } while ((getScriptTime() - $startTime) < $timeout); $this->finalizeSync(); flock($flock_file, LOCK_UN); fclose($flock_file); } public function updateParameterValue($productId, $value, $id_parameter, $column = 'value_char') { $data = ['id_product' => $productId, 'id_parameter' => $id_parameter]; $this->deleteSQL('parameters_products', $data); if ($value) { $data[$column] = $value; $this->insertSQL('parameters_products', $data); } } public function process_message_abra($body) { $this->processChange($body->data); } } if (empty($subclass)) { class Abra extends AbraBase { } }