diff --git a/README.md b/README.md index cca69b7..ed922b7 100644 --- a/README.md +++ b/README.md @@ -1 +1,21 @@ -Repository with Teclib modules for Dolibarr ERP CRM +Use it at you onwn risk +Open-DSI does not provide support for the use, installation or bug fixing for this software. For this you have to acquire a time credit on our online shop. +https://www.open-dsi.fr/boutique/ +You can also consult our company for any installation, configuration or development services. +Forker Repository of Teclib modules for Dolibarr ERP CRM +Only use to maintain ecommerceng module for Woocommerce +Before any use, please refer to the wiki to check the available functionnality +https://wiki.dolibarr.org/index.php/Module_woocommerce_EN +and read the document in +htdocs/ecommerceng/doc/ + +Utilisation à vos risques et périls. +Open-DSI ne fournit pas de support à l’utilisation, à l'installation ni de correction de bugs pour ce logiciel. Pour cela vous devrez faire l’acquisition d’un crédit temps sur notre boutique en ligne. +https://www.open-dsi.fr/boutique/ +Vous pouvez également nous consulter pour toute prestation d’installation, configuration ou développement. +Dépôt forké depuis celui de Teclib +Utilisé pour maintenir et publier le module ecommerceng pour WooCommerce +Avant toute utilisation, merci de prendre connaissance du wiki pour vérifier si le module répond en l'état à vos besoins +https://wiki.dolibarr.org/index.php/Module_woocommerce_EN +et lire la documentaion disponible dans +htdocs/ecommerceng/doc/ diff --git a/htdocs/ecommerceng/ChangeLog.md b/htdocs/ecommerceng/ChangeLog.md index cadd7d9..888c674 100644 --- a/htdocs/ecommerceng/ChangeLog.md +++ b/htdocs/ecommerceng/ChangeLog.md @@ -1,5 +1,60 @@ # ChangeLog +## 4.0.3.0 + +- Enregistre la société pour les clients anonymes pour chaque site et non dans l'option ECOMMERCENG_USE_THIS_THIRDPARTY_FOR_NONLOGGED_CUSTOMER +- Correction de la quantités des éléments lié au site pour la fiche du site (en non pour tous les sites) +- Ajout d'une option ECOMMERCENG_NO_COUNT_UPDATE pour ne pas récupérer les quantités à mettre à jour et afficher tous les boutons de synchronisation sur la fiche du site. +- Correction de la recherche de correspondance lors de la synchro des catégories +- Correction de la récupération des produits et de ses variantes +- Correction de la recherche de la ref du produits dans Dolibarr lors de la synchro +- Ne re-télécharge pas les informations du client pour recupérer les adresses du clients lors de la synchro des sociétés +- Lors de la synchro des contacts, rempli le nom si vide par "\[nom non renseigné\]" ou "Pas de nom/prénom renseigné" en fonction des cas +- Similaire pour les nom des tiers +- Ajout de l'option ECOMMERCENG_WOOCOMMERCE_ORDER_PROCESSING_STATUS_TO_DRAFT pour ne forcer les commandes woocommerce au statut "En cours" redescende au statut "Brouillon" dans Dolibarr +- Les commandes woocommerce au statut "Remboursées" redescende au statut "Annulée" dans Dolibarr +- Definie la description d'une ligne de produit vide par "L'api n'a pas pu récupérer la description du produit" +- Cherche la societe du contact d'une commande par son adresse mail si fourni pour les commandes anonymes +- Correction de la recherche d'un contact par ses informations +- Correction d'une partie de la gestion des erreurs +- Les logs bas niveaux woocommerce sont au niveau DEBUG et plus au niveau INFO +- Corrections mineures + +## 4.0.2.0 + +- D'un champ complémetaire oublié pour la fiche d'un produit d'un site woocommerce +- Affiche du lien de test sur les parametres du site que si l'adresse du site est renseigné +- Ajout de la description de l'erreur lors du fonctionnement de l'OAuth 2 +- Corrections mineures + +## 4.0.1.0 + +- Ajout OAuth2 pour Wordpress +- Mise en commun du type de prix renvoyé par la boutique (HT / TTC) +- Lien de test en fonction du type du site +- Creation de champ complémentaire à l'ajout d'un site Woocommerce +- Ajout de la gestion des classes de TVA Woocommerce pour les produits + Dictionnaire +- Ajout des fonctions de recherche, insert et update à la classe eCommerceDict +- Ajout et modification de traductions +- Corrections du decodage de la reponse de l'API Woocommerce +- Ajout de la synchronisation du produit lors de l'ajout de la catégorie mère "E-Commerce" (Trigger) +- Ne synchronise pas le produit lorsque l'on envèle la catégorie mère "E-Commerce" (Trigger) +- Creation du produit sur le site depuis dolibarr (Trigger) +- Correction du statut réel de la commande lors de la synchro de Dolibarr vers E-Commerce (Trigger) +- Ajout test connection a l'appel de la fonction connect de la classe remote access de Woocommerce +- Correction de la gestion des dates lors des fonctions ToUpdate de Woocommerce +- Modification gestion des tiers (avec recherche doublon par email, nom, ...) et distinctions entreprise/particulier +- Modification gestion des contacts/adresses avec recherche doublon +- Modification gestion des catégories avec recherche doublon +- Modification de mise a jour du prix du produit +- Ajout de gestion des extrafields sur les produits et commandes +- Ajout de la synchro methode de paiment sur la commande dans la synchro E-Commerce vers Dolibarr +- Ajout ECOMMERCENG_WOOCOMMERCE_FORCE_ORDER_STATUS_TO_DRAFT pour forcer le statut de la commande en brouillon lors de la synchro Woocommerce vers Dolibarr- Ajout de la possibilité d'ignorer les commandes anonyme avec la variable ECOMMERCENG_PASS_ORDER_FOR_NONLOGGED_CUSTOMER +- Ajout synchronisation des images avec E-commerce (possibilité de l'activer avec la variable ECOMMERCENG_ENABLE_SYNCHRO_IMAGES) (Necessite paramétrage OAuth2 pour l'envoi des images vers Woocommerce) +- Ajout envoie PDF facture / expedition à la génération du PDF sur la commande Woocommerce via Wordpress (Necessite paramétrage OAuth2) (l'activer avec la variable ECOMMERCENG_ENABLE_SEND_FILE_TO_ORDER) +- Ajout synchro des catégories de Woocommerce vers Dolibarr +- Corrections diverses + ## 3.9.1.0 - Add option ECOMMERCENG_THIRDPARTY_UNIQUE_ON to search existing thirdparties from email instead of name. diff --git a/htdocs/ecommerceng/admin/class/data/eCommerceDict.class.php b/htdocs/ecommerceng/admin/class/data/eCommerceDict.class.php index 611300a..7d98c8a 100644 --- a/htdocs/ecommerceng/admin/class/data/eCommerceDict.class.php +++ b/htdocs/ecommerceng/admin/class/data/eCommerceDict.class.php @@ -94,6 +94,141 @@ public function getAll() return $lines; } + /** + * Get all lines from database match with keys (array(field=>array(value, type))) + * @param array $keys Keys for the search array(field_name=>array('value'=>value, 'type'=>type) (type (optionnel: string, like, date) + * @return array + */ + public function search($keys) + { + $lines = array(); + + $sql = "SELECT * FROM `".$this->table."`"; + if (is_array($keys) && count($keys) > 0) { + $fields = array(); + foreach ($keys as $field => $value) { + switch ($value['type']) { + case 'string': + $key = "= '".$this->db->escape($value['value'])."'"; + break; + case 'like': + $key = "LIKE '".$this->db->escape($value['value'])."'"; + break; + case 'date': + $key = "= '".$this->db->idate($value['value'])."'"; + break; + default: + $key = '= '.$this->db->escape($value['value']); + break; + } + $fields[] = $field.' '.$key; + } + $sql .= " WHERE ".implode(' AND ', $fields); + } + + $result = $this->db->query($sql); + if ($result) { + while ($obj = $this->db->fetch_array($result)) { + $lines[] = $obj; + } + } + + return $lines; + } + + /** + * Update all lines from database match with keys + * @param array $values Values for the search array(field_name=>array('value'=>value, 'type'=>type) (type (optionnel: string, date) + * @param array $keys Keys for the search array(field_name=>array('value'=>value, 'type'=>type) (type (optionnel: string, like, date) + * @return boolean + */ + public function update($values, $keys) + { + $sql = "UPDATE `".$this->table."` SET "; + if (is_array($values) && count($values) > 0) { + $fields = array(); + foreach ($values as $field => $value) { + switch ($value['type']) { + case 'string': + $key = "= '" . $this->db->escape($value['value']) . "'"; + break; + case 'date': + $key = "= '" . $this->db->idate($value['value']) . "'"; + break; + default: + $key = '= ' . $this->db->escape($value['value']); + break; + } + $fields[] = $field . ' ' . $key; + } + $sql .= implode(' AND ', $fields); + } + if (is_array($keys) && count($keys) > 0) { + $fields = array(); + foreach ($keys as $field => $value) { + switch ($value['type']) { + case 'string': + $key = "= '" . $this->db->escape($value['value']) . "'"; + break; + case 'like': + $key = "LIKE '" . $this->db->escape($value['value']) . "'"; + break; + case 'date': + $key = "= '" . $this->db->idate($value['value']) . "'"; + break; + default: + $key = '= ' . $this->db->escape($value['value']); + break; + } + $fields[] = $field . ' ' . $key; + } + $sql .= " WHERE " . implode(' AND ', $fields); + } + + $result = $this->db->query($sql); + if ($result) { + return true; + } + + return false; + } + + /** + * Insert line to database + * @param array $fields Fields for insert + * @param array $values Values for the insert array(field_name=>array('value'=>value, 'type'=>type) (type (optionnel: string, date) + * @return boolean + */ + public function insert($fields, $values) + { + $values_list = array(); + if (is_array($values) && count($values) > 0) { + foreach ($fields as $field) { + if (isset($values[$field])) { + switch ($values[$field]['type']) { + case 'string': + $values_list[] = "'" . $this->db->escape($values[$field]['value']) . "'"; + break; + case 'date': + $values_list[] = "'" . $this->db->idate($values[$field]['value']) . "'"; + break; + default: + $values_list[] = $this->db->escape($values[$field]['value']); + break; + } + } + } + } + + $sql = "INSERT INTO `".$this->table."` (".implode(', ', $fields).") VALUES(".implode(', ', $values_list).")"; + $result = $this->db->query($sql); + if ($result) { + return true; + } + + return false; + } + /** * Get the value of ECOMMERCE_COMPANY_ANONYMOUS from db * @return int > 0 if OK, 0 if KO diff --git a/htdocs/ecommerceng/admin/eCommerceSetup.php b/htdocs/ecommerceng/admin/eCommerceSetup.php old mode 100755 new mode 100644 index 82383c4..21595a5 --- a/htdocs/ecommerceng/admin/eCommerceSetup.php +++ b/htdocs/ecommerceng/admin/eCommerceSetup.php @@ -37,14 +37,18 @@ require_once(DOL_DOCUMENT_ROOT . '/categories/class/categorie.class.php'); require_once(DOL_DOCUMENT_ROOT . '/core/lib/admin.lib.php'); +require_once DOL_DOCUMENT_ROOT.'/includes/OAuth/bootstrap.php'; dol_include_once('/ecommerceng/class/data/eCommerceSite.class.php'); dol_include_once('/ecommerceng/admin/class/gui/eCommerceMenu.class.php'); dol_include_once('/ecommerceng/lib/eCommerce.lib.php'); +use OAuth\Common\Storage\DoliStorage; $langs->load('admin'); $langs->load('companies'); +$langs->load("oauth"); $langs->load('ecommerce@ecommerceng'); +$langs->load('woocommerce@ecommerceng'); $siteId = null; $errors = array(); @@ -53,6 +57,11 @@ if (!$user->admin || !$user->rights->ecommerceng->site) accessforbidden(); +$error = GETPOST('error', 'alpha'); +if (!empty($error)) { + setEventMessage($error, 'errors'); +} + //DATABASE ACCESS $siteDb = new eCommerceSite($db); @@ -118,15 +127,16 @@ $siteDb->filter_value = $_POST['ecommerce_filter_value']; $siteDb->fk_cat_societe = $_POST['ecommerce_fk_cat_societe']; $siteDb->fk_cat_product = $_POST['ecommerce_fk_cat_product']; + $siteDb->fk_anonymous_thirdparty = $_POST['ecommerce_fk_anonymous_thirdparty']>0?$_POST['ecommerce_fk_anonymous_thirdparty']:null; $siteDb->fk_warehouse = $_POST['ecommerce_fk_warehouse']; $siteDb->stock_sync_direction = $_POST['ecommerce_stock_sync_direction']; $siteDb->last_update = $_POST['ecommerce_last_update']; //$siteDb->timeout = $_POST['ecommerce_timeout']; $siteDb->magento_use_special_price = ($_POST['ecommerce_magento_use_special_price'] ? 1 : 0); - $siteDb->magento_price_type = $_POST['ecommerce_magento_price_type']; + $siteDb->ecommerce_price_type = $_POST['ecommerce_price_type']; - // TODO Save this into table of ecommerce_site, field fk_thirdparty instead of global var. - dolibarr_set_const($db, 'ECOMMERCENG_USE_THIS_THIRDPARTY_FOR_NONLOGGED_CUSTOMER', GETPOST('ECOMMERCENG_USE_THIS_THIRDPARTY_FOR_NONLOGGED_CUSTOMER','int')); + $siteDb->oauth_id = $_POST['ecommerce_oauth_id']; + $siteDb->oauth_secret = $_POST['ecommerce_oauth_secret']; $result = 0; if (intval($_POST['ecommerce_id'])) @@ -138,12 +148,102 @@ $result = $siteDb->create($user); } + $error = ''; + if ($result > 0) { + if ($siteDb->type == 2) { // Woocommerce + $result = ecommerceng_add_extrafields($db, $langs, [ + [ + 'attrname' => "ecommerceng_wc_status_{$siteDb->id}_{$conf->entity}", + 'label' => $langs->trans('ECommercengWoocommerceStatus', $siteDb->name), + 'type' => 'select', + 'pos' => 1, + 'size' => '', + 'elementtype' => 'product', + 'unique' => 0, + 'required' => 0, + 'default_value' => '', + 'param' => array('options' => array( + "draft" => $langs->trans('ECommercengWoocommerceStatusDraft', $siteDb->name), + "pending" => $langs->trans('ECommercengWoocommerceStatusPending', $siteDb->name), + "private" => $langs->trans('ECommercengWoocommerceStatusPrivate', $siteDb->name), + "publish" => $langs->trans('ECommercengWoocommerceStatusPublish', $siteDb->name), + )), + 'alwayseditable' => 1, + 'perms' => '', + 'list' => 0, + ],[ + 'attrname' => "ecommerceng_description_{$conf->entity}", + 'label' => 'ECommercengWoocommerceDescription', + 'type' => 'text', + 'pos' => 2, + 'size' => '', + 'elementtype' => 'product', + 'unique' => 0, + 'required' => 0, + 'default_value' => '', + 'param' => '', + 'alwayseditable' => 1, + 'perms' => '', + 'list' => 0, + ], + [ + 'attrname' => "ecommerceng_short_description_{$conf->entity}", + 'label' => 'ECommercengWoocommerceShortDescription', + 'type' => 'text', + 'pos' => 3, + 'size' => '', + 'elementtype' => 'product', + 'unique' => 0, + 'required' => 0, + 'default_value' => '', + 'param' => '', + 'alwayseditable' => 1, + 'perms' => '', + 'list' => 0, + ], + [ + 'attrname' => "ecommerceng_tax_class_{$siteDb->id}_{$conf->entity}", + 'label' => $langs->trans('ECommercengWoocommerceTaxClass', $siteDb->name), + 'type' => 'sellist', + 'pos' => 4, + 'size' => '', + 'elementtype' => 'product', + 'unique' => 0, + 'required' => 0, + 'default_value' => '', + 'param' => array('options' => array("c_ecommerceng_tax_class:label:code::active=1 AND site_id={$siteDb->id} AND entity={$conf->entity}" => null)), + 'alwayseditable' => 1, + 'perms' => '', + 'list' => 0, + ], + [ + 'attrname' => "ecommerceng_online_payment_{$conf->entity}", + 'label' => 'ECommercengWoocommerceOnlinePayment', + 'type' => 'boolean', + 'pos' => 1, + 'size' => '1', + 'elementtype' => 'commande', + 'unique' => 0, + 'required' => 0, + 'default_value' => '', + 'param' => '', + 'alwayseditable' => 1, + 'perms' => '', + 'list' => 0, + ], + ], $error); + } + } + if ($result > 0) { $eCommerceMenu = new eCommerceMenu($db, $siteDb); $eCommerceMenu->updateMenu(); $db->commit(); + if ($siteDb->type == 2) { // Woocommerce + ecommerceng_update_woocommerce_dict_tax_class($db, $siteDb); + } if (!empty($conf->global->PRODUIT_MULTIPRICES) && $siteDb->price_level != $last_price_level) { updatePriceLevel($siteDb); } @@ -152,7 +252,11 @@ } else { $db->rollback(); - setEventMessages($siteDb->error, $siteDb->errors, 'errors'); + if (!empty($error)) { + setEventMessage($error, 'errors'); + } else { + setEventMessages($siteDb->error, $siteDb->errors, 'errors'); + } } } else @@ -178,6 +282,12 @@ unset($_POST); } } +// Update dictionary for tax class of woocommerce +elseif ($_POST['site_form_detail_action'] == 'update_woocommerce_tax_class') { + if (ecommerceng_update_woocommerce_dict_tax_class($db, $siteDb)) { + setEventMessage($langs->trans('ECommercengWoocommerceDictTaxClassUpdated')); + } +} @@ -212,6 +322,17 @@ $ecommerceName = ($_POST['ecommerce_name'] ? $_POST['ecommerce_name'] : $siteDb->name); $ecommerceType = ($_POST['ecommerce_type'] ? $_POST['ecommerce_type'] : intval($siteDb->type)); $ecommerceWebserviceAddress = ($_POST['ecommerce_webservice_address'] ? $_POST['ecommerce_webservice_address'] : $siteDb->webservice_address); +$ecommerceWebserviceAddressTest = ''; +if (!empty($ecommerceWebserviceAddress)) { + switch ($ecommerceType) { + case 1: // Magento + $ecommerceWebserviceAddressTest = $ecommerceWebserviceAddress .(substr($ecommerceWebserviceAddress, -1, 1)!='/'?'/':''). 'api/?wsdl'; + break; + case 2: // Woocommerce + $ecommerceWebserviceAddressTest = $ecommerceWebserviceAddress .(substr($ecommerceWebserviceAddress, -1, 1)!='/'?'/':''). 'wp-json/'; + break; + } +} $ecommerceUserName = ($_POST['ecommerce_user_name'] ? $_POST['ecommerce_user_name'] : $siteDb->user_name); $ecommerceUserPassword = ($_POST['ecommerce_user_password'] ? $_POST['ecommerce_user_password'] : $siteDb->user_password); $ecommercePriceLevel = ($_POST['ecommerce_price_level'] ? $_POST['ecommerce_price_level'] : $siteDb->price_level); @@ -219,15 +340,55 @@ $ecommerceFilterValue = ($_POST['ecommerce_filter_value'] ? $_POST['ecommerce_filter_value'] : $siteDb->filter_value); $ecommerceFkCatSociete = ($_POST['ecommerce_fk_cat_societe'] ? $_POST['ecommerce_fk_cat_societe'] : intval($siteDb->fk_cat_societe)); $ecommerceFkCatProduct = ($_POST['ecommerce_fk_cat_product'] ? $_POST['ecommerce_fk_cat_product'] : intval($siteDb->fk_cat_product)); +$ecommerceFkAnonymousThirdparty = ($_POST['ecommerce_fk_anonymous_thirdparty'] ? $_POST['ecommerce_fk_anonymous_thirdparty'] : intval($siteDb->fk_anonymous_thirdparty)); $ecommerceFkWarehouse = ($_POST['ecommerce_fk_warehouse'] ? $_POST['ecommerce_fk_warehouse'] : intval($siteDb->fk_warehouse)); $ecommerceStockSyncDirection = ($_POST['ecommerce_stock_sync_direction'] ? $_POST['ecommerce_stock_sync_direction'] : $siteDb->stock_sync_direction); $ecommerceMagentoUseSpecialPrice = ($_POST['ecommerce_magento_use_special_price'] ? $_POST['ecommerce_magento_use_special_price'] : intval($siteDb->magento_use_special_price)); -$ecommerceMagentoPriceType = ($_POST['ecommerce_magento_price_type'] ? $_POST['ecommerce_magento_price_type'] : $siteDb->ecommerce_magento_price_type); +$ecommercePriceType = ($_POST['ecommerce_price_type'] ? $_POST['ecommerce_price_type'] : $siteDb->ecommerce_price_type); /*$ecommerceTimeout = 300; if (isset($_POST['ecommerce_timeout'])) $ecommerceTimeout = $_POST['ecommerce_timeout']; elseif (isset($siteDb->timeout)) $ecommerceTimeout = $siteDb->timeout;*/ +$ecommerceOAuth = false; +$ecommerceOAuthGenerateToken = false; +if (!empty($ecommerceId)) { + if ($ecommerceType == 2) { + $ecommerceOAuth = true; + $ecommerceOAuthWordpressOAuthSetupUri = $ecommerceWebserviceAddress . (substr($ecommerceWebserviceAddress, -1, 1) != '/' ? '/' : '') . 'wp-admin/admin.php?page=wo_settings#clients'; + } + + if ($ecommerceOAuth) { + $ecommerceOAuthRedirectUri = dol_buildpath('/custom/ecommerceng/core/modules/oauth/wordpress_oauthcallback.php', 2).'?ecommerce_id='.$ecommerceId; + $ecommerceOAuthId = ($_POST['ecommerce_oauth_id'] ? $_POST['ecommerce_oauth_id'] : $siteDb->oauth_id); + $ecommerceOAuthSecret = ($_POST['ecommerce_oauth_secret'] ? $_POST['ecommerce_oauth_secret'] : $siteDb->oauth_secret); + + // Token + $ecommerceOAuthTokenObj = null; + $storage = new DoliStorage($db, $conf); + try { + $ecommerceOAuthTokenObj = $storage->retrieveAccessToken('ECommerce_'.$ecommerceId); + } catch(Exception $e) {} + $ecommerceOAuthGenerateToken = (!empty($ecommerceOAuthId) && !empty($ecommerceOAuthSecret) || is_object($ecommerceOAuthTokenObj)); + + $ecommerceOAuthBackToUri = urlencode(dol_buildpath('/custom/ecommerceng/admin/eCommerceSetup.php', 2).'?ecommerce_id='.$ecommerceId); + + if (is_object($ecommerceOAuthTokenObj)) { + $ecommerceOAuthTokenExpired = ($ecommerceOAuthTokenObj->getEndOfLife() !== $ecommerceOAuthTokenObj::EOL_NEVER_EXPIRES && $ecommerceOAuthTokenObj->getEndOfLife() !== $ecommerceOAuthTokenObj::EOL_UNKNOWN && time() > ($ecommerceOAuthTokenObj->getEndOfLife() - 30)); + + $ecommerceOAuthHasRefreshToken = !empty($ecommerceOAuthTokenObj->getRefreshToken()); + + $endoflife = $ecommerceOAuthTokenObj->getEndOfLife(); + if ($endoflife == $ecommerceOAuthTokenObj::EOL_NEVER_EXPIRES) { + $ecommerceOAuthTokenExpireDate = $langs->trans("Never"); + } elseif ($endoflife == $ecommerceOAuthTokenObj::EOL_UNKNOWN) { + $ecommerceOAuthTokenExpireDate = $langs->trans("Unknown"); + } else { + $ecommerceOAuthTokenExpireDate = dol_print_date($endoflife, "dayhour"); + } + } + } +} $ecommerceLastUpdate = $siteDb->last_update; $var = true; diff --git a/htdocs/ecommerceng/admin/tpl/eCommerceSetup.tpl.php b/htdocs/ecommerceng/admin/tpl/eCommerceSetup.tpl.php old mode 100755 new mode 100644 index 41f3130..fa18c35 --- a/htdocs/ecommerceng/admin/tpl/eCommerceSetup.tpl.php +++ b/htdocs/ecommerceng/admin/tpl/eCommerceSetup.tpl.php @@ -146,9 +146,7 @@ > trans('ThirdPartyForNonLoggedUsers') ?> - select_company($conf->global->ECOMMERCENG_USE_THIS_THIRDPARTY_FOR_NONLOGGED_CUSTOMER, 'ECOMMERCENG_USE_THIS_THIRDPARTY_FOR_NONLOGGED_CUSTOMER', '', 1); - ?> + select_company($ecommerceFkAnonymousThirdparty, 'ecommerce_fk_anonymous_thirdparty', '', 1); ?> trans('SynchUnkownCustomersOnThirdParty') ?> @@ -182,8 +180,8 @@ '.$langs->trans("ECommerceClickUrlToTestUrl").''; + if ($ecommerceWebserviceAddressTest) + print '
'.$langs->trans("ECommerceClickUrlToTestUrl").''; ?> trans('ECommerceSiteAddressDescription') ?> @@ -201,35 +199,33 @@ - > - trans('ECommerceUserPassword') ?> - - - - trans('ECommerceUserPasswordDescription') ?> - + > + trans('ECommerceUserPassword') ?> + + trans('ECommerceUserPasswordDescription') ?> + global->PRODUIT_MULTIPRICES)) { - $var = !$var; -?> - > - trans('ECommercePriceLevel') ?> - - - - trans('ECommercePriceLevelDescription') ?> - - + $var = !$var; +?> + > + trans('ECommercePriceLevel') ?> + + + + trans('ECommercePriceLevelDescription') ?> + + > - trans('ECommerceMagentoPriceType') ?> + trans('ECommercePriceType') ?> - + + - trans('ECommerceMagentoPriceTypeDescription') ?> + trans('ECommercePriceTypeDescription') ?> -
+trans("ECommerceOAuthWordpressSetup", $ecommerceOAuthWordpressOAuthSetupUri)); +?> + + + + + + + + > + + + + + + > + + + + + + > + + + + + + > + + + + + + > + + + + + + > + + + + + + > + + + + + + > + + + + + +
trans('Parameter') ?>trans('Value') ?>trans('Description') ?>
trans("ECommerceSiteOAuthRedirectUri"); ?>trans('ECommerceSiteOAuthRedirectUriDescription') ?>
trans("ECommerceSiteOAuthId"); ?>trans('ECommerceSiteOAuthIdDescription') ?>
trans("ECommerceSiteOAuthSecret"); ?>trans('ECommerceSiteOAuthSecretDescription') ?>
trans("IsTokenGenerated"); ?>trans("HasAccessToken") : $langs->trans("NoAccessToken")); ?> +' . $langs->trans('DeleteAccess') . '

'; + } + // Request remote token + print '' . $langs->trans('RequestAccess') . '

'; + // Check remote access + if ($ecommerceOAuthCheckTokenUri) { + print $langs->trans("ECommerceOAuthCheckToken", $ecommerceOAuthCheckTokenUri); + } +?> +
trans("Token"); ?>getAccessToken() : ''); ?>
trans("TOKEN_REFRESH"); ?>
trans("TOKEN_EXPIRED"); ?>
trans("TOKEN_EXPIRE_AT"); ?>
+ +
stock->enabled) @@ -293,7 +385,7 @@ trans('ECommerceStockSyncDirection') ?> $langs->trans('None'), 'ecommerce2dolibarr'=>'eCommerce to Dolibarr', 'dolibarr2ecommerce'=>'Dolibarr to eCommerce'); + $array=array('none'=>$langs->trans('None'), 'ecommerce2dolibarr'=>$langs->trans('ECommerceToDolibarr'), 'dolibarr2ecommerce'=>$langs->trans('DolibarrToeCommerce')); print $form->selectarray('ecommerce_stock_sync_direction', $array, $ecommerceStockSyncDirection); ?> @@ -327,6 +419,14 @@ { ?> +type == 2) +{ +?> + ")'>trans('ECommerceWoocommerceUpdateDictTaxClasses') ?> + ")'>trans('Delete') ?> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +dol_include_once('/ecommerceng/class/business/eCommerceSynchro.class.php'); + +/** + * \file htdocs/ecommerceng/class/actions_ecommerceng.class.php + * \ingroup ecommerceng + * \brief File for hooks + */ + +class ActionsECommerceNg +{ + /** + * Overloading the doActions function : replacing the parent's function with the one below + * + * @param array() $parameters Hook metadatas (context, etc...) + * @param CommonObject &$object The object to process (an invoice if you are in invoice module, a propale in propale's module, etc...) + * @param string &$action Current action (if set). Generally create or edit or null + * @param HookManager $hookmanager Hook manager propagated to allow calling another hook + * @return int < 0 on error, 0 on success, 1 to replace standard code + */ + function doActions($parameters, &$object, &$action, $hookmanager) + { + global $conf, $user, $langs; + + if (in_array('productdocuments', explode(':', $parameters['context'])) && $action == 'synchronize_images') { + if (!empty($conf->global->ECOMMERCENG_ENABLE_SYNCHRO_IMAGES)) { + $result = $object->call_trigger('PRODUCT_MODIFY', $user); + if ($result < 0) { + if (count($object->errors)) setEventMessages($object->error, $object->errors, 'errors'); + else setEventMessages($langs->trans($object->error), null, 'errors'); + } else { + setEventMessage($langs->trans('ECommerceProductImagesSynchronized')); + } + } + } + + return 0; // or return 1 to replace standard code + } + + /** + * Overloading the formObjectOptions function : replacing the parent's function with the one below + * + * @param array() $parameters Hook metadatas (context, etc...) + * @param CommonObject &$object The object to process (an invoice if you are in invoice module, a propale in propale's module, etc...) + * @param string &$action Current action (if set). Generally create or edit or null + * @param HookManager $hookmanager Hook manager propagated to allow calling another hook + * @return int < 0 on error, 0 on success, 1 to replace standard code + */ + function formObjectOptions($parameters, &$object, &$action, $hookmanager) + { + global $conf, $langs; + + if (in_array('productdocuments', explode(':', $parameters['context']))) { + if (!empty($conf->global->ECOMMERCENG_ENABLE_SYNCHRO_IMAGES)) { + $buttons = '
'; + $buttons .= ''; + $buttons .= '
'; + + print ''; + } + } + + return 0; // or return 1 to replace standard code + } + + /** + * Overloading the afterODTCreation function : replacing the parent's function with the one below + * + * @param array() $parameters Hook metadatas (context, etc...) + * @param CommonObject &$object The object to process (an invoice if you are in invoice module, a propale in propale's module, etc...) + * @param string &$action Current action (if set). Generally create or edit or null + * @param HookManager $hookmanager Hook manager propagated to allow calling another hook + * @return int < 0 on error, 0 on success, 1 to replace standard code + */ + function afterODTCreation($parameters, &$object, &$action, $hookmanager) + { + global $conf, $user, $langs, $db; + + if ((in_array('expeditioncard', explode(':', $parameters['context'])) && $action == 'confirm_valid') || + (in_array('invoicecard', explode(':', $parameters['context'])) && $action == 'confirm_valid')) { + if (!empty($conf->global->ECOMMERCENG_ENABLE_SEND_FILE_TO_ORDER)) { + $commande_id = 0; + $societe_id = 0; + $object_src = $parameters['object']; + $object_src->fetchObjectLinked('', 'commande', $object_src->id, $object_src->element); + if (!empty($object_src->linkedObjects)) { + foreach ($object_src->linkedObjects['commande'] as $element) { + $commande_id = $element->id; + $societe_id = $element->socid; + } + } + + if ($commande_id > 0) { + $db->begin(); + + $error = 0; // Error counter + $eCommerceSite = new eCommerceSite($db); + $sites = $eCommerceSite->listSites('object'); + + foreach ($sites as $site) { + if (!$error) { + $eCommerceCommande = new eCommerceCommande($db); + $eCommerceCommande->fetchByCommandeId($commande_id, $site->id); + + $eCommerceSociete = new eCommerceSociete($db); + $eCommerceSociete->fetchByFkSociete($societe_id, $site->id); + + if ($eCommerceCommande->remote_id > 0 && $eCommerceSociete->remote_id > 0) { + $eCommerceSynchro = new eCommerceSynchro($db, $site); + dol_syslog("Hook ActionsECommerceNg::afterPDFCreation try to connect to eCommerce site " . $site->name); + $eCommerceSynchro->connect(); + if (count($eCommerceSynchro->errors)) { + $error++; + setEventMessages($eCommerceSynchro->error, $eCommerceSynchro->errors, 'errors'); + } + + if (!$error) { + $result = $eCommerceSynchro->eCommerceRemoteAccess->sendFileForCommande($eCommerceCommande->remote_id, $eCommerceSociete->remote_id, $object_src, $parameters['file'], $parameters['outputlangs']); + if (!$result) { + $error++; + $this->errors[] = $eCommerceSynchro->eCommerceRemoteAccess->error; + } + } + } else { + dol_syslog("Order with id " . $commande_id . " is not linked to an ecommerce record so we don't send file to it."); + } + } + } + + if ($error) { + $db->rollback(); + return -1; + } else { + $db->commit(); + return 0; + } + } + } + } + + return 0; // or return 1 to replace standard code + } + + /** + * Overloading the afterPDFCreation function : replacing the parent's function with the one below + * + * @param array() $parameters Hook metadatas (context, etc...) + * @param CommonObject &$object The object to process (an invoice if you are in invoice module, a propale in propale's module, etc...) + * @param string &$action Current action (if set). Generally create or edit or null + * @param HookManager $hookmanager Hook manager propagated to allow calling another hook + * @return int < 0 on error, 0 on success, 1 to replace standard code + */ + function afterPDFCreation($parameters, &$object, &$action, $hookmanager) + { + global $conf, $user, $langs, $db; + + if ((in_array('expeditioncard', explode(':', $parameters['context'])) && $action == 'confirm_valid') || + (in_array('invoicecard', explode(':', $parameters['context'])) && $action == 'confirm_valid')) { + if (!empty($conf->global->ECOMMERCENG_ENABLE_SEND_FILE_TO_ORDER)) { + $commande_id = 0; + $societe_id = 0; + $object_src = $parameters['object']; + $object_src->fetchObjectLinked('', 'commande', $object_src->id, $object_src->element); + if (!empty($object_src->linkedObjects)) { + foreach ($object_src->linkedObjects['commande'] as $element) { + $commande_id = $element->id; + $societe_id = $element->socid; + } + } + + if ($commande_id > 0) { + $db->begin(); + + $error = 0; // Error counter + $eCommerceSite = new eCommerceSite($db); + $sites = $eCommerceSite->listSites('object'); + + foreach ($sites as $site) { + if (!$error) { + $eCommerceCommande = new eCommerceCommande($db); + $eCommerceCommande->fetchByCommandeId($commande_id, $site->id); + + $eCommerceSociete = new eCommerceSociete($db); + $eCommerceSociete->fetchByFkSociete($societe_id, $site->id); + + if ($eCommerceCommande->remote_id > 0 && $eCommerceSociete->remote_id > 0) { + $eCommerceSynchro = new eCommerceSynchro($db, $site); + dol_syslog("Hook ActionsECommerceNg::afterPDFCreation try to connect to eCommerce site " . $site->name); + $eCommerceSynchro->connect(); + if (count($eCommerceSynchro->errors)) { + $error++; + setEventMessages($eCommerceSynchro->error, $eCommerceSynchro->errors, 'errors'); + } + + if (!$error) { + $result = $eCommerceSynchro->eCommerceRemoteAccess->sendFileForCommande($eCommerceCommande->remote_id, $eCommerceSociete->remote_id, $object_src, $parameters['file'], $parameters['outputlangs']); + if (!$result) { + $error++; + $this->errors[] = $eCommerceSynchro->eCommerceRemoteAccess->error; + } + } + } else { + dol_syslog("Order with id " . $commande_id . " is not linked to an ecommerce record so we don't send file to it."); + } + } + } + + if ($error) { + $db->rollback(); + return -1; + } else { + $db->commit(); + return 0; + } + } + } + } + + return 0; // or return 1 to replace standard code + } +} \ No newline at end of file diff --git a/htdocs/ecommerceng/class/business/eCommerceSynchro.class.php b/htdocs/ecommerceng/class/business/eCommerceSynchro.class.php old mode 100755 new mode 100644 index 663f7a5..372d601 --- a/htdocs/ecommerceng/class/business/eCommerceSynchro.class.php +++ b/htdocs/ecommerceng/class/business/eCommerceSynchro.class.php @@ -305,7 +305,7 @@ public function getNbCategoriesInDolibarr() public function getNbCategoriesInDolibarrLinkedToE($excludeid = 0) { - $sql="SELECT COUNT(rowid) as nb FROM ".MAIN_DB_PREFIX."ecommerce_category WHERE type = 0"; + $sql="SELECT COUNT(rowid) as nb FROM ".MAIN_DB_PREFIX."ecommerce_category WHERE type = 0 AND fk_site=".$this->eCommerceSite->id; $sql.=" AND fk_category <> ".$excludeid; $resql=$this->db->query($sql); if ($resql) @@ -336,7 +336,7 @@ public function getNbProductInDolibarr() public function getNbProductInDolibarrLinkedToE() { - $sql="SELECT COUNT(rowid) as nb FROM ".MAIN_DB_PREFIX."ecommerce_product"; + $sql="SELECT COUNT(rowid) as nb FROM ".MAIN_DB_PREFIX."ecommerce_product WHERE fk_site=".$this->eCommerceSite->id; $resql=$this->db->query($sql); if ($resql) { @@ -370,7 +370,7 @@ public function getNbSocieteInDolibarr() public function getNbSocieteInDolibarrLinkedToE() { - $sql="SELECT COUNT(s.rowid) as nb FROM ".MAIN_DB_PREFIX."ecommerce_societe as s"; + $sql="SELECT COUNT(s.rowid) as nb FROM ".MAIN_DB_PREFIX."ecommerce_societe as s WHERE fk_site=".$this->eCommerceSite->id; $resql=$this->db->query($sql); if ($resql) @@ -401,7 +401,7 @@ public function getNbCommandeInDolibarr() public function getNbCommandeInDolibarrLinkedToE() { - $sql="SELECT COUNT(rowid) as nb FROM ".MAIN_DB_PREFIX."ecommerce_commande"; + $sql="SELECT COUNT(rowid) as nb FROM ".MAIN_DB_PREFIX."ecommerce_commande WHERE fk_site=".$this->eCommerceSite->id; $resql=$this->db->query($sql); if ($resql) { @@ -431,7 +431,7 @@ public function getNbFactureInDolibarr() public function getNbFactureInDolibarrLinkedToE() { - $sql="SELECT COUNT(rowid) as nb FROM ".MAIN_DB_PREFIX."ecommerce_facture"; + $sql="SELECT COUNT(rowid) as nb FROM ".MAIN_DB_PREFIX."ecommerce_facture WHERE fk_site=".$this->eCommerceSite->id; $resql=$this->db->query($sql); if ($resql) { @@ -761,7 +761,7 @@ public function synchCategory($toNb=0) $counter++; if ($toNb > 0 && $counter > $toNb) break; - dol_syslog("synchCategory Process sync of magento category remote_id=".$categoryArray['category_id']." name=".$categoryArray['name']." remote parent_id=".$categoryArray['parent_id']); + dol_syslog("synchCategory Process sync of ecommerce category remote_id=".$categoryArray['category_id']." name=".$categoryArray['name']." remote parent_id=".$categoryArray['parent_id']); $this->db->begin(); @@ -769,59 +769,33 @@ public function synchCategory($toNb=0) $dBCategorie = new Categorie($this->db); - // Get parent (if already synch) - $this->eCommerceMotherCategory->fetchByRemoteId($categoryArray['parent_id'], $this->eCommerceSite->id); - - // If there is a parent, we check we set it into $this->eCommerceMotherCategory - /*if ($parentremoteid > 0 && empty($this->eCommerceMotherCategory->id)) - { - $error++; - $this->errors[]="Failed to get/create parent category"; - }*/ - - /* - // Check if the ecommerce category has an ecommerce parent category, if not, that implies it is root. - // !!!!!! This is true only if categories are returned in order of parent first. - $motherExists = $this->eCommerceMotherCategory->fetchByRemoteId($parentremoteid, $this->eCommerceSite->id); + // Check if the ecommerce category has an ecommerce parent category, if not, that implies it is root + $motherExists = $this->eCommerceMotherCategory->fetchByRemoteId($categoryArray['parent_id'], $this->eCommerceSite->id); // Now $this->eCommerceMotherCategory contains the mother category or null - if ($motherExists < 1) // Not found. + // if fetch on eCommerceMotherCategory has failed, it is root + /* Disable automatic creation in dolibarr of link to parent into table of links + because, we can't know what is the parent. Here code suppose it is the root category defined into setup + but we may have several levels + if ($motherExists < 1 && ($this->eCommerceMotherCategory->fetchByFKCategory($this->eCommerceSite->fk_cat_product, $this->eCommerceSite->id) < 0)) { - // if remote fetch on eCommerceMotherCategory has failed, it is root - // !!!!!! This is true only if categories are returned in order of parent first. - - // We get the ROOT category. - if ($this->eCommerceMotherCategory->fetchByFKCategory($this->eCommerceSite->fk_cat_product, $this->eCommerceSite->id) < 0) - { - // get the importRootCategory of Dolibarr set for the eCommerceSite - $dBCategorie->fetch($this->eCommerceSite->fk_cat_product); - - // We rely on first parent of current record because root is not already synch, - // it means, it's first synch, in such a case, the first record is just under ROOT. - // TODO Make remote call until we found the true ROOT and the the first parent - $parentremoteid=$categoryArray['parent_id']; - - $this->eCommerceMotherCategory->label = $dBCategorie->label; - $this->eCommerceMotherCategory->type = $dBCategorie->type; - $this->eCommerceMotherCategory->description = $dBCategorie->description; - $this->eCommerceMotherCategory->fk_category = $dBCategorie->id; - $this->eCommerceMotherCategory->fk_site = $this->eCommerceSite->id; - $this->eCommerceMotherCategory->remote_id = $parentremoteid; - $this->eCommerceMotherCategory->last_update = strtotime($categoryArray['updated_at']); - - // reset $dBCategorie - $dBCategorie = new Categorie($this->db); - - // Create an entry to map importRootCategory in eCommerceCategory - $this->eCommerceMotherCategory->create($this->user); - } - else - { - // The root category is already synch. - } - }*/ - - // Process category to synch. + // get the importRootCategory of Dolibarr set for the eCommerceSite + $dBCategorie->fetch($this->eCommerceSite->fk_cat_product); + + $this->eCommerceMotherCategory->label = $dBCategorie->label; + $this->eCommerceMotherCategory->type = $dBCategorie->type; + $this->eCommerceMotherCategory->description = $dBCategorie->description; + $this->eCommerceMotherCategory->fk_category = $dBCategorie->id; + $this->eCommerceMotherCategory->fk_site = $this->eCommerceSite->id; + $this->eCommerceMotherCategory->remote_id = $categoryArray['parent_id']; + + // reset $dBCategorie + $dBCategorie = new Categorie($this->db); + + // Create an entry to map importRootCategory in eCommerceCategory + $this->eCommerceMotherCategory->create($this->user); + } */ + $eCommerceCatExists = $this->eCommerceCategory->fetchByRemoteId($categoryArray['category_id'], $this->eCommerceSite->id); if ($this->eCommerceCategory->fk_category > 0) @@ -829,7 +803,7 @@ public function synchCategory($toNb=0) $synchExists = $eCommerceCatExists >= 0 ? $dBCategorie->fetch($this->eCommerceCategory->fk_category) : -1; if ($synchExists == 0) { - // Category entry exists into table link ecommerce_category with fk_category exists but it links to a non existing category in dolibarr + // Category entry exists into ecommerce_category with fk_category that link to non existing category // Should not happend because we added a cleaned of all orphelins entries into getCategoriesToUpdate $synchExists = -1; } @@ -863,9 +837,21 @@ public function synchCategory($toNb=0) if ($synchExists >= 0) { $result = $dBCategorie->update($this->user); - } else + } + else { - $result = $dBCategorie->create($this->user); + /* Disabled, not sure this is good/required + if ($dBCategorie->already_exists()) { + $cats = $dBCategorie->rechercher('', $dBCategorie->label, $dBCategorie->type, true, true); + foreach ($cats as $cat) { + if ($cat->fk_parent == $dBCategorie->fk_parent) { + $dBCategorie->id = $cat->id; + $result = $dBCategorie->update($this->user); + } + } + } else { */ + $result = $dBCategorie->create($this->user); + /* } */ } // if synchro category ok if ($result >= 0) @@ -873,7 +859,7 @@ public function synchCategory($toNb=0) $this->eCommerceCategory->label = $dBCategorie->label; $this->eCommerceCategory->description = $dBCategorie->description; $this->eCommerceCategory->remote_parent_id = $categoryArray['parent_id']; - $this->eCommerceCategory->last_update = strtotime($categoryArray['updated_at']); + if (!empty($categoryArray['updated_at'])) $this->eCommerceCategory->last_update = strtotime($categoryArray['updated_at']); if ($synchExists > 0) // update it remotely { if ($this->eCommerceCategory->update($this->user) < 0) @@ -915,7 +901,7 @@ public function synchCategory($toNb=0) $this->eCommerceCategory->fk_site = $this->eCommerceSite->id; $this->eCommerceCategory->remote_id = $categoryArray['category_id']; $this->eCommerceCategory->remote_parent_id = $categoryArray['parent_id']; - $this->eCommerceCategory->last_update = strtotime($categoryArray['updated_at']); + if (!empty($categoryArray['updated_at'])) $this->eCommerceCategory->last_update = strtotime($categoryArray['updated_at']); if ($this->eCommerceCategory->create($this->user) < 0) // insert into table lxx_ecommerce_category { @@ -935,6 +921,8 @@ public function synchCategory($toNb=0) $this->errors[] = $this->langs->trans('ECommerceSynchCategoryError').' '.$dBCategorie->error; break; } + + } //var_dump($nbgoodsunchronize);exit; @@ -1044,12 +1032,14 @@ public function synchSociete($toNb=0) if ($refExists >= 0) { $dBSociete->name = $societeArray['name']; - $dBSociete->name_alias = $societeArray['name_alias']; //$dBSociete->ref_ext = $this->eCommerceSite->name.'-'.$societeArray['remote_id']; // No need of ref_ext, we will search if already exists on name - $dBSociete->email = $societeArray['email']; $dBSociete->client = $societeArray['client']; - $dBSociete->tva_intra = $societeArray['vatnumber']; - $dBSociete->tva_assuj = 1; // tba_intra is not saved if this field is not set + if (isset($societeArray['name_alias'])) $dBSociete->name_alias = $societeArray['name_alias']; + if (isset($societeArray['email'])) $dBSociete->email = $societeArray['email']; + if (isset($societeArray['vatnumber'])) { + $dBSociete->tva_intra = $societeArray['vatnumber']; + } + $dBSociete->tva_assuj = 1; // tva_intra is not saved if this field is not set $dBSociete->context['fromsyncofecommerceid'] = $this->eCommerceSite->id; $result = $dBSociete->update($dBSociete->id, $this->user); @@ -1069,45 +1059,55 @@ public function synchSociete($toNb=0) //if societe not exists in eCommerceSociete, societe is created else { + $result = 0; + // First, we check object does not alreay exists. If not, we create it, if it exists, do nothing. - //$result = $dBSociete->fetch(0, '', $this->eCommerceSite->name.'-'.$societeArray['remote_id']); // No need of ref_ext, we will search if already exists on name + if (isset($societeArray['email_key']) && !empty($societeArray['email_key'])) { + // Search into email company and contact + $result = get_company_by_email($this->db, $societeArray['email_key']); - $unicity='name'; - if (! empty($conf->global->ECOMMERCENG_THIRDPARTY_UNIQUE_ON) && $conf->global->ECOMMERCENG_THIRDPARTY_UNIQUE_ON == 'email') - { - $unicity='email'; + if ($result > 0 && $result != $this->eCommerceSite->fk_anonymous_thirdparty) { + $result = $dBSociete->fetch($result); + } } - $result = 0; - // If unicity is on NAME - if ($unicity == 'name') - { - $result = $dBSociete->fetch(0, $societeArray['name']); - } - // If unicity is on EMAIL - if ($unicity == 'email') - { - $sql = 'SELECT s.rowid FROM '.MAIN_DB_PREFIX."societe as s where email like '".$this->db->escape($societeArray['email'])."'"; - $resqlid = $this->db->query($sql); - if ($resqlid) - { - $obj = $this->db->fetch_object($resqlid); - if ($obj) - { - $thirdpartyid = $obj->rowid; - $result = $dBSociete->fetch(0, $thirdpartyid); - } - } - else - { - $error++; - $this->error='Error in getting id from email.'; - $this->errors[]=$this->error; - } + if ($result < 1 && (!isset($societeArray['type']) || $societeArray['type'] == 'company')) { + + $unicity='name'; + if (! empty($conf->global->ECOMMERCENG_THIRDPARTY_UNIQUE_ON) && $conf->global->ECOMMERCENG_THIRDPARTY_UNIQUE_ON == 'email') + { + $unicity='email'; + } + + // If unicity is on NAME + if ($unicity == 'name') + { + $result = $dBSociete->fetch(0, $societeArray['name']); + } + // If unicity is on EMAIL + if ($unicity == 'email') + { + $sql = 'SELECT s.rowid FROM '.MAIN_DB_PREFIX."societe as s where email = '".$this->db->escape($societeArray['email'])."'"; + $resqlid = $this->db->query($sql); + if ($resqlid) + { + $obj = $this->db->fetch_object($resqlid); + if ($obj) + { + $thirdpartyid = $obj->rowid; + $result = $dBSociete->fetch(0, $thirdpartyid); + } + } + else + { + $error++; + $this->error='Error in getting id from email.'; + $this->errors[]=$this->error; + } + } } - if ($result == -2) - { + if ($result == -2) { $error++; $this->error='Several thirdparties with name '.$societeArray['name'].' were found in Dolibarr. Sync is not possible. Please rename one of it to avoid duplicate.'; $this->errors[]=$this->error; @@ -1133,15 +1133,17 @@ public function synchSociete($toNb=0) if ($result == 0) { $dBSociete->name = $societeArray['name']; - $dBSociete->name_alias = $societeArray['name_alias']; //$dBSociete->ref_ext = $this->eCommerceSite->name.'-'.$societeArray['remote_id']; // No need of ref_ext, we will search if already exists on name - $dBSociete->email = $societeArray['email']; $dBSociete->client = $societeArray['client']; - $dBSociete->tva_intra = dol_trunc($societeArray['vatnumber'], 20, 'right', 'UTF-8', 1); + if (isset($societeArray['name_alias'])) $dBSociete->name_alias = $societeArray['name_alias']; + if (isset($societeArray['email'])) $dBSociete->email = $societeArray['email']; + if (isset($societeArray['vatnumber'])) { + $dBSociete->tva_intra = dol_trunc($societeArray['vatnumber'], 20, 'right', 'UTF-8', 1); + } $dBSociete->tva_assuj = 1; // tva_intra is not saved if this field is not set + $dBSociete->context['fromsyncofecommerceid'] = $this->eCommerceSite->id; $dBSociete->code_client = -1; // Automatic code $dBSociete->code_fournisseur = -1; // Automatic code - $dBSociete->context['fromsyncofecommerceid'] = $this->eCommerceSite->id; $result = $dBSociete->create($this->user); if ($result < 0) @@ -1150,16 +1152,20 @@ public function synchSociete($toNb=0) $this->errors[]=$this->langs->trans('ECommerceSynchSocieteCreateError').' '.$dBSociete->error; $this->errors = array_merge($this->errors, $dBSociete->errors); } + + $dBSociete->update_note($societeArray['note_private'],'_private'); } else if ($result > 0) { $dBSociete->name = $societeArray['name']; - $dBSociete->name_alias = $societeArray['name_alias']; //$dBSociete->ref_ext = $this->eCommerceSite->name.'-'.$societeArray['remote_id']; // No need of ref_ext, we will search if already exists on name - $dBSociete->email = $societeArray['email']; $dBSociete->client = $societeArray['client']; - $dBSociete->tva_intra = $societeArray['vatnumber']; - $dBSociete->tva_assuj = 1; // tba_intra is not saved if this field is not set + if (isset($societeArray['name_alias'])) $dBSociete->name_alias = $societeArray['name_alias']; + if (isset($societeArray['email'])) $dBSociete->email = $societeArray['email']; + if (isset($societeArray['vatnumber'])) { + $dBSociete->tva_intra = $societeArray['vatnumber']; + } + $dBSociete->tva_assuj = 1; // tva_intra is not saved if this field is not set $dBSociete->context['fromsyncofecommerceid'] = $this->eCommerceSite->id; $result = $dBSociete->update($dBSociete->id, $this->user); @@ -1216,6 +1222,9 @@ public function synchSociete($toNb=0) { dol_syslog("Make a remote call to get contacts"); // Slow because done on each thirdparty to sync. $listofaddressids=$this->eCommerceRemoteAccess->getRemoteAddressIdForSociete($societeArray['remote_id']); // Ask contacts to magento + if ($this->eCommerceSite->type == 2) { // Woocommerce + $listofaddressids = $societeArray['remote_datas']; + } if (is_array($listofaddressids)) { $socpeoples = $this->eCommerceRemoteAccess->convertRemoteObjectIntoDolibarrSocpeople($listofaddressids); @@ -1287,6 +1296,10 @@ public function synchSocpeople($socpeopleArray) global $conf; $error=0; + $synchExists = 0; + $contactExists = 0; + + $dBContact = new Contact($this->db); try { dol_syslog("***** eCommerceSynchro synchSocPeople remote_id=".$socpeopleArray['remote_id']." site=".$this->eCommerceSite->id); @@ -1297,92 +1310,74 @@ public function synchSocpeople($socpeopleArray) //print "Work on remote_id = " .$socpeopleArray['remote_id']." type = ".$socpeopleArray['type']."\n"; //check if contact exists in eCommerceSocpeople table - // $socpeopleArray['type'] = 1 = Contact de tiers - // $socpeopleArray['type'] = 2 = Contact de commande - // $socpeopleArray['type'] = 3 = Contact de facture - // $socpeopleArray['type'] = 4 = Contact de livraison - $synchExists = $this->eCommerceSocpeople->fetchByRemoteId($socpeopleArray['remote_id'], $socpeopleArray['type'], $this->eCommerceSite->id); + if (!empty($socpeopleArray['remote_id'])) { + // $socpeopleArray['type'] = 1 = Contact de tiers + // $socpeopleArray['type'] = 2 = Contact de commande + // $socpeopleArray['type'] = 3 = Contact de facture + // $socpeopleArray['type'] = 4 = Contact de livraison + $synchExists = $this->eCommerceSocpeople->fetchByRemoteId($socpeopleArray['remote_id'], $socpeopleArray['type'], $this->eCommerceSite->id); + + if ($synchExists > 0) { + $contactExists = $dBContact->fetch($this->eCommerceSocpeople->fk_socpeople); + } + } //set data into contact - $dBContact = new Contact($this->db); - - $contactExists = 0; - - if ($synchExists > 0) + $dBContact->socid = $socpeopleArray['fk_soc']; + $dBContact->fk_soc = $socpeopleArray['fk_soc']; + $dBContact->firstname = $socpeopleArray['firstname']; + $dBContact->lastname = $socpeopleArray['lastname']; + $dBContact->address = $socpeopleArray['address']; + $dBContact->cp = $socpeopleArray['zip']; + if ((float) DOL_VERSION >= 6.0) { - $test = $dBContact->fetch($this->eCommerceSocpeople->fk_socpeople); - if ($test > 0) - { - $contactExists = $dBContact->id; - } + $dBContact->zip = dol_trunc($socpeopleArray['zip'], 25, 'right', 'UTF-8', 1); } - - if (! $contactExists) + else { - $dBContact->socid = $socpeopleArray['fk_soc']; - $dBContact->fk_soc = $socpeopleArray['fk_soc']; - //$dBContact->fk_pays = $socpeopleArray['fk_pays']; - $dBContact->lastname = $socpeopleArray['lastname']; - $dBContact->town = dol_trunc($socpeopleArray['town'], 30, 'right', 'UTF-8', 1); - $dBContact->ville = $dBContact->town; - $dBContact->firstname = $socpeopleArray['firstname']; - if ((float) DOL_VERSION >= 6.0) - { - $dBContact->zip = dol_trunc($socpeopleArray['zip'], 25, 'right', 'UTF-8', 1); - } - else - { - $dBContact->zip = dol_trunc($socpeopleArray['zip'], 10, 'right', 'UTF-8', 1); - } - $dBContact->cp = $socpeopleArray['zip']; - $dBContact->address = $socpeopleArray['address']; - $dBContact->phone_pro = dol_trunc($socpeopleArray['phone'], 30, 'right', 'UTF-8', 1); - $dBContact->fax = dol_trunc($socpeopleArray['fax'], 30, 'right', 'UTF-8', 1); - $dBContact->context['fromsyncofecommerceid'] = $this->eCommerceSite->id; - + $dBContact->zip = dol_trunc($socpeopleArray['zip'], 10, 'right', 'UTF-8', 1); + } + $dBContact->town = dol_trunc($socpeopleArray['town'], 30, 'right', 'UTF-8', 1); + $dBContact->ville = $dBContact->town; + $dBContact->country_id = $socpeopleArray['country_id']; + $dBContact->email = $socpeopleArray['email']; + $dBContact->phone_pro = dol_trunc($socpeopleArray['phone'], 30, 'right', 'UTF-8', 1); + $dBContact->fax = dol_trunc($socpeopleArray['fax'], 30, 'right', 'UTF-8', 1); + $dBContact->context['fromsyncofecommerceid'] = $this->eCommerceSite->id; + + if (!$contactExists) { $contactExists = $this->getContactIdFromInfos($dBContact); + if ($contactExists > 0) { + $contactExists = $dBContact->fetch($contactExists); + if (isset($socpeopleArray['country_id'])) $dBContact->country_id = $socpeopleArray['country_id']; + if (isset($socpeopleArray['email'])) $dBContact->email = $socpeopleArray['email']; + if (isset($socpeopleArray['phone'])) $dBContact->phone_pro = dol_trunc($socpeopleArray['phone'], 30, 'right', 'UTF-8', 1); + if (isset($socpeopleArray['fax'])) $dBContact->fax = dol_trunc($socpeopleArray['fax'], 30, 'right', 'UTF-8', 1); + $dBContact->context['fromsyncofecommerceid'] = $this->eCommerceSite->id; + $synchExists = $this->eCommerceSocpeople->fetchByFkSocpeople($dBContact->id, $this->eCommerceSite->id); + } } - if ($contactExists) - $dBContact->id = $contactExists; - - //if contact exists in eCommerceSocpeople, contact should exists also in llx_socpeople - if (($synchExists > 0 && $this->eCommerceSocpeople->fk_socpeople > 0) || $contactExists > 0) - { - $refExists = $dBContact->fetch($contactExists > 0 ? $contactExists : $this->eCommerceSocpeople->fk_socpeople); - - if ($refExists > 0) - { - //dol_syslog("We don't know if contact on ecommerce was modified so we force update of all fields"); - //$result = $dBContact->update($dBContact->id, $this->user); - $result = 0; - } - else if ($refExists == 0) // If not, we create it - { - $result = $dBContact->create($this->user); - if ($result < 0) - { - $error++; - $this->errors[]=$this->langs->trans('ECommerceSynchContactCreateError').' (remote id = '.$socpeopleArray['remote_id'].') '.$dBContact->error; - $this->errors = array_merge($this->errors, $this->dBContact->errors); - } - } - else if ($refExists < 0) - { - $this->errors[] = $this->langs->trans('ECommerceSynchSocieteErrorBetweenECommerceSocpeopleAndContact'); - return false; + if ($contactExists > 0) { + $result = $dBContact->update($dBContact->id, $this->user); + if ($result < 0) { + $error++; + $this->error = $this->langs->trans('ECommerceSynchContactUpdateError') . ' ' . $dBContact->error; + $this->errors[] = $this->error; } - } - //if no previous synchro exists (not found in table of links) - else - { + } else if ($contactExists == 0) { $result = $dBContact->create($this->user); - if ($result < 0) - { + if ($result < 0) { $error++; - $this->errors[]=$this->langs->trans('ECommerceSynchContactCreateError').' (remote id = '.$socpeopleArray['remote_id'].') '.$dBContact->error; - $this->errors = array_merge($this->errors, $dBContact->errors); + $this->error = $this->langs->trans('ECommerceSynchContactCreateError') . ' ' . $dBContact->error; + $this->errors[] = $this->error; } + } else if ($synchExists > 0 && $contactExists < 0) { + $error++; + $this->errors[] = $this->langs->trans('ECommerceSynchSocieteErrorBetweenECommerceSocpeopleAndContact'); + } else if ($contactExists < 0) { + $error++; + $this->errors[] = $this->langs->trans('ECommerceSynchContactFetchError') . ' ' . $dBContact->error; } //if create/update of contact table is ok @@ -1406,11 +1401,11 @@ public function synchSocpeople($socpeopleArray) { //eCommerce create $this->eCommerceSocpeople->fk_site = $this->eCommerceSite->id; - $this->eCommerceSocpeople->remote_id = $socpeopleArray['remote_id']; + $this->eCommerceSocpeople->remote_id = isset($socpeopleArray['remote_id']) ? $socpeopleArray['remote_id'] : 'none-'.$dBContact->id; $this->eCommerceSocpeople->type = $socpeopleArray['type']; if ($this->eCommerceSocpeople->create($this->user) < 0) { - $this->errors[] = $this->langs->trans('ECommerceSyncheCommerceSocpeopleCreateError'); + $this->errors[] = $this->langs->trans('ECommerceSynchECommerceSocpeopleCreateError', $socpeopleArray['fk_soc'], $socpeopleArray['firstname'], $socpeopleArray['lastname']) . ' : ' . $this->eCommerceSocpeople->error; $this->errors = array_merge($this->errors, $this->eCommerceSocpeople->errors); return false; } @@ -1557,7 +1552,7 @@ public function synchProduct($toNb=0) dol_syslog("- Process synch of product remote_id=".$productArray['remote_id']); $counter++; - if ($toNb > 0 && $counter > $toNb) break; + // if ($toNb > 0 && $counter > $toNb) break; if (empty($productArray['remote_id'])) { @@ -1569,93 +1564,93 @@ public function synchProduct($toNb=0) $this->db->begin(); - //check if product exists in eCommerceProduct (with remote id) - $synchExists = $this->eCommerceProduct->fetchByRemoteId($productArray['remote_id'], $this->eCommerceSite->id); - $dBProduct = new Product($this->db); + //check if product exists in eCommerceProduct (with remote id) + $idProduct = ''; + $synchExists = $this->eCommerceProduct->fetchByRemoteId($productArray['remote_id'], $this->eCommerceSite->id); + if ($synchExists > 0) { + $idProduct = $this->eCommerceProduct->fk_product; + } else { // First, we check object does not alreay exists. If not, we create it, if it exists, update it. - $refExists = $dBProduct->fetch('', dol_string_nospecial(trim($productArray['ref']))); - $result = -1; + $refExists = $dBProduct->fetch('', dol_string_nospecial(trim($productArray['ref']))); + if ($refExists > 0 && $this->eCommerceProduct->fetchByProductId($dBProduct->id, $this->eCommerceSite->id) > 0) { + $refExists = ''; + } + } //libelle of product object = label into database + $dBProduct->ref = dol_string_nospecial(trim($productArray['ref'])); $dBProduct->label = $productArray['label']; - $dBProduct->description = $productArray['description']; + $dBProduct->description = isset($productArray['description']) ? $productArray['description'] : $dBProduct->description; $dBProduct->weight = $productArray['weight']; $dBProduct->type = $productArray['fk_product_type']; $dBProduct->finished = $productArray['finished']; $dBProduct->status = $productArray['envente']; + $dBProduct->status_buy = $productArray['enachat']; $dBProduct->country_id = $productArray['fk_country']; $dBProduct->context['fromsyncofecommerceid'] = $this->eCommerceSite->id; $dBProduct->ref_ext = $this->eCommerceSite->name.'-'.$productArray['remote_id']; $dBProduct->url = $productArray['url']; + if ($conf->barcode->enabled) + $dBProduct->barcode = -1; + + if (is_array($productArray['extrafields'])) { + foreach ($productArray['extrafields'] as $extrafield => $extrafield_value) { + $dBProduct->array_options['options_'.$extrafield] = $extrafield_value; + } + } if ($refExists > 0 && isset($dBProduct->id)) { //update $result = $dBProduct->update($dBProduct->id, $this->user); - if ($result >= 0)// rajouter constante TTC/HT + if ($result >= 0) { - if (empty($productArray['price_base_type'])) $productArray['price_base_type']='HT'; - - /* - var_dump($this->eCommerceSite->price_level); - // Result from ecommerce - var_dump($productArray['price_base_type'].' - '.$productArray['price'].' - '.price2num((float) $productArray['tax_rate']).' - '.$productArray['price_min']); - // Into dolibarr database - var_dump($dBProduct->price_base_type." - ".$dBProduct->price." - ".price2num((float) $dBProduct->tva_tx)." - ".$dBProduct->price_min); - var_dump($dBProduct->multiprices_base_type[$this->eCommerceSite->price_level]." - ".$dBProduct->multiprices[$this->eCommerceSite->price_level]." - ".$dBProduct->multiprices_tva_tx[$this->eCommerceSite->price_level]." - ".$dBProduct->multiprices_min[$this->eCommerceSite->price_level]); - */ - - // Update price - if (!empty($conf->global->PRODUIT_MULTIPRICES)) - { - $price_level = $this->eCommerceSite->price_level; - $price_min = $dBProduct->multiprices_min[$price_level]; - if (isset($productArray['price_min'])) $price_min = $productArray['price_min']; - - if ($productArray['price'] != $dBProduct->multiprices[$price_level] || $productArray['price_base_type'] != $dBProduct->multiprices_base_type[$price_level] || (price2num((float) $productArray['tax_rate']) != price2num((float) $dBProduct->multiprices_tva_tx[$price_level])) || (isset($productArray['price_min']) && ($productArray['price_min'] != $dBProduct->multiprices_min[$price_level]))) - { - $dBProduct->updatePrice($productArray['price'], $productArray['price_base_type'], $this->user, $productArray['tax_rate'], $price_min, $price_level); - } - else - { - //print 'No change in price for '.$dBProduct->ref."\n"; - } - } - else - { - $price_min = $dBProduct->price_min; - if (isset($productArray['price_min'])) $price_min = $productArray['price_min']; - - if ($productArray['price'] != $dBProduct->price || $productArray['price_base_type'] != $dBProduct->price_base_type || (price2num((float) $productArray['tax_rate']) != price2num((float) $dBProduct->tva_tx)) || (isset($productArray['price_min']) && ($productArray['price_min'] != $dBProduct->price_min))) - { - $dBProduct->updatePrice($productArray['price'], $productArray['price_base_type'], $this->user, $productArray['tax_rate'], $price_min); - } - else - { - print 'No change in price for '.$dBProduct->ref."\n"; - } + // Update price if need + $price_level = empty($this->eCommerceSite->price_level) ? 1 : $this->eCommerceSite->price_level; + + // Get current product values + if (empty($conf->global->PRODUIT_MULTIPRICES)) { + $price_base_type_org = $dBProduct->price_base_type; + $price_org = $dBProduct->price; + $price_min_org = $dBProduct->price_min; + $tax_rate_org = $dBProduct->tva_tx; + } else { + $price_base_type_org = $dBProduct->multiprices_base_type[$price_level]; + $price_org = $dBProduct->multiprices[$price_level]; + $price_min_org = $dBProduct->multiprices_min[$price_level]; + $tax_rate_org = $dBProduct->multiprices_tva_tx[$price_level]; } - // If eCommerce setup has changed and now prices are switch TI/TE (Tax Include / Tax Excluded) - if ($dBProduct->price_base_type != $this->eCommerceSite->magento_price_type && empty($conf->global->ECOMMERCENG_DISABLE_MAGENTO_PRICE_TYPE)) - { - dol_syslog("Setup price for eCommerce are switched from TE toTI or TI to TE, we update price of product"); + $price_base_type = $this->eCommerceSite->ecommerce_price_type; + if (isset($productArray['price_base_type'])) $price_base_type = $productArray['price_base_type']; + + if ($price_base_type_org != $price_base_type || + $price_org != $productArray['price'] || + (isset($productArray['price_min']) && $price_min_org != $productArray['price_min']) || + price2num((float) $productArray['tax_rate']) != price2num((float) $tax_rate_org) + ) { + // The price type from eCommerce is defined for the site: TI/TE (Tax Include / Tax Excluded) if (empty($conf->global->PRODUIT_MULTIPRICES)) { - $dBProduct->updatePrice($dBProduct->price, $this->eCommerceSite->magento_price_type, $this->user); + $dBProduct->updatePrice($productArray['price'], $price_base_type, $this->user, $productArray['tax_rate'], $productArray['price_min']); } else { - $price_level = $this->eCommerceSite->price_level; - $dBProduct->updatePrice($dBProduct->multiprices[$price_level], $this->eCommerceSite->magento_price_type, $this->user, $dBProduct->multiprices_tva_tx[$price_level], $dBProduct->multiprices_min[$price_level], $price_level); + $dBProduct->updatePrice($productArray['price'], $price_base_type, $this->user, $productArray['tax_rate'], $productArray['price_min'], $price_level); } } } + else + { + $error++; + $this->error=$this->langs->trans('ECommerceSynchProductUpdateError').' '.$dBProduct->error; + $this->errors[]=$this->error; + } // We must set the initial stock if ($this->eCommerceSite->stock_sync_direction == 'ecommerce2dolibarr' && ($productArray['stock_qty'] != $dBProduct->stock_reel)) // Note: $dBProduct->stock_reel is 0 after a creation { - dol_syslog("Stock for product updated is ".$productArray['stock_qty']," in ecommerce, but ".$dBProduct->stock_reel." in Dolibarr, we must update it"); + dol_syslog("Stock for product updated is ".$productArray['stock_qty']." in ecommerce, but ".$dBProduct->stock_reel." in Dolibarr, we must update it"); if (empty($this->eCommerceSite->fk_warehouse)) { $error++; @@ -1676,7 +1671,7 @@ public function synchProduct($toNb=0) { $error++; $this->error=$this->langs->trans('ECommerceSynchMouvementStockChangeError').' '.$movement->error; - $this->errors = array_merge($this->errors, $movement->errors); + $this->errors[] = $this->error; } } } @@ -1684,28 +1679,20 @@ public function synchProduct($toNb=0) else { //create - $dBProduct->ref = dol_string_nospecial(trim($productArray['ref'])); $dBProduct->canvas = $productArray['canvas']; $dBProduct->note = 'Initialy created from '.$this->eCommerceSite->name; $result = $dBProduct->create($this->user); - if ($result >= 0)// rajouter constante TTC/HT + if ($result >= 0) { - if (!empty($conf->global->PRODUIT_MULTIPRICES)) { - $price_level = $this->eCommerceSite->price_level; - $dBProduct->updatePrice($productArray['price'], $dBProduct->multiprices_base_type[$price_level], $this->user, $productArray['tax_rate'], $productArray['price_min'], $price_level); - } - - // If eCommerce setup hase change and now prices are switch TI/TE (Tax Include / Tax Excluded) - if (empty($conf->global->ECOMMERCENG_DISABLE_MAGENTO_PRICE_TYPE)) - { - dol_syslog("Setup price for eCommerce are switched from TE toTI or TI to TE, we update price of product"); - if (empty($conf->global->PRODUIT_MULTIPRICES)) { - $dBProduct->updatePrice($dBProduct->price, $this->eCommerceSite->magento_price_type, $this->user); - } else { - $price_level = $this->eCommerceSite->price_level; - $dBProduct->updatePrice($dBProduct->multiprices[$price_level], $this->eCommerceSite->magento_price_type, $this->user, $dBProduct->multiprices_tva_tx[$price_level], $dBProduct->multiprices_min[$price_level], $price_level); - } + // Set price + $price_level = !empty($this->eCommerceSite->price_level) ? $this->eCommerceSite->price_level : 1; + + // The price type from eCommerce is defined for the site: TI/TE (Tax Include / Tax Excluded) + if (empty($conf->global->PRODUIT_MULTIPRICES)) { + $dBProduct->updatePrice($productArray['price'], $this->eCommerceSite->ecommerce_price_type, $this->user, $productArray['tax_rate'], $productArray['price_min']); + } else { + $dBProduct->updatePrice($productArray['price'], $this->eCommerceSite->ecommerce_price_type, $this->user, $productArray['tax_rate'], $productArray['price_min'], $price_level); } } else @@ -1713,7 +1700,7 @@ public function synchProduct($toNb=0) $error++; if ($dBProduct->error == 'ErrorProductAlreadyExists') $this->error=$this->langs->trans('ECommerceSynchProductCreateError').' '.$this->langs->trans($dBProduct->error, $dBProduct->ref); else $this->error=$this->langs->trans('ECommerceSynchProductCreateError').' '.$dBProduct->error; - $this->errors = array_merge($this->errors, $dBProduct->errors); + $this->errors[] = $this->error; } // We must set the initial stock @@ -1738,8 +1725,8 @@ public function synchProduct($toNb=0) if ($result <= 0) { $error++; - $this->errors[]=$this->langs->trans('ECommerceSynchMouvementStockChangeError').' '.$movement->error; - $this->errors = array_merge($this->errors, $movement->errors); + $this->error=$this->langs->trans('ECommerceSynchMouvementStockChangeError').' '.$movement->error; + $this->errors[] = $this->error; } } } @@ -1784,6 +1771,34 @@ public function synchProduct($toNb=0) } //$cat = new Categorie($this->db, $this->eCommerceSite->fk_cat_product); //$cat->add_type($dBProduct, 'product'); + + // Synchronize images + if (!empty($conf->global->ECOMMERCENG_ENABLE_SYNCHRO_IMAGES)) { + if (is_array($productArray['images'])) { + foreach ($productArray['images'] as $image) { + $ret = ecommerceng_download_image($image, $dBProduct, $error_message); + + if (!$ret) { + $error++; + $error_label = $this->langs->trans('ECommerceSyncheCommerceProductDownloadImageError', + implode(',', $image), $dBProduct->id, $productArray['remote_id'], $this->eCommerceSite->name) . ': ' . $error_message; + $this->errors[] = $error_label; + dol_syslog($error_label, LOG_ERR); + } + } + } + + // Remove obsolete image + $ret = ecommerceng_remove_obsolete_image($dBProduct, $productArray['images'], $error_message); + if (!$ret) { + $error++; + $error_label = $this->langs->trans('ECommerceSyncheCommerceProductDownloadImageError', + $dBProduct->id, $productArray['remote_id'], $this->eCommerceSite->name) . ': ' . $error_message; + $this->errors[] = $error_label; + dol_syslog($error_label, LOG_ERR); + } + } + $this->eCommerceProduct->last_update = $productArray['last_update']; $this->eCommerceProduct->fk_product = $dBProduct->id; @@ -1794,9 +1809,9 @@ public function synchProduct($toNb=0) if ($this->eCommerceProduct->update($this->user) < 0) { $error++; - $this->errors[] = $this->langs->trans('ECommerceSyncheCommerceProductUpdateError') . ' ' . $productArray['label']; - $this->errors = array_merge($this->errors, $this->eCommerceProduct->errors); - dol_syslog($this->langs->trans('ECommerceSyncheCommerceProductUpdateError') . ' ' . $productArray['label'], LOG_WARNING); + $this->error = $this->langs->trans('ECommerceSyncheCommerceProductUpdateError') . ' ' . $productArray['label']; + $this->errors[] = $this->error; + dol_syslog($this->error, LOG_WARNING); } } // if not previous synchro exists into link table (we faild to find it from the remote_id) @@ -1812,17 +1827,18 @@ public function synchProduct($toNb=0) if ($this->eCommerceProduct->create($this->user) < 0) { $error++; - $this->errors[] = $this->langs->trans('ECommerceSyncheCommerceProductCreateError') . ' ' . $productArray['label'].', '.$this->eCommerceProduct->error; - $this->errors = array_merge($this->errors, $this->eCommerceProduct->errors); - dol_syslog($this->langs->trans('ECommerceSyncheCommerceProductCreateError') . ' ' . $productArray['label'].', '.$this->eCommerceProduct->error, LOG_WARNING); + $this->error = $this->langs->trans('ECommerceSyncheCommerceProductCreateError') . ' ' . $productArray['label'].', '.$this->eCommerceProduct->error; + $this->errors[] = $this->error; + dol_syslog($this->error, LOG_WARNING); } } } else { $error++; - $this->errors[] = $this->langs->trans('ECommerceSynchProductError') . ' ' . $productArray['label']; - dol_syslog($this->langs->trans('ECommerceSynchProductError') . ' ' . $productArray['label'], LOG_WARNING); + $this->error = $this->langs->trans('ECommerceSynchProductError') . ' Ref:' . $productArray['ref'] . ' Nom:' . $productArray['label'] . ', remote ID:' . $productArray['remote_id']; + $this->errors[] = $this->error; + dol_syslog($this->error, LOG_WARNING); } unset($dBProduct); @@ -1863,8 +1879,9 @@ public function synchProduct($toNb=0) $this->errors[] = $this->error; } } catch (Exception $e) { - $this->errors[] = $this->langs->trans('ECommerceErrorsynchProduct'); - dol_syslog($this->langs->trans('ECommerceSynchProductError'), LOG_WARNING); + $this->error = $this->langs->trans('ECommerceErrorsynchProduct'); + $this->errors[] = $this->error; + dol_syslog($this->error, LOG_WARNING); } return -1; @@ -1941,10 +1958,10 @@ public function synchCommande($toNb=0) else { // This is an unknown customer. May be a non logged customer. - if (! empty($conf->global->ECOMMERCENG_USE_THIS_THIRDPARTY_FOR_NONLOGGED_CUSTOMER)) + if ($this->eCommerceSite->fk_anonymous_thirdparty > 0) { $societeExists = 1; - $this->eCommerceSociete->fk_societe = $conf->global->ECOMMERCENG_USE_THIS_THIRDPARTY_FOR_NONLOGGED_CUSTOMER; + $this->eCommerceSociete->fk_societe = $this->eCommerceSite->fk_anonymous_thirdparty; } else { @@ -1968,6 +1985,12 @@ public function synchCommande($toNb=0) $dBCommande->context['fromsyncofecommerceid'] = $this->eCommerceSite->id; + if (is_array($commandeArray['extrafields'])) { + foreach ($commandeArray['extrafields'] as $extrafield => $extrafield_value) { + $dBCommande->array_options['options_'.$extrafield] = $extrafield_value; + } + } + if ($dBCommande->ref_client != $commandeArray['ref_client'] || $tmpdateorder1 != $tmpdateorder2 || $tmpdatedeliv1 != $tmpdatedeliv2 @@ -1984,7 +2007,7 @@ public function synchCommande($toNb=0) { $error++; $this->errors[]=$this->langs->trans('ECommerceSynchCommandeUpdateError').' '.$dBCommande->error; - $this->errors = array_merge($this->errors, $dbCommande->errors); + $this->errors = array_merge($this->errors, $dBCommande->errors); } } @@ -2025,14 +2048,14 @@ public function synchCommande($toNb=0) { if ($dBCommande->statut != Commande::STATUS_VALIDATED) { - $dBCommande->setStatut(Commande::STATUS_VALIDATED, $dbCommande->id, $dbCommande->table_element); + $dBCommande->setStatut(Commande::STATUS_VALIDATED, $dBCommande->id, $dBCommande->table_element); } } if ($commandeArray['status'] == 2) // Should be Commande::STATUS_SHIPMENTONPROCESS but not defined in dolibarr 3.9 { if ($dBCommande->statut != 2) { - $dBCommande->setStatut(2, $dbCommande->id, $dbCommande->table_element); + $dBCommande->setStatut(2, $dBCommande->id, $dBCommande->table_element); } } if ($commandeArray['status'] == Commande::STATUS_CANCELED) @@ -2042,7 +2065,7 @@ public function synchCommande($toNb=0) $idWareHouse = 0; // We don't change stock here, even if dolibarr option is on because, this should be already done by product sync //if ($this->eCommerceSite->stock_sync_direction == 'ecommerce2dolibarr') $idWareHouse=$this->eCommerceSite->fk_warehouse; - $dBCommande->cancel(0, $idWarehouse); + $dBCommande->cancel(0, $idWareHouse); } } if ($commandeArray['status'] == Commande::STATUS_CLOSED) @@ -2082,7 +2105,13 @@ public function synchCommande($toNb=0) $dBCommande->note_private=isset($commandeArray['note'])?$commandeArray['note']:""; if (! empty($conf->global->ECOMMERCENG_ENABLE_LOG_IN_NOTE)) { - $dBCommande->note_private.="Last eCommerce order received:\n".dol_trunc(serialize(var_export($commandeArray['remote_order'], true)), 65000); + $dBCommande->note_private.="Last eCommerce order received:\n".serialize(var_export($commandeArray['remote_order'], true)); + } + + if (is_array($commandeArray['extrafields'])) { + foreach ($commandeArray['extrafields'] as $extrafield => $extrafield_value) { + $dBCommande->array_options['options_'.$extrafield] = $extrafield_value; + } } $result = $dBCommande->create($this->user); @@ -2090,8 +2119,8 @@ public function synchCommande($toNb=0) { dol_syslog("synchCommande result=".$result." ".$dBCommande->error, LOG_ERR); $error++; - $this->errors[]=$this->langs->trans('ECommerceSynchCommandeCreateError').' '.$dBCommande->error; - $this->errors = array_merge($this->errors, $dbCommande->errors); + $this->error = $this->langs->trans('ECommerceSynchCommandeCreateError').' '.$dBCommande->error; + $this->errors[] = $this->error; } // Add lines @@ -2110,7 +2139,7 @@ public function synchCommande($toNb=0) if (($result = $dBCommande->defineBuyPrice($item['price'], 0, $fk_product)) < 0) { $this->error = $this->langs->trans('ECommerceSyncheCommerceCommandeUpdateError').' '.$dBCommande->error; - $this->errors = array_merge($this->errors, $dbCommande->errors); + $this->errors[] = $this->error; $error++; break; // break on items } @@ -2135,8 +2164,18 @@ public function synchCommande($toNb=0) $buyprice = $productFournisseur->fourn_unitprice; }*/ - $result = $dBCommande->addline($item['description'], $item['price'], $item['qty'], $item['tva_tx'], 0, 0, - $this->eCommerceProduct->fk_product, //fk_product + $description = $item['description']; + if (empty($description) && $fk_product > 0) { + $dBProduct = new Product($this->db); + $dBProduct->fetch($fk_product); + $description = $dBProduct->label; + } + if (empty($description)) { + $description = $this->langs->trans('ECommerceNoDescForProductLine'); + } + + $result = $dBCommande->addline($description, $item['price'], $item['qty'], $item['tva_tx'], 0, 0, + $fk_product, //fk_product 0, //remise_percent 0, //info_bits 0, //fk_remise_except @@ -2154,8 +2193,8 @@ public function synchCommande($toNb=0) dol_syslog("result=".$result); if ($result <= 0) { - $this->errors[] = $this->langs->trans('ECommerceSyncheCommerceCommandeUpdateError').' '.$dBCommande->error; - $this->errors = array_merge($this->errors, $dbCommande->errors); + $this->error = $this->langs->trans('ECommerceSyncheCommerceCommandeUpdateError').' '.$dBCommande->error; + $this->errors[] = $this->error; $error++; break; // break on items } @@ -2191,8 +2230,8 @@ public function synchCommande($toNb=0) ); if ($result <= 0) { - $this->errors[] = $this->langs->trans('ECommerceSyncheCommerceCommandeUpdateError').' '.$dBCommande->error; - $this->errors = array_merge($this->errors, $dbCommande->errors); + $this->error = $this->langs->trans('ECommerceSyncheCommerceCommandeUpdateError').' '.$dBCommande->error; + $this->errors[] = $this->error; $error++; } } @@ -2265,9 +2304,23 @@ public function synchCommande($toNb=0) } //add or update contacts of order ($this->eCommerceSociete->fk_societe is id in Dolibarr of thirdparty but may be id of the generic "non logged user") - $commandeArray['socpeopleCommande']['fk_soc'] = $this->eCommerceSociete->fk_societe; - $commandeArray['socpeopleFacture']['fk_soc'] = $this->eCommerceSociete->fk_societe; - $commandeArray['socpeopleLivraison']['fk_soc'] = $this->eCommerceSociete->fk_societe; + $fk_soc_socpeopleCommande = $this->eCommerceSociete->fk_societe; + $fk_soc_socpeopleFacture = $this->eCommerceSociete->fk_societe; + $fk_soc_socpeopleLivraison = $this->eCommerceSociete->fk_societe; + if ($this->eCommerceSociete->fk_societe == $this->eCommerceSite->fk_anonymous_thirdparty) { + if (!empty(trim($commandeArray['socpeopleCommande']['email']))) { + $fk_soc_socpeopleCommande = get_company_by_email($this->db, $commandeArray['socpeopleCommande']['email'], $this->eCommerceSite->id); + } + if (!empty(trim($commandeArray['socpeopleFacture']['email']))) { + $fk_soc_socpeopleFacture = get_company_by_email($this->db, $commandeArray['socpeopleFacture']['email'], $this->eCommerceSite->id); + } + if (!empty(trim($commandeArray['socpeopleLivraison']['email']))) { + $fk_soc_socpeopleLivraison = get_company_by_email($this->db, $commandeArray['socpeopleLivraison']['email'], $this->eCommerceSite->id); + } + } + $commandeArray['socpeopleCommande']['fk_soc'] = $fk_soc_socpeopleCommande; + $commandeArray['socpeopleFacture']['fk_soc'] = $fk_soc_socpeopleFacture; + $commandeArray['socpeopleLivraison']['fk_soc'] = $fk_soc_socpeopleLivraison; if (! $error) { @@ -2288,6 +2341,16 @@ public function synchCommande($toNb=0) } } + // Update Payment method + if (! $error) { + if (isset($commandeArray['payment_method'])) { + $payment_method = dol_getIdFromCode($this->db, $commandeArray['payment_method'], 'c_paiement', 'libelle', 'id'); + if ($payment_method != '' && $payment_method > 0) { + $dBCommande->setPaymentMethods($payment_method); + } + } + } + //if synchro commande ok if (! $error) { @@ -2300,8 +2363,8 @@ public function synchCommande($toNb=0) if ($this->eCommerceCommande->update($this->user) < 0) { $error++; - $this->errors[] = $this->langs->trans('ECommerceSyncheCommerceCommandeUpdateError').' '.$this->eCommerceCommande->error; - $this->errors = array_merge($this->errors, $this->eCommerceCommande->errors); + $this->error = $this->langs->trans('ECommerceSyncheCommerceCommandeUpdateError').' '.$this->eCommerceCommande->error; + $this->errors[] = $this->error; } } //if not previous synchro exists @@ -2318,9 +2381,9 @@ public function synchCommande($toNb=0) if ($this->eCommerceCommande->create($this->user) < 0) { $error++; - $this->errors[] = $this->langs->trans('ECommerceSyncheCommerceCommandeCreateError').' '.$dBCommande->id.', '.$this->eCommerceCommande->error; - $this->errors = array_merge($this->errors, $this->eCommerceCommande->errors); - dol_syslog($this->langs->trans('ECommerceSyncheCommerceCommandeCreateError') . ' ' . $dBCommande->id.', '.$this->eCommerceCommande->error, LOG_WARNING); + $this->error = $this->langs->trans('ECommerceSyncheCommerceCommandeCreateError').' '.$dBCommande->id.', '.$this->eCommerceCommande->error; + $this->errors[] = $this->error; + dol_syslog($this->error, LOG_WARNING); } } } @@ -2333,23 +2396,30 @@ public function synchCommande($toNb=0) else { if ($commandeArray['remote_id_societe'] != 0) { $error++; - $this->errors[] = $this->langs->trans('ECommerceSynchCommandeErrorSocieteNotExists') . ' (remote_id='.$commandeArray['remote_id'].') ' . $commandeArray['remote_id_societe']; + $this->errors[] = $this->langs->trans('ECommerceSynchCommandeErrorSocieteNotExists') . ' (Commande ID='.$commandeArray['remote_id'].', Client ID:' . $commandeArray['remote_id_societe'].')'; } else { $error++; $this->errors[] = $this->langs->trans('ECommerceSynchCommandeErrorSocieteNotExists') . ' (remote_id='.$commandeArray['remote_id'].') - Unknown customer.'; - $this->errors[] = 'This order is not linked to a dedicated customer. Try to set option ECOMMERCENG_USE_THIS_THIRDPARTY_FOR_NONLOGGED_CUSTOMER'; + if (empty($this->eCommerceSite->fk_anonymous_thirdparty)) { + $this->errors[] = 'This order is not linked to a dedicated customer. Try to set option Anonymous thirdparty into the site parameters'; + } } } unset($dBCommande); unset($this->eCommerceSociete); unset($this->eCommerceCommande); - if ($error || ! empty($this->errors)) + if ($error) { $this->db->rollback(); $nbrecorderror++; - break; // We decide to stop on first error + // We decide to stop on first error (Can continue if anonymous order with const ECOMMERCENG_PASS_ORDER_FOR_NONLOGGED_CUSTOMER) + if ($commandeArray['remote_id_societe'] != 0 || empty($conf->global->ECOMMERCENG_PASS_ORDER_FOR_NONLOGGED_CUSTOMER)) { + break; + } else { + $error = 0; + } } else { @@ -2457,10 +2527,10 @@ public function synchFacture($toNb=0) else { // This is an unknown customer. May be a non logged customer. - if (! empty($conf->global->ECOMMERCENG_USE_THIS_THIRDPARTY_FOR_NONLOGGED_CUSTOMER)) + if ($this->eCommerceSite->fk_anonymous_thirdparty > 0) { $societeExists = 1; - $this->eCommerceSociete->fk_societe = $conf->global->ECOMMERCENG_USE_THIS_THIRDPARTY_FOR_NONLOGGED_CUSTOMER; + $this->eCommerceSociete->fk_societe = $this->eCommerceSite->fk_anonymous_thirdparty; } else { @@ -3085,7 +3155,8 @@ public function dropImportedAndSyncData($deletealsoindolibarr, $mode='') } //Drop socPeople - if (empty($mode) || preg_match('/^contacts/', $mode)) + if (empty($mode) || preg_match('/^thirdparties/', $mode)) +// if (empty($mode) || preg_match('/^contacts/', $mode)) { $dolObjectsDeleted = 0; $synchObjectsDeleted = 0; @@ -3136,6 +3207,9 @@ public function dropImportedAndSyncData($deletealsoindolibarr, $mode='') $this->initECommerceSociete(); if ($this->eCommerceSociete->fetch($idSociete) > 0) { + // Skip anonymous thirdparty + if ($this->eCommerceSociete->remote_id == 0) continue; + if ($deletealsoindolibarr) { $dbSociete = new Societe($this->db); @@ -3219,12 +3293,16 @@ function getContactIdFromInfos($contact) $contactId = -1; $sql = 'SELECT rowid FROM '.MAIN_DB_PREFIX.'socpeople'; - $sql .= ' WHERE lastname="'.$db->escape(trim($contact->lastname)).'"'; - $sql .= ' AND firstname="'.$db->escape(trim($contact->firstname)).'"'; - $sql .= ' AND address="'.$db->escape(trim($contact->address)).'"'; - $sql .= ' AND town="'.$db->escape(trim($contact->town)).'"'; - $sql .= ' AND zip="'.$db->escape(trim($contact->zip)).'"'; - $sql .= ' AND fk_soc="'.$contact->fk_soc.'"'; + $sql .= ' WHERE lastname LIKE "'.$db->escape(trim($contact->lastname)).'"'; + $sql .= ' AND firstname LIKE "'.$db->escape(trim($contact->firstname)).'"'; + $sql .= ' AND address LIKE "'.$db->escape(trim($contact->address)).'"'; + $sql .= ' AND town LIKE "'.$db->escape(trim($contact->town)).'"'; + $sql .= ' AND zip LIKE "'.$db->escape(trim($contact->zip)).'"'; + if (isset($contact->country_id)) $sql .= ' AND fk_pays='.($contact->country_id>0?$contact->country_id:'NULL'); + if (isset($contact->email)) $sql .= ' AND email LIKE "'.$db->escape(trim($contact->email)).'"'; + if (isset($contact->phone_pro)) $sql .= ' AND phone LIKE "'.$db->escape(trim($contact->phone_pro)).'"'; + if (isset($contact->fax)) $sql .= ' AND fax LIKE "'.$db->escape(trim($contact->fax)).'"'; + $sql .= ' AND fk_soc='.$contact->fk_soc; $resql = $this->db->query($sql); if($resql) @@ -3246,7 +3324,7 @@ function getContactIdFromInfos($contact) { $this->error=$this->db->lasterror(); dol_syslog("eCommerceSynchro::getContactIdFromInfos ".$this->error, LOG_ERR); - return -1; + return $contactId; } } diff --git a/htdocs/ecommerceng/class/data/eCommerceCategory.class.php b/htdocs/ecommerceng/class/data/eCommerceCategory.class.php index 054de82..4f8eef4 100644 --- a/htdocs/ecommerceng/class/data/eCommerceCategory.class.php +++ b/htdocs/ecommerceng/class/data/eCommerceCategory.class.php @@ -95,8 +95,8 @@ function create($user, $notrigger = 0) $sql.= " '" . $this->db->escape($this->description) . "',"; $sql.= " " . $this->db->escape($this->fk_category) . ","; $sql.= " " . $this->db->escape($this->fk_site) . ","; - $sql.= " " . $this->db->escape($this->remote_id) . ","; - $sql.= " " . $this->db->escape($this->remote_parent_id) . ","; + $sql.= " '" . $this->db->escape($this->remote_id) . "',"; + $sql.= " '" . $this->db->escape($this->remote_parent_id) . "',"; $sql.= " '" . $this->db->idate($this->last_update) . "'"; $sql.= ")"; @@ -380,7 +380,7 @@ public function checkForUpdate($siteId, $toDate, $remoteCatToCheck) global $langs; $updateRequired = 0; // If any error occurs, category won't appears in update array - $sql = "SELECT t.last_update as lastdate, t.remote_parent_id as parentid FROM " . MAIN_DB_PREFIX . $this->table_element . " as t"; + $sql = "SELECT t.last_update as lastdate, t.remote_parent_id as parentid, t.fk_category FROM " . MAIN_DB_PREFIX . $this->table_element . " as t"; $sql.= " WHERE t.remote_id=" . $remoteCatToCheck['category_id'] . " AND t.fk_site = " . $siteId; $resql = $this->db->query($sql); @@ -390,12 +390,24 @@ public function checkForUpdate($siteId, $toDate, $remoteCatToCheck) { $obj = $this->db->fetch_object($resql); - $now = $toDate; // Dolibarr's category time - $lu = $this->db->jdate($obj->lastdate); // date of last update process - $lumage = strtotime($remoteCatToCheck['updated_at']); - //var_dump($lu); - //var_dump($lumage); - $updateRequired = ($obj->parentid != $remoteCatToCheck['parent_id'] || ($lu < $lumage)) ? 1 : 0; + $diffdates = false; + $diffvalues = false; + if (empty($remoteCatToCheck['updated_at'])) { + $catObj = new Categorie($this->db); + $ret = $catObj->fetch($obj->fk_category); + if ($ret > 0) { + $diffvalues = $catObj->label != trim($remoteCatToCheck['name']) || $catObj->description != trim($remoteCatToCheck['description']); + } + } else { + $now = $toDate; // Dolibarr's category time + $lu = $this->db->jdate($obj->lastdate); // date of last update process + $lumage = strtotime($remoteCatToCheck['updated_at']); + //var_dump($lu); + //var_dump($lumage); + $diffdates = $lu < $lumage; + } + + $updateRequired = ($obj->parentid != $remoteCatToCheck['parent_id'] || $diffdates || $diffvalues) ? 1 : 0; } else { $updateRequired = 1; diff --git a/htdocs/ecommerceng/class/data/eCommerceProduct.class.php b/htdocs/ecommerceng/class/data/eCommerceProduct.class.php index 4197d9b..46d74eb 100644 --- a/htdocs/ecommerceng/class/data/eCommerceProduct.class.php +++ b/htdocs/ecommerceng/class/data/eCommerceProduct.class.php @@ -190,7 +190,7 @@ function update($user=0, $notrigger=0) $sql.= " fk_product=".(isset($this->fk_product)?intval($this->fk_product):0).","; $sql.= " fk_site=".(isset($this->fk_site)?intval($this->fk_site):0).","; - $sql.= " remote_id=".(isset($this->remote_id)?"'".$this->db->escape($this->remote_id)."'":"").","; + $sql.= " remote_id='".$this->db->escape($this->remote_id)."',"; $sql.= " last_update=".(isset($this->last_update)?"'".$this->last_update."'" : 'null').""; $sql.= " WHERE rowid=".$this->id; @@ -343,7 +343,7 @@ public function fetchByRemoteId($remoteId, $siteId) $sql.= " t.last_update"; $sql.= " FROM ".MAIN_DB_PREFIX."ecommerce_product as t"; $sql.= " WHERE t.fk_site = ".$siteId; - $sql.= " AND t.remote_id = ".$remoteId; + $sql.= " AND t.remote_id = '".$this->db->escape($remoteId)."'"; dol_syslog(get_class($this)."::fetchByRemoteId sql=".$sql, LOG_DEBUG); $resql=$this->db->query($sql); if ($resql) diff --git a/htdocs/ecommerceng/class/data/eCommerceRemoteAccess.class.php b/htdocs/ecommerceng/class/data/eCommerceRemoteAccess.class.php index ff2d8e2..fd63109 100644 --- a/htdocs/ecommerceng/class/data/eCommerceRemoteAccess.class.php +++ b/htdocs/ecommerceng/class/data/eCommerceRemoteAccess.class.php @@ -401,4 +401,36 @@ public function createRemoteLivraison($livraison, $remote_order_id) $this->errors=$this->class->errors; return $result; } + + /** + * Create a remote product + * + * @param Object $object Object product + * @return bool + */ + public function createRemoteProduct($object) + { + $result=$this->class->createRemoteProduct($object); + $this->error=$this->class->error; + $this->errors=$this->class->errors; + return $result; + } + + /** + * Send a file for remote commande + * + * @param int $order_remote_id Id of order on remote ecommerce + * @param int $societe_remote_id Id of societe on remote ecommerce + * @param Object $object Object product + * @param string $file File path + * @param Translate $outputlangs Lang output object + * @return bool + */ + public function sendFileForCommande($order_remote_id, $societe_remote_id, $object, $file, $outputlangs) + { + $result=$this->class->sendFileForCommande($order_remote_id, $societe_remote_id, $object, $file, $outputlangs); + $this->error=$this->class->error; + $this->errors=$this->class->errors; + return $result; + } } diff --git a/htdocs/ecommerceng/class/data/eCommerceSite.class.php b/htdocs/ecommerceng/class/data/eCommerceSite.class.php old mode 100755 new mode 100644 index 9287094..8677165 --- a/htdocs/ecommerceng/class/data/eCommerceSite.class.php +++ b/htdocs/ecommerceng/class/data/eCommerceSite.class.php @@ -41,12 +41,16 @@ class eCommerceSite // extends CommonObject var $filter_value; var $fk_cat_societe; var $fk_cat_product; + var $fk_anonymous_thirdparty; var $fk_warehouse; var $stock_sync_direction; var $last_update; var $timeout; var $magento_use_special_price; - var $magento_price_type; + var $ecommerce_price_type; + + var $oauth_id; + var $oauth_secret; //The site type name is used to define class name in eCommerceRemoteAccess class private $siteTypes = array(1=>'magento', 2=>'woocommerce'); @@ -111,10 +115,14 @@ function create($user, $notrigger=0) if (isset($this->filter_value)) $this->filter_value=trim($this->filter_value); if (isset($this->fk_cat_societe)) $this->fk_cat_societe=trim($this->fk_cat_societe); if (isset($this->fk_cat_product)) $this->fk_cat_product=trim($this->fk_cat_product); + if (isset($this->fk_anonymous_thirdparty)) $this->fk_anonymous_thirdparty=trim($this->fk_anonymous_thirdparty); if (isset($this->fk_warehouse)) $this->fk_warehouse=trim($this->fk_warehouse); if (isset($this->stock_sync_direction)) $this->stock_sync_direction=trim($this->stock_sync_direction); if (isset($this->timeout)) $this->timeout=trim($this->timeout); + if (isset($this->oauth_id)) $this->oauth_id=trim($this->oauth_id); + if (isset($this->oauth_secret)) $this->oauth_secret=trim($this->oauth_secret); + // Check parameters // Put here code to add control on parameters values @@ -130,29 +138,35 @@ function create($user, $notrigger=0) $sql.= "filter_value,"; $sql.= "fk_cat_societe,"; $sql.= "fk_cat_product,"; + $sql.= "fk_anonymous_thirdparty,"; $sql.= "fk_warehouse,"; $sql.= "stock_sync_direction,"; $sql.= "last_update,"; $sql.= "timeout,"; $sql.= "magento_use_special_price,"; - $sql.= "magento_price_type"; + $sql.= "ecommerce_price_type,"; + $sql.= "oauth_id,"; + $sql.= "oauth_secret"; $sql.= ") VALUES ("; $sql.= " ".(! isset($this->name)?'NULL':"'".$this->db->escape($this->name)."'").","; $sql.= " ".(! isset($this->type)?'NULL':"'".$this->type."'").","; $sql.= " ".(! isset($this->webservice_address)?'NULL':"'".$this->db->escape($this->webservice_address)."'").","; $sql.= " ".(! isset($this->user_name)?'NULL':"'".$this->db->escape($this->user_name)."'").","; $sql.= " ".(! isset($this->user_password)?'NULL':"'".$this->db->escape($this->user_password)."'").","; - $sql.= " ".(! isset($this->price_level)?'NULL':"'".$this->db->escape($this->price_level)."'").","; + $sql.= " ".(! isset($this->price_level)?'1':"'".$this->db->escape($this->price_level)."'").","; $sql.= " ".(! isset($this->filter_label)?'NULL':"'".$this->db->escape($this->filter_label)."'").","; $sql.= " ".(! isset($this->filter_value)?'NULL':"'".$this->db->escape($this->filter_value)."'").","; $sql.= " ".($this->fk_cat_societe > 0 ? $this->fk_cat_societe : "NULL").","; $sql.= " ".($this->fk_cat_product > 0 ? $this->fk_cat_product : "NULL").","; + $sql.= " ".($this->fk_anonymous_thirdparty > 0 ? $this->fk_anonymous_thirdparty : "NULL").","; $sql.= " ".($this->fk_warehouse > 0 ? $this->fk_warehouse : "NULL").","; $sql.= " ".($this->stock_sync_direction ? "'".$this->stock_sync_direction."'" : "'none'").","; $sql.= " ".(! isset($this->last_update) || strlen($this->last_update)==0?'NULL':"'".$this->db->idate($this->last_update)."'").","; $sql.= " ".(! isset($this->timeout)?'300':"'".intval($this->timeout)."'").","; $sql.= " ".(! isset($this->magento_use_special_price)?'0':"'".intval($this->magento_use_special_price)."'").","; - $sql.= " ".(! isset($this->magento_price_type)?'HT':"'".$this->magento_price_type."'").""; + $sql.= " ".(! isset($this->ecommerce_price_type)?'HT':"'".$this->ecommerce_price_type."'").","; + $sql.= " ".(! isset($this->oauth_id)?"NULL":"'".$this->db->escape($this->oauth_id)."'").","; + $sql.= " ".(! isset($this->oauth_secret)?"NULL":"'".$this->db->escape($this->oauth_secret)."'").""; $sql.= ")"; $this->db->begin(); @@ -167,7 +181,7 @@ function create($user, $notrigger=0) //create an entry for anonymous company $eCommerceSociete = new eCommerceSociete($this->db); - $eCommerceSociete->fk_societe = dolibarr_get_const($this->db, 'ECOMMERCE_COMPANY_ANONYMOUS'); + $eCommerceSociete->fk_societe = $this->fk_anonymous_thirdparty; $eCommerceSociete->fk_site = $this->id; $eCommerceSociete->remote_id = 0; if ($eCommerceSociete->create($user)<0) @@ -217,12 +231,15 @@ function fetch($id) $sql.= " t.filter_value,"; $sql.= " t.fk_cat_societe,"; $sql.= " t.fk_cat_product,"; + $sql.= " t.fk_anonymous_thirdparty,"; $sql.= " t.fk_warehouse,"; $sql.= " t.stock_sync_direction,"; $sql.= " t.last_update,"; $sql.= " t.timeout,"; $sql.= " t.magento_use_special_price,"; - $sql.= " t.magento_price_type"; + $sql.= " t.ecommerce_price_type,"; + $sql.= " t.oauth_id,"; + $sql.= " t.oauth_secret"; $sql.= " FROM ".MAIN_DB_PREFIX."ecommerce_site as t"; $sql.= " WHERE t.rowid = ".$id; @@ -246,13 +263,16 @@ function fetch($id) $this->filter_value = $obj->filter_value; $this->fk_cat_societe = $obj->fk_cat_societe; $this->fk_cat_product = $obj->fk_cat_product; + $this->fk_anonymous_thirdparty = $obj->fk_anonymous_thirdparty; $this->fk_warehouse = $obj->fk_warehouse; $this->stock_sync_direction = $obj->stock_sync_direction; $this->last_update = $this->db->jdate($obj->last_update); $this->timeout = $obj->timeout; $this->magento_use_special_price = $obj->magento_use_special_price; - $this->magento_price_type = $obj->magento_price_type; + $this->ecommerce_price_type = $obj->ecommerce_price_type; + $this->oauth_id = $obj->oauth_id; + $this->oauth_secret = $obj->oauth_secret; } $this->db->free($resql); @@ -292,8 +312,11 @@ function update($user=0, $notrigger=0) if (isset($this->filter_value)) $this->filter_value=trim($this->filter_value); if (isset($this->fk_cat_societe)) $this->fk_cat_societe=trim($this->fk_cat_societe); if (isset($this->fk_cat_product)) $this->fk_cat_product=trim($this->fk_cat_product); + if (isset($this->fk_anonymous_thirdparty)) $this->fk_anonymous_thirdparty=trim($this->fk_anonymous_thirdparty); if (isset($this->fk_warehouse)) $this->fk_warehouse=trim($this->fk_warehouse); if (isset($this->timeout)) $this->timeout=trim($this->timeout); + if (isset($this->oauth_id)) $this->oauth_id=trim($this->oauth_id); + if (isset($this->oauth_secret)) $this->oauth_secret=trim($this->oauth_secret); // Check parameters // Put here code to add control on parameters values @@ -306,17 +329,20 @@ function update($user=0, $notrigger=0) $sql.= " webservice_address=".(isset($this->webservice_address)?"'".$this->db->escape($this->webservice_address)."'":"null").","; $sql.= " user_name=".(isset($this->user_name)?"'".$this->db->escape($this->user_name)."'":"null").","; $sql.= " user_password=".(isset($this->user_password)?"'".$this->db->escape($this->user_password)."'":"null").","; - $sql.= " price_level=".(isset($this->price_level)?"'".$this->db->escape($this->price_level)."'":"null").","; + $sql.= " price_level=".(isset($this->price_level)?"'".$this->db->escape($this->price_level)."'":"1").","; $sql.= " filter_label=".(isset($this->filter_label)?"'".$this->db->escape($this->filter_label)."'":"null").","; $sql.= " filter_value=".(isset($this->filter_value)?"'".$this->db->escape($this->filter_value)."'":"null").","; $sql.= " fk_cat_societe=".($this->fk_cat_societe > 0 ? $this->fk_cat_societe:"null").","; $sql.= " fk_cat_product=".($this->fk_cat_product > 0 ? $this->fk_cat_product:"null").","; + $sql.= " fk_anonymous_thirdparty=".($this->fk_anonymous_thirdparty > 0 ? $this->fk_anonymous_thirdparty:"null").","; $sql.= " fk_warehouse=".($this->fk_warehouse > 0 ? $this->fk_warehouse:"null").","; $sql.= " stock_sync_direction=".($this->stock_sync_direction ? "'".$this->stock_sync_direction."'":"'none'").","; $sql.= " last_update=".((isset($this->last_update) && $this->last_update != '') ? "'".$this->db->idate($this->last_update)."'" : 'null').","; $sql.= " timeout=".(isset($this->timeout)? "'".intval($this->timeout)."'" : '300').","; $sql.= " magento_use_special_price=".(isset($this->magento_use_special_price)? "'".intval($this->magento_use_special_price)."'" : '0').","; - $sql.= " magento_price_type=".(isset($this->magento_price_type)? "'".$this->magento_price_type."'" : 'HT').""; + $sql.= " ecommerce_price_type=".(isset($this->ecommerce_price_type)? "'".$this->ecommerce_price_type."'" : 'HT').","; + $sql.= " oauth_id=".(isset($this->oauth_id)?"'".$this->oauth_id."'":"NULL").","; + $sql.= " oauth_secret=".(isset($this->oauth_secret)?"'".$this->oauth_secret."'":"NULL").""; $sql.= " WHERE rowid=".$this->id; $this->db->begin(); @@ -327,7 +353,36 @@ function update($user=0, $notrigger=0) if (! $error) { - if (! $notrigger) + $eCommerceSociete = new eCommerceSociete($this->db); + if ($eCommerceSociete->fetchByRemoteId(0, $this->id) > 0) { + if (isset($this->fk_anonymous_thirdparty)) { + // update an entry for anonymous company + $eCommerceSociete->fk_societe = $this->fk_anonymous_thirdparty; + if ($eCommerceSociete->update($user) < 0) { + $error++; + $this->errors[] = "Error " . $this->db->lasterror(); + } + } else { + // delete an entry for anonymous company + if ($eCommerceSociete->delete($user) < 0) { + $error++; + $this->errors[] = "Error " . $this->db->lasterror(); + } + } + } else { + // create an entry for anonymous company + $eCommerceSociete = new eCommerceSociete($this->db); + $eCommerceSociete->fk_societe = $this->fk_anonymous_thirdparty; + $eCommerceSociete->fk_site = $this->id; + $eCommerceSociete->remote_id = 0; + if ($eCommerceSociete->create($user)<0) + { + $error++; + $this->errors[]="Error ".$this->db->lasterror(); + } + } + + if (! $notrigger && !$error) { // Uncomment this and change MYOBJECT to your own tag if you // want this action call a trigger. @@ -492,12 +547,15 @@ function initAsSpecimen() $this->filter_value=''; $this->fk_cat_societe=''; $this->fk_cat_product=''; + $this->fk_anonymous_thirdparty=''; $this->fk_warehouse=''; $this->stock_sync_direction='none'; $this->last_update=''; $this->timeout=''; $this->magento_use_special_price=''; - $this->magento_price_type=''; + $this->ecommerce_price_type=''; + $this->oauth_id=''; + $this->oauth_secret=''; } /** @@ -542,6 +600,30 @@ function listSites($mode='array') return $list; } + /** + * Check if type site is created + * + * @param integer $typeSite Type of site + * @return boolean true if type site has created + */ + function hasTypeSite($typeSite) + { + $sql = "SELECT"; + $sql.= " count(*) AS count"; + $sql.= " FROM ".MAIN_DB_PREFIX."ecommerce_site as t"; + $sql.= " WHERE t.type=".$typeSite; + + $result = $this->db->query($sql); + if ($result) { + $obj = $this->db->fetch_object($result); + if ($obj->count > 0) { + return true; + } + } + + return false; + } + /** * Return list of available site types * @@ -576,7 +658,14 @@ public function getBackUrl() { // Try to guess public home page of ecommerce web site from the api url $url=$this->getFrontUrl(); - $url.='index.php/admin'; + switch ($this->type) { + case 1: // Magento + $url.='index.php/admin'; + break; + case 2: // Woocommerce + $url.=(substr($url, -1, 1)!='/'?'/':'').'wp-admin'; + break; + } return $url; } diff --git a/htdocs/ecommerceng/class/data/eCommerceSocpeople.class.php b/htdocs/ecommerceng/class/data/eCommerceSocpeople.class.php old mode 100755 new mode 100644 index 1625dee..2e59e7f --- a/htdocs/ecommerceng/class/data/eCommerceSocpeople.class.php +++ b/htdocs/ecommerceng/class/data/eCommerceSocpeople.class.php @@ -87,7 +87,7 @@ function create($user, $notrigger=0) $sql.= " ".(isset($this->fk_site)?intval($this->fk_site):0).","; $sql.= " ".(isset($this->remote_id)?"'".$this->remote_id."'":"").","; $sql.= " ".(isset($this->type)?intval($this->type):1).","; - $sql.= " ".(isset($this->last_update)?"'".$this->last_update."'" : 'null').""; + $sql.= " ".(isset($this->last_update)?"'".$this->db->idate($this->last_update)."'" : 'null').""; $sql.= ")"; diff --git a/htdocs/ecommerceng/class/data/woocommerce/eCommerceRemoteAccessWoocommerce.class.php b/htdocs/ecommerceng/class/data/woocommerce/eCommerceRemoteAccessWoocommerce.class.php old mode 100755 new mode 100644 index 0c77508..19c6eba --- a/htdocs/ecommerceng/class/data/woocommerce/eCommerceRemoteAccessWoocommerce.class.php +++ b/htdocs/ecommerceng/class/data/woocommerce/eCommerceRemoteAccessWoocommerce.class.php @@ -29,6 +29,11 @@ dol_include_once('/ecommerceng/includes/WooCommerce/HttpClient/Request.php'); dol_include_once('/ecommerceng/includes/WooCommerce/HttpClient/Response.php'); +dol_include_once('/ecommerceng/lib/eCommerce.lib.php'); + +dol_include_once('/ecommerceng/includes/WordPressClient.php'); + +require_once DOL_DOCUMENT_ROOT.'/categories/class/categorie.class.php'; require_once DOL_DOCUMENT_ROOT.'/core/lib/company.lib.php'; use Automattic\WooCommerce\Client; @@ -39,23 +44,93 @@ */ class eCommerceRemoteAccessWoocommerce { - + /** + * eCommerceSite object. + * + * @var eCommerceSite + */ private $site; + + /** + * Woocommerce client new API v2. + * + * @var Client + */ private $client; + /** + * Woocommerce client old API v3. + * + * @var Client + */ private $clientOld; - private $filter; - private $taxRates; + /** + * WordPress client. + * + * @var WordPressClient + */ + private $worpressclient; + + /** + * Dolibarr tax rates. + * + * @var array + */ + private $dolibarrTaxes; + + /** + * Woocommerce taxes. + * + * @var array + */ + private $woocommerceTaxes; + + /** + * Database handler. + * + * @var DoliDB + */ private $db; /** - * Constructor - * @param DoliDB $db Database handler - * @param string $site eCommerceSite + * Errors list. + * + * @var array + */ + public $errors; + + /** + * GMT timezone. + * + * @var DateTimeZone + */ + public $gmtTimeZone; + + /** + * Current timezone. + * + * @var DateTimeZone + */ + public $currentTimeZone; + + /** + * Constructor + * @param DoliDB $db Database handler + * @param eCommerceSite $site eCommerceSite object */ function eCommerceRemoteAccessWoocommerce($db, $site) { + global $langs; + + $langs->load("ecommerce@ecommerceng"); + $langs->load("woocommerce@ecommerceng"); + $this->db = $db; $this->site = $site; + $this->errors = []; + + $this->gmtTimeZone = new DateTimeZone('GMT'); + $this->currentTimeZone = new DateTimeZone(date_default_timezone_get()); + return 1; } @@ -66,18 +141,13 @@ function eCommerceRemoteAccessWoocommerce($db, $site) */ public function connect() { - global $conf; - - try { - require_once(DOL_DOCUMENT_ROOT.'/core/lib/functions2.lib.php'); - $params=getSoapParams(); - - @ini_set('default_socket_timeout', $params['response_timeout']); - @ini_set("memory_limit", "1024M"); + dol_syslog(__METHOD__ . ": Connect to API webservice_address=" . $this->site->webservice_address . " user_name=" . + $this->site->user_name . " user_password=" . $this->site->user_password . " for site ID {$this->site->id}", LOG_DEBUG); + global $conf, $langs; - $response_timeout = (empty($conf->global->MAIN_USE_RESPONSE_TIMEOUT)?$params['response_timeout']:$conf->global->MAIN_USE_RESPONSE_TIMEOUT); // Response timeout + $response_timeout = (empty($conf->global->MAIN_USE_RESPONSE_TIMEOUT) ? 30 : $conf->global->MAIN_USE_RESPONSE_TIMEOUT); // Response timeout - dol_syslog("eCommerceRemoteAccessWoocommerce Connect to API webservice_address=".$this->site->webservice_address." user_name=".$this->site->user_name." user_password=".$this->site->user_password); + try { $this->client = new Client( $this->site->webservice_address, $this->site->user_name, @@ -88,6 +158,8 @@ public function connect() 'timeout' => $response_timeout, ] ); + $this->client->get('customers', [ 'page' => 1, 'per_page' => 1 ]); + $this->clientOld = new Client( $this->site->webservice_address, $this->site->user_name, @@ -97,42 +169,60 @@ public function connect() 'timeout' => $response_timeout, ] ); - - dol_syslog("eCommerceRemoteAccessWoocommerce connected with new Client ok."); - - return true; + $this->clientOld->get('customers', [ 'page' => 1, 'filter' => [ 'limit' => 1 ] ]); } catch (HttpClientException $fault) { - $this->errors[]=$fault->getMessage().'-'.$fault->getCode(); - dol_syslog(__METHOD__.': '.$fault->getMessage().'-'.$fault->getCode().'-'.$fault->getTraceAsString(), LOG_WARNING); + $this->errors[] = $langs->trans('ECommerceWoocommerceConnect', $this->site->name, $fault->getCode() . ': ' . $fault->getMessage()); + dol_syslog(__METHOD__ . + ': Error:' . $langs->transnoentitiesnoconv('ECommerceWoocommerceConnect', $this->site->name, $fault->getCode() . ': ' . $fault->getMessage()) . + ' - Request:' . json_encode($fault->getRequest()) . ' - Response:' . json_encode($fault->getResponse()), LOG_ERR); + return false; + } + + try { + $this->worpressclient = new WordPressClient( + $this->site->webservice_address, + $this->site->oauth_id, + $this->site->oauth_secret, + dol_buildpath('/custom/ecommerceng/core/modules/oauth/wordpress_oauthcallback.php', 2) . '?ecommerce_id=' . $this->site->id + ); + } catch (Exception $e) { + $this->errors[] = $langs->trans('ECommerceWoocommerceConnect', $this->site->name, $e->getMessage()); + dol_syslog(__METHOD__ . ': Error:' . $langs->transnoentitiesnoconv('ECommerceWoocommerceConnect', $this->site->name, $e->getMessage()), LOG_ERR); return false; } + + dol_syslog(__METHOD__ . ": end, ok", LOG_DEBUG); + return true; } /** - * Call Woocommerce API to get last updated companies + * Call Woocommerce API to get last updated companies. We are interested here by list of id only. We will retreive properties later. + * + * @param int $fromDate From date + * @param int $toDate To date * - * @param datetime $fromDate From date - * @param datetime $toDate To date - * @return boolean|mixed Response from REST Api call, normally an associative array mirroring the structure of the XML response, nothing if error + * @return array|boolean List of companies ID to update or false if error */ public function getSocieteToUpdate($fromDate, $toDate) { - global $conf; + dol_syslog(__METHOD__ . ": start gt = " . (!empty($fromDate) ? dol_print_date($fromDate, 'standard') : 'none') . + ", lt = " . (!empty($toDate) ? dol_print_date($toDate, 'standard') : 'none') . " for site ID {$this->site->id}", LOG_DEBUG); + global $conf, $langs; - try { - $result = array(); - $idxPage = 1; - $per_page = empty($conf->global->ECOMMERCENG_MAXSIZE_MULTICALL) ? 100 : $conf->global->ECOMMERCENG_MAXSIZE_MULTICALL; - $from_date = isset($fromDate) && !empty($fromDate) ? new DateTime(dol_print_date($fromDate, 'standard')) : null; - $to_date = isset($toDate) && !empty($toDate) ? new DateTime(dol_print_date($toDate, 'standard')) : null; - - $filter = [ 'limit' => $per_page ]; - // Not work with customers - //if (isset($fromDate) && !empty($fromDate)) $filter['updated_at_min'] = dol_print_date($fromDate - (24 * 60 * 60), 'dayrfc'); - //if (isset($toDate) && !empty($toDate)) $filter['updated_at_max'] = dol_print_date($toDate + (24 * 60 * 60), 'dayrfc'); - - dol_syslog("getSocieteToUpdate start gt = " . dol_print_date($fromDate, 'standard') . ", lt = " . dol_print_date($toDate, 'standard')); - while (true) { + $last_update = []; + $result = []; + $idxPage = 1; + $per_page = empty($conf->global->ECOMMERCENG_MAXSIZE_MULTICALL) ? 100 : min($conf->global->ECOMMERCENG_MAXSIZE_MULTICALL, 100); + $from_date = isset($fromDate) && !empty($fromDate) ? new DateTime(dol_print_date($fromDate, 'standard')) : null; + $to_date = isset($toDate) && !empty($toDate) ? new DateTime(dol_print_date($toDate, 'standard')) : null; + + $filter = ['limit' => $per_page]; + // Not work with customers + //if (isset($fromDate) && !empty($fromDate)) $filter['updated_at_min'] = dol_print_date($fromDate - (24 * 60 * 60), 'dayrfc'); + //if (isset($toDate) && !empty($toDate)) $filter['updated_at_max'] = dol_print_date($toDate + (24 * 60 * 60), 'dayrfc'); + + while (true) { + try { $page = $this->clientOld->get('customers', [ 'page' => $idxPage++, @@ -140,169 +230,144 @@ public function getSocieteToUpdate($fromDate, $toDate) 'fields' => 'id,created_at,last_update' ] ); - if (!isset($page['customers']) || ($nbCustomers = count($page['customers'])) == 0) break; - $page = $page['customers']; + } catch (HttpClientException $fault) { + $this->errors[] = $langs->trans('ECommerceWoocommerceGetSocieteToUpdate', $this->site->name, $fault->getCode() . ': ' . $fault->getMessage()); + dol_syslog(__METHOD__ . + ': Error:' . $langs->transnoentitiesnoconv('ECommerceWoocommerceGetSocieteToUpdate', $this->site->name, $fault->getCode() . ': ' . $fault->getMessage()) . + ' - Request:' . json_encode($fault->getRequest()) . ' - Response:' . json_encode($fault->getResponse()), LOG_ERR); + return false; + } - for ($idxCustomer = 0; $idxCustomer < $nbCustomers; $idxCustomer++) { - $created_at = new DateTime($page[$idxCustomer]['created_at']); - $date = new DateTime($page[$idxCustomer]['last_update']); - $date = $date < $created_at ? $created_at : $date; + if (!isset($page['customers']) || ($nbCustomers = count($page['customers'])) == 0) break; + $page = $page['customers']; - if ((!isset($from_date) || $from_date < $date) && (!isset($to_date) || $date <= $to_date)) { - $result[] = $page[$idxCustomer]['id']; - } + foreach ($page as $customer) { + $date = $this->getDateTimeFromGMTDateTime(!empty($customer['updated_at']) ? $customer['updated_at'] : $customer['created_at']); + + if ((!isset($from_date) || $from_date < $date) && (!isset($to_date) || $date <= $to_date)) { + $id = $customer['id']; + $result[$id] = $id; + $last_update[$id] = $date->format('Y-m-d H:i:s'); } } + } - dol_syslog("getSocieteToUpdate end (found ".count($result)." record)"); - return $result; - } catch (HttpClientException $fault) { - $this->errors[]=$fault->getMessage().'-'.$fault->getCode(); - dol_syslog(__METHOD__.': '.$fault->getMessage().'-'.$fault->getCode().'-'.$fault->getTraceAsString(), LOG_WARNING); - return false; + //important - order by last update + if (count($result)) { + array_multisort($last_update, SORT_ASC, $result); } + + dol_syslog(__METHOD__ . ": end", LOG_DEBUG); + return $result; } /** - * Call Magenta API to get last updated products. We are interested here by list of id only. We will retreive properties later. + * Call Woocommerce API to get last updated products. We are interested here by list of id only. We will retreive properties later. + * + * @param int $fromDate From date + * @param int $toDate To date * - * @param datetime $fromDate From date - * @param datetime $toDate To date - * @return boolean|mixed Response from SOAP call, normally an associative array mirroring the structure of the XML response, nothing if error + * @return array|boolean List of products ID to update or false if error */ public function getProductToUpdate($fromDate, $toDate) { - global $conf; + dol_syslog(__METHOD__ . ": start gt = " . (!empty($fromDate) ? dol_print_date($fromDate, 'standard') : 'none') . + ", lt = " . (!empty($toDate) ? dol_print_date($toDate, 'standard') : 'none') . " for site ID {$this->site->id}", LOG_DEBUG); + global $conf, $langs; - try { - dol_syslog("getProductToUpdate start gt=".dol_print_date($fromDate, 'standard')." lt=".dol_print_date($toDate, 'standard')); - $result = array(); - $idxPage = 1; - $per_page = empty($conf->global->ECOMMERCENG_MAXSIZE_MULTICALL) ? 100 : $conf->global->ECOMMERCENG_MAXSIZE_MULTICALL; - $from_date = isset($fromDate) && !empty($fromDate) ? new DateTime(dol_print_date($fromDate, 'standard')) : null; - $to_date = isset($toDate) && !empty($toDate) ? new DateTime(dol_print_date($toDate, 'standard')) : null; - - $filter = [ 'limit' => $per_page ]; - if (isset($fromDate) && !empty($fromDate)) $filter['updated_at_min'] = dol_print_date($fromDate - (24 * 60 * 60), 'dayrfc'); - if (isset($toDate) && !empty($toDate)) $filter['updated_at_max'] = dol_print_date($toDate + (24 * 60 * 60), 'dayrfc'); - - dol_syslog("getProductToUpdate start gt=".dol_print_date($fromDate != null ? $fromDate : 0, 'standard')." lt=".dol_print_date($toDate, 'standard')); - while (true) { + $last_update = []; + $result = []; + $idxPage = 1; + $per_page = empty($conf->global->ECOMMERCENG_MAXSIZE_MULTICALL) ? 100 : min($conf->global->ECOMMERCENG_MAXSIZE_MULTICALL, 100); + $from_date = isset($fromDate) && !empty($fromDate) ? new DateTime(dol_print_date($fromDate, 'standard')) : null; + $to_date = isset($toDate) && !empty($toDate) ? new DateTime(dol_print_date($toDate, 'standard')) : null; + + $filter = ['limit' => $per_page]; + if (isset($fromDate) && !empty($fromDate)) $filter['updated_at_min'] = dol_print_date($fromDate - (24 * 60 * 60), 'dayrfc'); + if (isset($toDate) && !empty($toDate)) $filter['updated_at_max'] = dol_print_date($toDate + (24 * 60 * 60), 'dayrfc'); + + while (true) { + try { $page = $this->clientOld->get('products', [ 'page' => $idxPage++, 'filter' => $filter, - 'fields' => 'id,created_at,updated_at' + 'fields' => 'id,created_at,updated_at,variations' ] ); - if (!isset($page['products']) || ($nbProducts = count($page['products'])) == 0) break; - $page = $page['products']; - - for ($idxProduct = 0; $idxProduct < $nbProducts; $idxProduct++) { - $created_at = new DateTime($page[$idxProduct]['created_at']); - $date = new DateTime($page[$idxProduct]['updated_at']); - $date = $date < $created_at ? $created_at : $date; - - if ((!isset($from_date) || $from_date < $date) && (!isset($to_date) || $date <= $to_date)) { - $product = $page[$idxProduct]; - //if ($product['virtual'] || $product['downloadable']) continue; - $result[] = $product['id']; - } - } + } catch (HttpClientException $fault) { + $this->errors[] = $langs->trans('ECommerceWoocommerceGetProductToUpdate', $this->site->name, $fault->getCode() . ': ' . $fault->getMessage()); + dol_syslog(__METHOD__ . + ': Error:' . $langs->transnoentitiesnoconv('ECommerceWoocommerceGetProductToUpdate', $this->site->name, $fault->getCode() . ': ' . $fault->getMessage()) . + ' - Request:' . json_encode($fault->getRequest()) . ' - Response:' . json_encode($fault->getResponse()), LOG_ERR); + return false; } - dol_syslog("getProductToUpdate end (found ".count($results)." record)"); - return $result; - } catch (HttpClientException $fault) { - $this->errors[]=$fault->getMessage().'-'.$fault->getCode(); - dol_syslog(__METHOD__.': '.$fault->getMessage().'-'.$fault->getCode().'-'.$fault->getTraceAsString(), LOG_WARNING); - return false; - } - } - - /** - * Call Magenta API to get last updated orders - * - * @param datetime $fromDate From date - * @param datetime $toDate To date - * @return boolean|mixed Response from SOAP call, normally an associative array mirroring the structure of the XML response, nothing if error - */ - public function getCommandeToUpdate($fromDate, $toDate) - { - global $conf; + if (!isset($page['products']) || ($nbProducts = count($page['products'])) == 0) break; + $page = $page['products']; - try { - $result = array(); - $idxPage = 1; - $per_page = empty($conf->global->ECOMMERCENG_MAXSIZE_MULTICALL) ? 100 : $conf->global->ECOMMERCENG_MAXSIZE_MULTICALL; - $from_date = isset($fromDate) && !empty($fromDate) ? new DateTime(dol_print_date($fromDate, 'standard')) : null; - $to_date = isset($toDate) && !empty($toDate) ? new DateTime(dol_print_date($toDate, 'standard')) : null; - - $filter = [ 'limit' => $per_page ]; - if (isset($fromDate) && !empty($fromDate)) $filter['updated_at_min'] = dol_print_date($fromDate - (24 * 60 * 60), 'dayrfc'); - if (isset($toDate) && !empty($toDate)) $filter['updated_at_max'] = dol_print_date($toDate + (24 * 60 * 60), 'dayrfc'); - - dol_syslog("getCommandeToUpdate start gt=".dol_print_date($fromDate, 'standard')." lt=".dol_print_date($toDate, 'standard')); - while (true) { - $page = $this->clientOld->get('orders', - [ - 'page' => $idxPage++, - 'filter' => $filter, - 'fields' => 'id,created_at,updated_at' - ] - ); - if (!isset($page['orders']) || ($nbOrders = count($page['orders'])) == 0) break; - $page = $page['orders']; + foreach ($page as $product) { + $update = false; + $date_product = $this->getDateTimeFromGMTDateTime(!empty($product['updated_at']) ? $product['updated_at'] : $product['created_at']); - for ($idxOrder = 0; $idxOrder < $nbOrders; $idxOrder++) { - $created_at = new DateTime($page[$idxOrder]['created_at']); - $date = new DateTime($page[$idxOrder]['updated_at']); - $date = $date < $created_at ? $created_at : $date; + // Product + if ((!isset($from_date) || $from_date < $date_product) && (!isset($to_date) || $date_product <= $to_date)) { + $id = $product['id']; + $result[$id] = $id; + $last_update[$id] = $date_product->format('Y-m-d H:i:s'); + } - if ((!isset($from_date) || $from_date < $date) && (!isset($to_date) || $date <= $to_date)) { - $result[] = $page[$idxOrder]['id']; + // Variations + if (!$update) { + foreach ($product['variations'] as $variation) { + $date_variation = $this->getDateTimeFromGMTDateTime(!empty($variation['updated_at']) ? $variation['updated_at'] : $variation['created_at']); + + if ((!isset($from_date) || $from_date < $date_variation) && (!isset($to_date) || $date_variation <= $to_date)) { + $id = $product['id'].'|'.$variation['id']; + $result[$id] = $id; + $last_update[$id] = $date_variation->format('Y-m-d H:i:s'); + } } } } - - dol_syslog("getCommandeToUpdate end (found ".count($result)." record)"); - return $result; - } catch (HttpClientException $fault) { - $this->errors[]=$fault->getMessage().'-'.$fault->getCode(); - dol_syslog(__METHOD__.': '.$fault->getMessage().'-'.$fault->getCode().'-'.$fault->getTraceAsString(), LOG_WARNING); - return false; } - catch (Exception $e) { - $this->errors[]=$e->getMessage().'-'.$e->getCode(); - dol_syslog(__METHOD__.': '.$e->getMessage().'-'.$e->getCode().'-'.$e->getTraceAsString(), LOG_WARNING); - return false; + + //important - order by last update + if (count($result)) { + array_multisort($last_update, SORT_ASC, $result); } + + dol_syslog(__METHOD__ . ": end", LOG_DEBUG); + return $result; } /** - * Call Magenta API to get last updated invoices + * Call Woocommerce API to get last updated orders. We are interested here by list of id only. We will retreive properties later. + * + * @param int $fromDate From date + * @param int $toDate To date * - * @param datetime $fromDate From date - * @param datetime $toDate To date - * @return boolean|mixed Response from SOAP call, normally an associative array mirroring the structure of the XML response, nothing if error + * @return array|boolean List of orders ID to update or false if error */ - public function getFactureToUpdate($fromDate, $toDate) + public function getCommandeToUpdate($fromDate, $toDate) { - /* global $conf; + dol_syslog(__METHOD__ . ": start gt = " . (!empty($fromDate) ? dol_print_date($fromDate, 'standard') : 'none') . + ", lt = " . (!empty($toDate) ? dol_print_date($toDate, 'standard') : 'none') . " for site ID {$this->site->id}", LOG_DEBUG); + global $conf, $langs; - try { - $result = array(); - $idxPage = 1; - $per_page = empty($conf->global->ECOMMERCENG_MAXSIZE_MULTICALL) ? 100 : $conf->global->ECOMMERCENG_MAXSIZE_MULTICALL; - $from_date = isset($fromDate) && !empty($fromDate) ? new DateTime(dol_print_date($fromDate, 'standard')) : null; - $to_date = isset($toDate) && !empty($toDate) ? new DateTime(dol_print_date($toDate, 'standard')) : null; - - $filter = [ 'limit' => $per_page ]; - if (isset($fromDate) && !empty($fromDate)) $filter['updated_at_min'] = dol_print_date($fromDate - (24 * 60 * 60), 'dayrfc'); - if (isset($toDate) && !empty($toDate)) $filter['updated_at_max'] = dol_print_date($toDate + (24 * 60 * 60), 'dayrfc'); - - dol_syslog("getFactureToUpdate start gt=".dol_print_date($fromDate, 'standard')." lt=".dol_print_date($toDate, 'standard')); - while (true) { + $last_update = []; + $result = []; + $idxPage = 1; + $per_page = empty($conf->global->ECOMMERCENG_MAXSIZE_MULTICALL) ? 100 : min($conf->global->ECOMMERCENG_MAXSIZE_MULTICALL, 100); + $from_date = isset($fromDate) && !empty($fromDate) ? new DateTime(dol_print_date($fromDate, 'standard')) : null; + $to_date = isset($toDate) && !empty($toDate) ? new DateTime(dol_print_date($toDate, 'standard')) : null; + + $filter = ['limit' => $per_page]; + if (isset($fromDate) && !empty($fromDate)) $filter['updated_at_min'] = dol_print_date($fromDate - (24 * 60 * 60), 'dayrfc'); + if (isset($toDate) && !empty($toDate)) $filter['updated_at_max'] = dol_print_date($toDate + (24 * 60 * 60), 'dayrfc'); + + while (true) { + try { $page = $this->clientOld->get('orders', [ 'page' => $idxPage++, @@ -310,1681 +375,1996 @@ public function getFactureToUpdate($fromDate, $toDate) 'fields' => 'id,created_at,updated_at' ] ); - if (!isset($page['orders']) || ($nbOrders = count($page['orders'])) == 0) break; - $page = $page['orders']; - - for ($idxOrder = 0; $idxOrder < $nbOrders; $idxOrder++) { - $created_at = new DateTime($page[$idxOrder]['created_at']); - $date = new DateTime($page[$idxOrder]['updated_at']); - $date = $date < $created_at ? $created_at : $date; - - if ((!isset($from_date) || $from_date < $date) && (!isset($to_date) || $date <= $to_date)) { -// $status = $page[$idxOrder]['status']; -// if ($status == 'completed' || $status == 'refunded') { - $result[] = $page[$idxOrder]['id']; -// } - } - } + } catch (HttpClientException $fault) { + $this->errors[] = $langs->trans('ECommerceWoocommerceGetCommandeToUpdate', $this->site->name, $fault->getCode() . ': ' . $fault->getMessage()); + dol_syslog(__METHOD__ . + ': Error:' . $langs->transnoentitiesnoconv('ECommerceWoocommerceGetCommandeToUpdate', $this->site->name, $fault->getCode() . ': ' . $fault->getMessage()) . + ' - Request:' . json_encode($fault->getRequest()) . ' - Response:' . json_encode($fault->getResponse()), LOG_ERR); + return false; } - dol_syslog("getFactureToUpdate end (found ".count($result)." record)"); - return $result; - } catch (HttpClientException $fault) { - $this->errors[]=$fault->getMessage().'-'.$fault->getCode(); - dol_syslog(__METHOD__.': '.$fault->getMessage().'-'.$fault->getCode().'-'.$fault->getTraceAsString(), LOG_WARNING); - return false; - }*/ - } - - - /** - * Put the remote data into category dolibarr data from instantiated class in the constructor - * Return array of category by update time. - * - * @param array $remoteObject Array of ids of objects to convert - * @param int $toNb Max nb - * @return array societe - */ - public function convertRemoteObjectIntoDolibarrCategory($remoteObject, $toNb=0) - { - global $conf; - - $categories = array(); + if (!isset($page['orders']) || ($nbOrders = count($page['orders'])) == 0) break; + $page = $page['orders']; - // No need to make $this->client->multiCall($this->session, $calls); to get details. + foreach ($page as $order) { + $date = $this->getDateTimeFromGMTDateTime(!empty($order['updated_at']) ? $order['updated_at'] : $order['created_at']); - // We just need to sort array on updated_at - $categories = $remoteObject; + if ((!isset($from_date) || $from_date < $date) && (!isset($to_date) || $date <= $to_date)) { + $id = $order['id']; + $result[$id] = $id; + $last_update[$id] = $date->format('Y-m-d H:i:s'); + } + } + } //important - order by last update - if (count($categories)) - { - $last_update=array(); - foreach ($categories as $key => $row) - { - $last_update[$key] = $row['updated_at']; - } - array_multisort($last_update, SORT_ASC, $categories); + if (count($result)) { + array_multisort($last_update, SORT_ASC, $result); } - return $categories; + dol_syslog(__METHOD__ . ": end", LOG_DEBUG); + return $result; } /** - * Put the remote data into societe dolibarr data from instantiated class in the constructor - * Return array of thirdparty by update time. + * Desactivated because is not supported by woocommerce. * - * @param array $remoteObject Array of ids of objects to convert - * @param int $toNb Max nb - * @return array societe + * @param int $fromDate From date + * @param int $toDate To date + * + * @return array Empty list */ - public function convertRemoteObjectIntoDolibarrSociete($remoteObject, $toNb=0) + public function getFactureToUpdate($fromDate, $toDate) { - global $conf; - - $societes = array(); - - $maxsizeofmulticall = (empty($conf->global->ECOMMERCENG_MAXSIZE_MULTICALL)?100:$conf->global->ECOMMERCENG_MAXSIZE_MULTICALL); - $nbsynchro = 0; - $nbremote = count($remoteObject); - if ($nbremote) - { - // Create n groups of $maxsizeofmulticall records max to call the multiCall - $callsgroup = array(); - $calls = array(); - foreach ($remoteObject as $rsociete) - { - if (($nbsynchro % $maxsizeofmulticall) == 0) - { - if (count($calls)) $callsgroup[] = $calls; // Add new group for lot of 1000 call arrays - $calls = array(); - } - - $calls[] = $rsociete; - - $nbsynchro++; // nbsynchro is now number of calls to do - } - if (count($calls)) $callsgroup[] = $calls; // Add new group for the remain lot of calls not yet added - - dol_syslog("convertRemoteObjectIntoDolibarrSociete Call WS to get detail for the " . count($remoteObject) . " objects (" . count($callsgroup) . " calls with " . $maxsizeofmulticall . " max of records each) then create a Dolibarr array for each object"); - //var_dump($callsgroup);exit; - - $results=array(); - $nbcall=0; - foreach ($callsgroup as $calls) - { - try { - $nbcall++; - dol_syslog("convertRemoteObjectIntoDolibarrSociete Call WS nb ".$nbcall." (".count($calls)." record)"); - $resulttmp = $this->client->get('customers', - [ - 'per_page' => $maxsizeofmulticall, - 'include' => implode(',', $calls), - ] - ); - $results=array_merge($results, $resulttmp); - } catch (HttpClientException $fault) { - dol_syslog('convertRemoteObjectIntoDolibarrSociete :'.$fault->getMessage().'-'.$fault->getCode().'-'.$fault->getTraceAsString(), LOG_WARNING); - return false; - } - } - - if (count($results)) - { - foreach ($results as $societe) - { - $newobj = array( - 'remote_id' => $societe['id'], - 'last_update' => isset($societe['date_modified']) ? $societe['date_modified'] : $societe['date_created'], - 'name' => dolGetFirstLastname($societe['first_name'], $societe['last_name']), - 'name_alias' => $this->site->name . ' id ' . $societe['id'], // See also the delete in eCommerceSociete - 'email' => $societe['email'], - 'client' => 3, //for client/prospect - 'vatnumber' => $societe['taxvat'] - ); - $societes[] = $newobj; - } - } - } - - //important - order by last update - if (count($societes)) - { - $last_update = array(); - foreach ($societes as $key => $row) - { - $last_update[$key] = $row['last_update']; - } - array_multisort($last_update, SORT_ASC, $societes); - } - - dol_syslog("convertRemoteObjectIntoDolibarrSociete end (found ".count($societes)." record)"); - return $societes; + dol_syslog(__METHOD__ . ": Desactivated for site ID {$this->site->id}", LOG_DEBUG); + return []; } - /** - * Put the remote data into societe dolibarr data from instantiated class in the constructor - * Return array of people by update time. + * Call Woocommerce API to get company datas and put into dolibarr company class. * - * @param array $listofids List of object with customer_address_id that is remote id of addresss - * @param int $toNb Max nb. Not used for socpeople. - * @return array societe + * @param array $remoteObject List of id of remote companies to convert + * @param int $toNb Max nb + * @return array|boolean List of companies sorted by update time or false if error. */ - public function convertRemoteObjectIntoDolibarrSocpeople($listofids, $toNb=0) + public function convertRemoteObjectIntoDolibarrSociete($remoteObject, $toNb=0) { - global $conf; + dol_syslog(__METHOD__ . ": Get " . count($remoteObject) . " remote companies ID: " . implode(', ', $remoteObject) . " for site ID {$this->site->id}", LOG_DEBUG); + global $conf, $langs; - $socpeoples = array(); - $calls = array(); - if (count($listofids)) - { - dol_syslog("convertRemoteObjectIntoDolibarrSocpeople Call WS to get detail for the ".count($listofids)." objects then create a Dolibarr array for each object"); - foreach ($listofids as $listofid) - { - $calls[] = $listofid; - } + $companies = []; + $nb_max_by_request = empty($conf->global->ECOMMERCENG_MAXSIZE_MULTICALL) ? 100 : min($conf->global->ECOMMERCENG_MAXSIZE_MULTICALL, 100); + $requestGroups = $this->getRequestGroups($remoteObject, $nb_max_by_request, $toNb); + + foreach ($requestGroups as $request) { + dol_syslog(__METHOD__ . ": Get partial remote companies ID: " . implode(', ', $request), LOG_DEBUG); try { - $results = $this->client->get('customers', + $results = $this->client->get('customers', [ - 'per_page' => 100, - 'include' => implode(',', $calls), + 'per_page' => $nb_max_by_request, + 'include' => implode(',', $request), ] ); } catch (HttpClientException $fault) { - dol_syslog('convertRemoteObjectIntoDolibarrSocpeople :'.$fault->getMessage().'-'.$fault->getCode().'-'.$fault->getTraceAsString(), LOG_WARNING); + $this->errors[] = $langs->trans('ECommerceWoocommerceConvertRemoteObjectIntoDolibarrSociete', $this->site->name, $fault->getCode() . ': ' . $fault->getMessage()); + dol_syslog(__METHOD__ . + ': Error:' . $langs->transnoentitiesnoconv('ECommerceWoocommerceConvertRemoteObjectIntoDolibarrSociete', $this->site->name, $fault->getCode() . ': ' . $fault->getMessage()) . + ' - Request:' . json_encode($fault->getRequest()) . ' - Response:' . json_encode($fault->getResponse()), LOG_ERR); + return false; } - if (count($results)) { - $billingName = (empty($conf->global->ECOMMERCENG_BILLING_CONTACT_NAME) ? 'Billing' : $conf->global->ECOMMERCENG_BILLING_CONTACT_NAME); // Contact name treated as billing address. - $shippingName = (empty($conf->global->ECOMMERCENG_SHIPPING_CONTACT_NAME) ? 'Shipping' : $conf->global->ECOMMERCENG_SHIPPING_CONTACT_NAME); // Contact name treated as shipping address. - - foreach ($results as $socpeople) { - $billing = $socpeople['billing']; - $newobj = array( - 'remote_id' => $socpeople['id'] . '|1', - //'type' => eCommerceSocpeople::CONTACT_TYPE_COMPANY, - 'last_update' => isset($socpeople['date_modified']) ? $socpeople['date_modified'] : $socpeople['date_created'], - 'name' => $billingName, - 'email' => $billing['email'], - 'address' => $billing['address_1'] . (!empty($billing['address_1']) && !empty($billing['address_2']) ? "\n" : "") . $billing['address_2'], - 'town' => $billing['city'], - 'zip' => $billing['postcode'], - 'country_code' => getCountry($billing['country'], 3), - 'phone' => $billing['phone'], - 'fax' => "", - 'firstname' => "", // $billing['first_name'], - 'lastname' => $billingName, // $billing['last_name'], - 'vatnumber' => "", - 'is_default_billing' => true, - 'is_default_shipping' => false - ); - $socpeoples[] = $newobj; - - $shipping = $socpeople['shipping']; - if ((!empty($shipping['address_1']) || !empty($shipping['address_1'])) && !empty($shipping['city']) && !empty($shipping['postcode']) && !empty($shipping['country'])) { - $newobj = array( - 'remote_id' => $socpeople['id'] . '|2', - //'type' => eCommerceSocpeople::CONTACT_TYPE_COMPANY, - 'last_update' => isset($socpeople['date_modified']) ? $socpeople['date_modified'] : $socpeople['date_created'], - 'name' => $shippingName, - 'email' => "", - 'address' => $shipping['address_1'] . (!empty($shipping['address_1']) && !empty($shipping['address_2']) ? "\n" : "") . $shipping['address_2'], - 'town' => $shipping['city'], - 'zip' => $shipping['postcode'], - 'country_code' => getCountry($shipping['country'], 3), - 'phone' => "", - 'fax' => "", - 'firstname' => "", // $shipping['first_name'], - 'lastname' => $shippingName, // $shipping['last_name'], - 'vatnumber' => "", - 'is_default_billing' => false, - 'is_default_shipping' => true - ); - } else { - $newobj = array( - 'remote_id' => $socpeople['id'] . '|2', - //'type' => eCommerceSocpeople::CONTACT_TYPE_COMPANY, - 'last_update' => isset($socpeople['date_modified']) ? $socpeople['date_modified'] : $socpeople['date_created'], - 'name' => $shippingName, - 'email' => $billing['email'], - 'address' => $billing['address_1'] . (!empty($billing['address_1']) && !empty($billing['address_2']) ? "\n" : "") . $billing['address_2'], - 'town' => $billing['city'], - 'zip' => $billing['postcode'], - 'country_code' => getCountry($billing['country'], 3), - 'phone' => "", - 'fax' => "", - 'firstname' => "", // $billing['first_name'], - 'lastname' => $shippingName, // $billing['last_name'], - 'vatnumber' => "", - 'is_default_billing' => false, - 'is_default_shipping' => true - ); + if (is_array($results)) { + foreach ($results as $company) { + $last_update = $this->getDateTimeFromGMTDateTime(!empty($company['date_modified_gmt']) ? $company['date_modified_gmt'] : $company['date_created_gmt']); + + // Company + if (!empty($company['billing']['company'])) { + $companies[] = [ + 'remote_id' => $company['id'], + 'last_update' => $last_update->format('Y-m-d H:i:s'), + 'type' => 'company', + 'name' => $company['billing']['company'], + 'name_alias' => null, + 'email' => null, + 'email_key' => $company['email'], + 'client' => 1, + 'vatnumber' => null, + 'note_private' => "Site: '{$this->site->name}' - ID: {$company['id']}", + 'country_id' => getCountry($company['billing']['country'], 3), + 'remote_datas' => $company, + ]; + } + // User + else { + $firstname = !empty($company['first_name']) ? $company['first_name'] : $company['billing']['first_name']; + $lastname = !empty($company['last_name']) ? $company['last_name'] : $company['billing']['last_name']; + if (!empty($firstname) && !empty($lastname)) { + $name = dolGetFirstLastname($firstname, $lastname); + } elseif (!empty($firstname)) { + $name = dolGetFirstLastname($firstname, $langs->trans("ECommercengWoocommerceLastnameNotInformed")); + } else { + $name = $langs->trans('ECommercengWoocommerceWithoutFirstnameLastname'); + } + $companies[] = [ + 'remote_id' => $company['id'], + 'last_update' => $last_update->format('Y-m-d H:i:s'), + 'type' => 'user', + 'name' => $name, + 'name_alias' => null, + 'email' => $company['email'], + 'email_key' => $company['email'], + 'client' => 1, + 'vatnumber' => null, + 'note_private' => "Site: '{$this->site->name}' - ID: {$company['id']}", + 'country_id' => getCountry($company['billing']['country'], 3), + 'remote_datas' => $company, + ]; } - $socpeoples[] = $newobj; } } } //important - order by last update - if (count($socpeoples)) - { - $last_update = array(); - foreach ($socpeoples as $key => $row) - { + if (count($companies)) { + $last_update = []; + foreach ($companies as $key => $row) { $last_update[$key] = $row['last_update']; } - array_multisort($last_update, SORT_ASC, $socpeoples); + array_multisort($last_update, SORT_ASC, $companies); } - dol_syslog("convertRemoteObjectIntoDolibarrSocPeople end (found ".count($socpeoples)." record)"); - return $socpeoples; + dol_syslog(__METHOD__ . ": end, converted " . count($companies) . " remote companies", LOG_DEBUG); + return $companies; } - /** - * Put the remote data into product dolibarr data from instantiated class in the constructor - * Return array or products by update time. + * Call Woocommerce API to get contact datas and put into dolibarr contact class. * - * @param array $remoteObject Array of remote products (got by caller from getProductToUpdate. Only few properties defined) - * @param int $toNb Max nb - * @return array product + * @param array $remoteCompany Remote company infos + * @return array|boolean List of contact sorted by update time or false if error. */ - public function convertRemoteObjectIntoDolibarrProduct($remoteObject, $toNb=0) + public function convertRemoteObjectIntoDolibarrSocpeople($remoteCompany) { - global $conf; - - include_once DOL_DOCUMENT_ROOT.'/core/lib/company.lib.php'; - - $products = array(); - - $canvas = ''; - - $ecommerceurl = $this->site->getFrontUrl(); + dol_syslog(__METHOD__ . ": Get remote contacts ID: {$remoteCompany["id"]} for site ID {$this->site->id}", LOG_DEBUG); + global $langs; + + $contacts = []; + $last_update = $this->getDateTimeFromGMTDateTime(!empty($remoteCompany['date_modified_gmt']) ? $remoteCompany['date_modified_gmt'] : $remoteCompany['date_created_gmt']); + + $bContact = $remoteCompany['billing']; + if (!empty($bContact['address_1']) || !empty($bContact['address_2']) || !empty($bContact['postcode']) || + !empty($bContact['city']) || !empty($bContact['country']) || + !empty($bContact['email']) || !empty($bContact['phone']) + ) { + $firstname = !empty($bContact['first_name']) ? $bContact['first_name'] : $remoteCompany['first_name']; + $lastname = !empty($bContact['last_name']) ? $bContact['last_name'] : $remoteCompany['last_name']; + if (!empty($firstname) && empty($lastname)) { + $lastname = $langs->trans("ECommercengWoocommerceLastnameNotInformed"); + } elseif (empty($firstname) && empty($lastname)) { + $lastname = $langs->trans('ECommercengWoocommerceWithoutFirstnameLastname'); + } + $contacts[] = [ + 'remote_id' => null, + 'last_update' => $last_update->format('Y-m-d H:i:s'), + 'firstname' => $firstname, + 'lastname' => $lastname, + 'address' => $bContact['address_1'] . (!empty($bContact['address_1']) && !empty($bContact['address_2']) ? "\n" : "") . $bContact['address_2'], + 'zip' => $bContact['postcode'], + 'town' => $bContact['city'], + 'country_id' => getCountry($bContact['country'], 3), + 'email' => !empty($bContact['email']) ? $bContact['email'] : $remoteCompany['email'], + 'phone' => $bContact['phone'], + 'fax' => null, + ]; + } - $maxsizeofmulticall = (empty($conf->global->ECOMMERCENG_MAXSIZE_MULTICALL)?100:$conf->global->ECOMMERCENG_MAXSIZE_MULTICALL); // 1000 seems ok for multicall. - $nbsynchro = 0; - $nbremote = count($remoteObject); - if ($nbremote) - { - // Create n groups of $maxsizeofmulticall records max to call the multiCall - $callsgroup = array(); - $calls=array(); - foreach ($remoteObject as $rproduct) - { - if (($nbsynchro % $maxsizeofmulticall) == 0) - { - if (count($calls)) $callsgroup[]=$calls; // Add new group for lot of 1000 call arrays - $calls=array(); + $sContact = $remoteCompany['shipping']; + if (!empty($sContact['address_1']) || !empty($sContact['address_2']) || + !empty($sContact['postcode']) || !empty($sContact['city']) || + !empty($sContact['country']) + ) { + if ($bContact['first_name'] != $sContact['first_name'] || $bContact['last_name'] != $sContact['last_name'] || + $bContact['address_1'] != $sContact['address_1'] || $bContact['address_2'] != $sContact['address_2'] || + $bContact['postcode'] != $sContact['postcode'] || $bContact['city'] != $sContact['city'] || + $bContact['country'] != $sContact['country'] + ) { + $firstname = !empty($sContact['first_name']) ? $sContact['first_name'] : $remoteCompany['first_name']; + $lastname = !empty($sContact['last_name']) ? $sContact['last_name'] : $remoteCompany['last_name']; + if (!empty($firstname) && empty($lastname)) { + $lastname = $langs->trans("ECommercengWoocommerceLastnameNotInformed"); + } elseif (empty($firstname) && empty($lastname)) { + $lastname = $langs->trans('ECommercengWoocommerceWithoutFirstnameLastname'); } + $contacts[] = [ + 'remote_id' => null, + 'last_update' => $last_update->format('Y-m-d H:i:s'), + 'firstname' => $firstname, + 'lastname' => $lastname, + 'address' => $sContact['address_1'] . (!empty($sContact['address_1']) && !empty($sContact['address_2']) ? "\n" : "") . $sContact['address_2'], + 'zip' => $sContact['postcode'], + 'town' => $sContact['city'], + 'country_id' => getCountry($sContact['country'], 3), + 'email' => null, + 'phone' => null, + 'fax' => null, + ]; + } + } + + dol_syslog(__METHOD__ . ": end, converted " . count($contacts) . " remote contacts", LOG_DEBUG); + return $contacts; + } - $calls[] = $rproduct; + /** + * Call Woocommerce API to get product datas and put into dolibarr product class. + * + * @param array $remoteObject List of id of remote products to convert + * @param int $toNb Max nb + * @return array|boolean List of products sorted by update time or false if error. + */ + public function convertRemoteObjectIntoDolibarrProduct($remoteObject, $toNb=0) + { + dol_syslog(__METHOD__ . ": Get " . count($remoteObject) . " remote products ID: " . implode(', ', $remoteObject) . " for site ID {$this->site->id}", LOG_DEBUG); + global $conf, $langs; - $nbsynchro++; // nbsynchro is now number of calls to do + $canvas = ''; + $products = []; + $remoteVariationObject = []; + $products_last_update = []; + $nb_max_by_request = empty($conf->global->ECOMMERCENG_MAXSIZE_MULTICALL) ? 100 : min($conf->global->ECOMMERCENG_MAXSIZE_MULTICALL, 100); + + // Products + $newRemoteObject = []; + $remoteObject = array_slice($remoteObject, 0, $toNb); + foreach ($remoteObject as $id) { + if (($pos = strpos($id, '|')) !== false) { + $variation_id = substr($id,$pos+1); + $id = substr($id,0,$pos); + if (!isset($remoteVariationObject[$id])) $remoteVariationObject[$id] = []; + $remoteVariationObject[$id][] = $variation_id; + } + $newRemoteObject[$id] = $id; + } + $requestGroups = $this->getRequestGroups($newRemoteObject, $nb_max_by_request); + foreach ($requestGroups as $request) { + dol_syslog(__METHOD__ . ": Get ".count($request)." partial remote products ID: " . implode(', ', $request), LOG_DEBUG); + try { + $results = $this->client->get('products', + [ + 'per_page' => $nb_max_by_request, + 'include' => implode(',', $request), + ] + ); + } catch (HttpClientException $fault) { + $this->errors[] = $langs->trans('ECommerceWoocommerceConvertRemoteObjectIntoDolibarrProduct', $this->site->name, $fault->getCode() . ': ' . $fault->getMessage()); + dol_syslog(__METHOD__ . + ': Error:' . $langs->transnoentitiesnoconv('ECommerceWoocommerceConvertRemoteObjectIntoDolibarrProduct', $this->site->name, $fault->getCode() . ': ' . $fault->getMessage()) . + ' - Request:' . json_encode($fault->getRequest()) . ' - Response:' . json_encode($fault->getResponse()), LOG_ERR); + return false; } - if (count($calls)) $callsgroup[]=$calls; // Add new group for the remain lot of calls not yet added - dol_syslog("convertRemoteObjectIntoDolibarrProduct Call WS to get detail for the ".count($remoteObject)." objects (".count($callsgroup)." calls with ".$maxsizeofmulticall." max of records each) then create a Dolibarr array for each object"); - //var_dump($callsgroup);exit; + if (is_array($results)) { + foreach ($results as $product) { + // Categories + $categories = []; + foreach ($product['categories'] as $category) { + $categories[] = $category['id']; + } - $results=array(); - $nbcall=0; - foreach ($callsgroup as $calls) - { - try { - $nbcall++; - dol_syslog("convertRemoteObjectIntoDolibarrProduct Call WS nb ".$nbcall." (".count($calls)." record)"); - $resulttmp = $this->client->get('products', - [ - 'per_page' => $maxsizeofmulticall, - 'include' => implode(',', $calls), - ] - ); - $results=array_merge($results, $resulttmp); - } catch (HttpClientException $fault) { - dol_syslog('convertRemoteObjectIntoDolibarrProduct :'.$fault->getMessage().'-'.$fault->getCode().'-'.$fault->getTraceAsString(), LOG_WARNING); + // Images + $images = []; + if (!empty($conf->global->ECOMMERCENG_ENABLE_SYNCHRO_IMAGES)) { + foreach ($product['images'] as $image) { + $last_update = $this->getDateTimeFromGMTDateTime(!empty($image['date_modified_gmt']) ? $image['date_modified_gmt'] : $image['date_created_gmt']); + $images[] = [ + 'url' => $image['src'], + 'date_modified' => $last_update->format('Y-m-d H:i:s'), + ]; + } + array_reverse($images); + } - return false; - } - } + $last_update_product = $this->getDateTimeFromGMTDateTime(!empty($product['date_modified_gmt']) ? $product['date_modified_gmt'] : $product['date_created_gmt']); + + $remote_id = $product['id']; // id product + $last_update = $last_update_product->format('Y-m-d H:i:s'); + + // Produit de base + if (in_array($remote_id, $remoteObject, true)) { + $products_last_update[$remote_id] = $last_update; + $products[$remote_id] = [ + 'remote_id' => $remote_id, + 'last_update' => $last_update, + 'fk_product_type' => ($product['virtual'] ? 1 : 0), // 0 (product) or 1 (service) + 'ref' => $product['sku'], + 'label' => $product['name'], + 'weight' => $product['weight'], + 'price' => $product['price'], + 'envente' => empty($product['variations']) ? 1 : 0, + 'enachat' => empty($product['variations']) ? 1 : 0, + 'finished' => 1, // 1 = manufactured, 0 = raw material + 'canvas' => $canvas, + 'categories' => $categories, + 'tax_rate' => $this->getTaxRate($product['tax_class'], $product['tax_status']), + 'price_min' => $product['price'], + 'fk_country' => '', + 'url' => $product['permalink'], + // Stock + 'stock_qty' => $product['stock_quantity'], + 'is_in_stock' => $product['in_stock'], // not used + 'extrafields' => [ + "ecommerceng_wc_status_{$this->site->id}_{$conf->entity}" => $product['status'], + "ecommerceng_description_{$conf->entity}" => $product['description'], + "ecommerceng_short_description_{$conf->entity}" => $product['short_description'], + "ecommerceng_tax_class_{$this->site->id}_{$conf->entity}" => $this->getTaxClass($product['tax_class'], $product['tax_status']), + ], + 'images' => $images, + ]; + } - if (count($results)) - { - foreach ($results as $cursorproduct => $product) - { // Variations - if (count($product['variations'])) { - try { - $variations = $this->client->get('products/' . $product['id'] . '/variations'); - } catch (HttpClientException $fault) { - $this->errors[] = $fault->getMessage() . '-' . $fault->getCode(); - dol_syslog($fault->getRequest(), LOG_WARNING); - dol_syslog($fault->getResponse(), LOG_WARNING); - dol_syslog(__METHOD__ . ': ' . $fault->getMessage() . '-' . $fault->getCode() . '-' . $fault->getTraceAsString(), LOG_WARNING); - return false; - } + $requestGroupsVariations = $this->getRequestGroups($remoteVariationObject[$product['id']], $nb_max_by_request); + foreach ($requestGroupsVariations as $requestVariations) { + dol_syslog(__METHOD__ . ": Get ".count($requestVariations)." products variations of remote product (ID:{$product['id']}): " . implode(', ', $requestVariations), LOG_DEBUG); + try { + $results = $this->client->get('products/' . $product['id'] . '/variations', + [ + 'per_page' => $nb_max_by_request, + 'include' => implode(',', $requestVariations), + ] + ); + } catch (HttpClientException $fault) { + $this->errors[] = $langs->trans('ECommerceWoocommerceConvertRemoteObjectIntoDolibarrProductVariations', $this->site->name, $fault->getCode() . ': ' . $fault->getMessage()); + dol_syslog(__METHOD__ . + ': Error:' . $langs->transnoentitiesnoconv('ECommerceWoocommerceConvertRemoteObjectIntoDolibarrProductVariations', $this->site->name, $fault->getCode() . ': ' . $fault->getMessage()) . + ' - Request:' . json_encode($fault->getRequest()) . ' - Response:' . json_encode($fault->getResponse()), LOG_ERR); + return false; + } - foreach ($variations as $variation) { + if (is_array($results)) { + foreach ($results as $variation) { $attributesLabel = ''; foreach ($variation['attributes'] as $attribute) { - $attributesLabel .= ', '.$attribute['name'].':'.$attribute['option']; + $attributesLabel .= ', ' . $attribute['name'] . ':' . $attribute['option']; + } + + // Images + $images = []; + if (!empty($conf->global->ECOMMERCENG_ENABLE_SYNCHRO_IMAGES)) { + if (!empty($variation['image'])) { + $last_update = $this->getDateTimeFromGMTDateTime(!empty($variation['image']['date_modified_gmt']) ? $variation['image']['date_modified_gmt'] : $variation['image']['date_created_gmt']); + $images[] = [ + 'url' => $variation['image']['src'], + 'date_modified' => $last_update->format('Y-m-d H:i:s'), + ]; + } } - $products[] = array( - 'remote_id' => $product['id'].'|'.$variation['id'], // id product | id variation - 'last_update' => isset($variation['date_modified'])?$variation['date_modified']:$variation['date_created'], - 'fk_product_type' => ($variation['virtual'] ? 1 : 0), // 0 (product) or 1 (service) - 'ref' => dol_string_nospecial($variation['sku']), - 'label' => ($product['name']?$product['name']:dol_string_nospecial($variation['sku'])).$attributesLabel, - 'description' => $variation['description'], - 'weight' => $variation['weight'], - 'price' => $variation['price'], //sale_price - 'envente' => $variation['purchasable'] ? 1 : 0, - 'finished' => 1, // 1 = manufactured, 0 = raw material - 'canvas' => $canvas, - 'categories' => '', //$product['categories'], // a check // Same as property $product['category_ids'] - 'tax_rate' => '', // $variation['tax_rate'], // a check - 'price_min' => $variation['price'], - 'fk_country' => '', - 'url' => $variation['permalink'], + $last_update_product_variation = $this->getDateTimeFromGMTDateTime(!empty($variation['date_modified_gmt']) ? $variation['date_modified_gmt'] : $variation['date_created_gmt']); + + $remote_id = $product['id'] . '|' . $variation['id']; // id product | id variation + $last_update = $last_update_product_variation->format('Y-m-d H:i:s'); + + // Variation + $products_last_update[$remote_id] = $last_update; + $products[$remote_id] = [ + 'remote_id' => $remote_id, + 'last_update' => $last_update, + 'fk_product_type' => ($variation['virtual'] ? 1 : 0), // 0 (product) or 1 (service) + 'ref' => $variation['sku'], + 'label' => $product['name'] . $attributesLabel, + 'weight' => $variation['weight'], + 'price' => $variation['price'], + 'envente' => 1, + 'enachat' => 1, + 'finished' => 1, // 1 = manufactured, 0 = raw material + 'canvas' => $canvas, + 'categories' => $product['categories'], + 'tax_rate' => $this->getTaxRate($variation['tax_class'], $variation['tax_status']), + 'price_min' => $variation['price'], + 'fk_country' => '', + 'url' => $variation['permalink'], // Stock - 'stock_qty' => $variation['stock_quantity'], - 'is_in_stock' => $variation['in_stock'], // not used - ); + 'stock_qty' => $variation['stock_quantity'], + 'is_in_stock' => $variation['in_stock'], // not used + 'extrafields' => [ + "ecommerceng_description_{$conf->entity}" => $variation['description'], + "ecommerceng_tax_class_{$this->site->id}_{$conf->entity}" => $this->getTaxClass($variation['tax_class'], $variation['tax_status']), + ], + 'images' => $images, + ]; } - } else { - $products[] = array( - 'remote_id' => $product['id'], // id product - 'last_update' => isset($product['date_modified'])?$product['date_modified']:$product['date_created'], - 'fk_product_type' => ($product['virtual'] ? 1 : 0), // 0 (product) or 1 (service) - 'ref' => dol_string_nospecial($product['sku']), - 'label' => ($product['name']?$product['name']:dol_string_nospecial($product['sku'])), - 'description' => $product['description'], - 'weight' => $product['weight'], - 'price' => $product['price'], //sale_price - 'envente' => $product['purchasable'] ? 1 : 0, - 'finished' => 1, // 1 = manufactured, 0 = raw material - 'canvas' => $canvas, - 'categories' => '', // $product['categories'], // a check // Same as property $product['category_ids'] - 'tax_rate' => '', // $product['tax_rate'], // a check - 'price_min' => $product['price'], - 'fk_country' => '', - 'url' => $product['permalink'], - // Stock - 'stock_qty' => $product['stock_quantity'], - 'is_in_stock' => $product['in_stock'], // not used - ); + } } } } } //important - order by last update - if (count($products)) - { - $last_update = array(); - foreach ($products as $key => $row) - { - $last_update[$key] = $row['last_update']; - } - array_multisort($last_update, SORT_ASC, $products); + if (count($products)) { + array_multisort($products_last_update, SORT_ASC, $products); } - dol_syslog("convertRemoteObjectIntoDolibarrProduct end (found ".count($products)." record)"); + dol_syslog(__METHOD__ . ": end, converted " . count($products) . " remote products", LOG_DEBUG); return $products; } /** - * Put the remote data into commande dolibarr data from instantiated class in the constructor - * Return array of orders by update time. + * Call Woocommerce API to get order datas and put into dolibarr order class. * - * @param array $remoteObject array of remote orders - * @param int $toNb Max nb - * @return array commande + * @param array $remoteObject List of id of remote orders to convert + * @param int $toNb Max nb + * @return array|boolean List of orders sorted by update time or false if error. */ public function convertRemoteObjectIntoDolibarrCommande($remoteObject, $toNb=0) { + dol_syslog(__METHOD__ . ": Get " . count($remoteObject) . " remote orders ID: " . implode(', ', $remoteObject) . " for site ID {$this->site->id}", LOG_DEBUG); global $conf, $langs; - $commandes = array(); - - $maxsizeofmulticall = (empty($conf->global->ECOMMERCENG_MAXSIZE_MULTICALL)?100:$conf->global->ECOMMERCENG_MAXSIZE_MULTICALL); // 1000 seems ok for multicall. - $nbsynchro = 0; - $nbremote = count($remoteObject); - if ($nbremote) - { - // Create n groups of $maxsizeofmulticall records max to call the multiCall - $callsgroup = array(); - $calls=array(); - foreach ($remoteObject as $rcommande) - { - if (($nbsynchro % $maxsizeofmulticall) == 0) - { - if (count($calls)) $callsgroup[]=$calls; // Add new group for lot of 1000 call arrays - $calls=array(); - } + $orders = []; + $nb_max_by_request = empty($conf->global->ECOMMERCENG_MAXSIZE_MULTICALL) ? 100 : min($conf->global->ECOMMERCENG_MAXSIZE_MULTICALL, 100); + $requestGroups = $this->getRequestGroups($remoteObject, $nb_max_by_request, $toNb); - $calls[] = $rcommande; - - $nbsynchro++; // nbsynchro is now number of calls to do + foreach ($requestGroups as $request) { + dol_syslog(__METHOD__ . ": Get partial remote orders ID: " . implode(', ', $request), LOG_DEBUG); + try { + $results = $this->client->get('orders', + [ + 'per_page' => $nb_max_by_request, + 'include' => implode(',', $request), + ] + ); + } catch (HttpClientException $fault) { + $this->errors[] = $langs->trans('ECommerceWoocommerceConvertRemoteObjectIntoDolibarrCommande', $this->site->name, $fault->getCode() . ': ' . $fault->getMessage()); + dol_syslog(__METHOD__ . + ': Error:' . $langs->transnoentitiesnoconv('ECommerceWoocommerceConvertRemoteObjectIntoDolibarrCommande', $this->site->name, $fault->getCode() . ': ' . $fault->getMessage()) . + ' - Request:' . json_encode($fault->getRequest()) . ' - Response:' . json_encode($fault->getResponse()), LOG_ERR); + return false; } - if (count($calls)) $callsgroup[]=$calls; // Add new group for the remain lot of calls not yet added - dol_syslog("convertRemoteObjectIntoDolibarrCommande Call WS to get detail for the ".count($remoteObject)." objects (".count($callsgroup)." calls with ".$maxsizeofmulticall." max of records each) then create a Dolibarr array for each object"); - //var_dump($callsgroup);exit; - - $results=array(); - $nbcall=0; - foreach ($callsgroup as $calls) - { - try { - $nbcall++; - dol_syslog("convertRemoteObjectIntoDolibarrCommande Call WS nb ".$nbcall." (".count($calls)." record)"); - $resulttmp = $this->client->get('orders', - [ - 'per_page' => $maxsizeofmulticall, - 'include' => implode(',', $calls), - ] - ); - $results=array_merge($results, $resulttmp); - } catch (HttpClientException $fault) { - dol_syslog('convertRemoteObjectIntoDolibarrCommande :'.$fault->getMessage().'-'.$fault->getCode().'-'.$fault->getTraceAsString(), LOG_WARNING); - return false; - } - } + if (is_array($results)) { + foreach ($results as $order) { + // Set items + $items = []; + foreach ($order['line_items'] as $item) { + $items[] = [ + 'item_id' => $item['id'], + 'id_remote_product' => !empty($item['variation_id']) ? $item['product_id'] . '|' . $item['variation_id'] : $item['product_id'], + 'description' => $item['name'], + 'product_type' => 'simple', + 'price' => $item['price'], + 'qty' => $item['quantity'], + 'tva_tx' => $this->getClosestDolibarrTaxRate($item['total'], $item['total_tax']), + ]; + } - if (count($results)) - { - foreach ($results as $commande) - { - // Process order - dol_syslog("- Process order remote_id=".$commande['order_id']." last_update=".$commande['updated_at']." societe remote_id=".$commande['customer_id']); - - //set each items - $items = array(); - $configurableItems = array(); - if (count($commande['line_items'])) - { - foreach ($commande['line_items'] as $item) - { - $items[] = array( - 'item_id' => $item['id'], - 'id_remote_product' => !empty($item['variation_id']) ? $item['product_id'].'|'.$item['variation_id'] : $item['product_id'], - 'description' => $item['name'], - 'product_type' => 'simple', - 'price' => $item['price'], - 'qty' => $item['quantity'], - 'tva_tx' => $this->getTaxRate($commande['total'], $commande['total_tax']) // tax_class > requete taxes rates - ); - } + // Set remote id company : 0 for anonymous + $eCommerceTempSoc = new eCommerceSociete($this->db); + if (empty($order['customer_id']) || $eCommerceTempSoc->fetchByRemoteId($order['customer_id'], $this->site->id) < 0) { + dol_syslog(__METHOD__ . ": The customer of the remote order ID " . $order['id'] . " was not found into companies table link", LOG_WARNING); + $remoteCompanyID = 0; // If company was not found into companies table link + } else { + $remoteCompanyID = $order['customer_id']; + } - //define remote id societe : 0 for anonymous - $eCommerceTempSoc = new eCommerceSociete($this->db); - if ($commande['customer_id'] == null || $eCommerceTempSoc->fetchByRemoteId($commande['customer_id'], $this->site->id) < 0) - { - dol_syslog("The customer of this order was not found into table link", LOG_WARNING); - $remoteIdSociete = 0; // If thirdparty was not found into thirdparty table link - } - else - { - $remoteIdSociete = $commande['customer_id']; - } + $last_update = $this->getDateTimeFromGMTDateTime(!empty($order['date_modified_gmt']) ? $order['date_modified_gmt'] : $order['date_created_gmt']); - //set order's address - $billingName = (empty($conf->global->ECOMMERCENG_BILLING_CONTACT_NAME)?'Billing':$conf->global->ECOMMERCENG_BILLING_CONTACT_NAME); // Contact name treated as billing address. - $commandeSocpeople = $commande['billing']; - $socpeopleCommande = array( - 'remote_id' => $remoteIdSociete.'|1', - 'type' => eCommerceSocpeople::CONTACT_TYPE_ORDER, - 'last_update' => isset($commande['date_modified'])?$commande['date_modified']:$commande['date_created'], - 'name' => $billingName, // $commandeSocpeople['last_name'], - 'lastname' => $billingName, // $commandeSocpeople['last_name'], - 'firstname' => '', // $commandeSocpeople['first_name'], - 'town' => $commandeSocpeople['city'], - //'fk_pays' => getCountry($commandeSocpeople['country'], 3), - 'fax' => '', - 'zip' => $commandeSocpeople['postcode'], - //add wrap - 'address' => (!empty(trim($commandeSocpeople['company'])) ? trim($commandeSocpeople['company']) . ", " : "") . - $commandeSocpeople['address_1'] . (!empty($commandeSocpeople['address_1']) && !empty($commandeSocpeople['address_2']) ? "\n" : "") . $commandeSocpeople['address_2'], - 'phone' => $commandeSocpeople['phone'], - ); - - //set billing's address - $socpeopleFacture = $socpeopleCommande; - $socpeopleFacture['type'] = eCommerceSocpeople::CONTACT_TYPE_INVOICE; - - //set shipping's address - $livraisonSocpeople = $commande['shipping']; - if ((!empty($livraisonSocpeople['address_1']) || !empty($livraisonSocpeople['address_1'])) && !empty($livraisonSocpeople['city']) && !empty($livraisonSocpeople['postcode']) && !empty($livraisonSocpeople['country'])) { - $shippingName = (empty($conf->global->ECOMMERCENG_SHIPPING_CONTACT_NAME) ? 'Shipping' : $conf->global->ECOMMERCENG_SHIPPING_CONTACT_NAME); // Contact name treated as shipping address. - $socpeopleLivraison = array( - 'remote_id' => $remoteIdSociete . '|2', - 'type' => eCommerceSocpeople::CONTACT_TYPE_DELIVERY, - 'last_update' => isset($commande['date_modified'])?$commande['date_modified']:$commande['date_created'], - 'name' => $shippingName, // $livraisonSocpeople['last_name'], - 'lastname' => $shippingName, // $livraisonSocpeople['last_name'], - 'firstname' => '', // $livraisonSocpeople['first_name'], - 'town' => $livraisonSocpeople['city'], - //'fk_pays' => getCountry($commandeSocpeople['country'], 3), - 'fax' => '', - 'zip' => $livraisonSocpeople['postcode'], - //add wrap - 'address' => (!empty(trim($livraisonSocpeople['company'])) ? addslashes(trim($livraisonSocpeople['company'])) . ", " : "") . - $livraisonSocpeople['address_1'] . (!empty($livraisonSocpeople['address_1']) && !empty($livraisonSocpeople['address_2']) ? "\n" : "") . - $livraisonSocpeople['address_2'], - 'phone' => '', - ); + // Set billing's address + $bContact = $order['billing']; + $firstname = $bContact['first_name']; + $lastname = $bContact['last_name']; + if (!empty($firstname) && empty($lastname)) { + $lastname = $langs->trans("ECommercengWoocommerceLastnameNotInformed"); + } elseif (empty($firstname) && empty($lastname)) { + $lastname = $langs->trans('ECommercengWoocommerceWithoutFirstnameLastname'); + } + $contactBilling = [ + 'remote_id' => "", + 'type' => 1, //eCommerceSocpeople::CONTACT_TYPE_ORDER, + 'last_update' => $last_update->format('Y-m-d H:i:s'), + 'firstname' => $firstname, + 'lastname' => $lastname, + 'address' => $bContact['address_1'] . (!empty($bContact['address_1']) && !empty($bContact['address_2']) ? "\n" : "") . $bContact['address_2'], + 'zip' => $bContact['postcode'], + 'town' => $bContact['city'], + 'country_id' => getCountry($bContact['country'], 3), + 'email' => $bContact['email'], + 'phone' => $bContact['phone'], + 'fax' => null, + ]; + + // Set invoice's address + $contactInvoice = $contactBilling; + $contactInvoice['type'] = 1; //eCommerceSocpeople::CONTACT_TYPE_INVOICE; + + // Set shipping's address + $sContact = $order['shipping']; + if (!empty($sContact['address_1']) || !empty($sContact['address_2']) || + !empty($sContact['postcode']) || !empty($sContact['city']) || + !empty($sContact['country']) + ) { + if ($bContact['first_name'] != $sContact['first_name'] || $bContact['last_name'] != $sContact['last_name'] || + $bContact['address_1'] != $sContact['address_1'] || $bContact['address_2'] != $sContact['address_2'] || + $bContact['postcode'] != $sContact['postcode'] || $bContact['city'] != $sContact['city'] || + $bContact['country'] != $sContact['country'] + ) { + $firstname = $sContact['first_name']; + $lastname = $sContact['last_name']; + if (!empty($firstname) && empty($lastname)) { + $lastname = $langs->trans("ECommercengWoocommerceLastnameNotInformed"); + } elseif (empty($firstname) && empty($lastname)) { + $lastname = $langs->trans('ECommercengWoocommerceWithoutFirstnameLastname'); + } + $contactShipping = [ + 'remote_id' => "", + 'type' => 1, //eCommerceSocpeople::CONTACT_TYPE_DELIVERY, + 'last_update' => $last_update->format('Y-m-d H:i:s'), + 'firstname' => $firstname, + 'lastname' => $lastname, + 'address' => $sContact['address_1'] . (!empty($sContact['address_1']) && !empty($sContact['address_2']) ? "\n" : "") . $sContact['address_2'], + 'zip' => $sContact['postcode'], + 'town' => $sContact['city'], + 'country_id' => getCountry($sContact['country'], 3), + 'email' => null, + 'phone' => null, + 'fax' => null, + ]; } else { - $socpeopleLivraison = $socpeopleCommande; - $socpeopleLivraison['type'] = eCommerceSocpeople::CONTACT_TYPE_DELIVERY; + $contactShipping = $contactBilling; + $contactShipping['type'] = 1; //eCommerceSocpeople::CONTACT_TYPE_DELIVERY; } + } else { + $contactShipping = $contactBilling; + $contactShipping['type'] = 1; //eCommerceSocpeople::CONTACT_TYPE_DELIVERY; + } - //set delivery as service - $langs->load("ecommerce@ecommerceng"); - $shippingDisplayIfNull = (empty($conf->global->ECOMMERCENG_SHIPPING_NOT_DISPLAY_IF_NULL)?true:false); // Contact name treated as shipping address. - $delivery = array( - 'description' => $langs->trans('ECommerceShipping') . (isset($commande['shipping_lines'][0]) ? ' - '.$commande['shipping_lines'][0]['method_title'] : ''), // $commande['customer_note'] - 'price' => $commande['shipping_total'], - 'qty' => $shippingDisplayIfNull || isset($commande['shipping_lines'][0]) ? 1 : 0, //0 to not show - 'tva_tx' => $this->getTaxRate($commande['shipping_total'], $commande['shipping_tax']) - ); - - //define delivery date - if (isset($commande['date_completed']) && $commande['date_completed'] != null) - $deliveryDate = $commande['date_completed']; - else - $deliveryDate = $commande['date_created']; - - // define status of order - // $commande['status'] is: 'pending', 'processing', 'on-hold', 'completed', 'cancelled', 'refunded', 'failed' - $tmp = $commande['status']; - - // try to match dolibarr status - $status = ''; - if (preg_match('/^pending/', $tmp)) $status = Commande::STATUS_VALIDATED; // manage 'pending', 'pending_payment', 'pending_paypal', 'pending_ogone', 'pending_...' - elseif ($tmp == 'processing') $status = Commande::STATUS_ACCEPTED; // shipment in process or invoice done = processing // Should be constant Commande::STATUS_SHIPMENTONPROCESS but not defined in dolibarr 3.9 - elseif ($tmp == 'on-hold') $status = Commande::STATUS_ACCEPTED; - elseif (preg_match('/^cancelled/', $tmp)) $status = Commande::STATUS_CANCELED; // manage 'canceled', 'canceled_bnpmercanetcw', 'canceled_...' - elseif ($tmp == 'completed') $status = Commande::STATUS_CLOSED; - elseif ($tmp == 'refunded') $status = Commande::STATUS_CLOSED; - elseif ($tmp == 'failed') $status = Commande::STATUS_CANCELED; - if ($status == '') - { - dol_syslog("Status: We found an order id ".$commande['increment_id']." with ecommerce status '".$tmp."' that is unknown, not supported. We will use '0' for Dolibarr", LOG_WARNING); - $status = Commande::STATUS_DRAFT; // draft by default (draft does not exists with magento, so next line will set correct status) - } - else - { - dol_syslog("Status: We found an order id ".$commande['increment_id']." with ecommerce status '".$tmp."'. We convert it into Dolibarr status '".$status."'"); - } - - // try to match dolibarr billed status (payed or not) - $billed = -1; // unknown - if ($commande['status'] == 'pending') $billed = 0; - if ($commande['status'] == 'processing') $billed = 0; // - if ($commande['status'] == 'on-hold') $billed = 0; // - if ($commande['status'] == 'completed') $billed = 1; // We are sure for complete that order is payed - if ($commande['status'] == 'cancelled') $billed = 0; // We are sure for canceled that order was not payed - if ($commande['status'] == 'refunded') $billed = 1; // - if ($commande['status'] == 'failed') $billed = 0; // - // Note: with processing, billed can be 0 or 1, so we keep -1 - - - // Add order content to array or orders - $commandes[] = array( - 'last_update' => isset($commande['date_modified'])?$commande['date_modified']:$commande['date_created'], - 'remote_id' => $commande['id'], - 'remote_increment_id' => $commande['id'], - 'remote_id_societe' => $remoteIdSociete, - 'ref_client' => $commande['id'], - 'date_commande' => $commande['date_created'], - 'date_livraison' => $commande['date_completed'], // $deliveryDate, - 'items' => $items, - 'delivery' => $delivery, - 'note' => $commande['customer_note'], - 'socpeopleCommande' => $socpeopleCommande, - 'socpeopleFacture' => $socpeopleFacture, - 'socpeopleLivraison' => $socpeopleLivraison, - 'status' => $status, // dolibarr status - 'billed' => $billed, - 'remote_state' => $commande['status'], // remote state, for information only (less accurate than status) - 'remote_status' => $commande['status'], // remote status, for information only (more accurate than state) - 'remote_order' => $commande - ); + // Set delivery as service + $shippingDisplayIfNull = (empty($conf->global->ECOMMERCENG_SHIPPING_NOT_DISPLAY_IF_NULL) ? true : false); + $delivery = [ + 'description' => $langs->trans('ECommerceShipping') . (isset($order['shipping_lines'][0]) ? ' - ' . + $order['shipping_lines'][0]['method_title'] : ''), // $order['customer_note'] + 'price' => $order['shipping_total'], + 'qty' => $shippingDisplayIfNull || isset($order['shipping_lines'][0]) ? 1 : 0, //0 to not show + 'tva_tx' => $this->getClosestDolibarrTaxRate($order['shipping_total'], $order['shipping_tax']) + ]; + + // Set status of order + // $order['status'] is: 'pending', 'processing', 'on-hold', 'completed', 'cancelled', 'refunded', 'failed' + $orderStatus = $order['status']; + + $status = ''; + switch ($orderStatus) { + case 'on-hold': $status = Commande::STATUS_DRAFT; break; + case 'pending': $status = Commande::STATUS_DRAFT; break; + case 'processing': $status = !empty($conf->global->ECOMMERCENG_WOOCOMMERCE_ORDER_PROCESSING_STATUS_TO_DRAFT)?Commande::STATUS_DRAFT:Commande::STATUS_VALIDATED; break; + case 'completed': $status = Commande::STATUS_CLOSED; break; + case 'refunded': $status = Commande::STATUS_CANCELED; break; + case 'cancelled': $status = Commande::STATUS_CANCELED; break; + case 'failed': $status = Commande::STATUS_CANCELED; break; } - else - { - dol_syslog("No items in this order", LOG_WARNING); + if (!empty($conf->global->ECOMMERCENG_WOOCOMMERCE_FORCE_ORDER_STATUS_TO_DRAFT)) $status = Commande::STATUS_DRAFT; + if ($status == '') { + dol_syslog(__METHOD__ . ": Status \"$orderStatus\" was not found for remote order ID {$order['id']} and set in draft", LOG_WARNING); + $status = Commande::STATUS_DRAFT; // draft by default } + + // Set dolibarr billed status (payed or not) + $billed = -1; // unknown + if ($order['status'] == 'pending') $billed = 0; + if ($order['status'] == 'processing') $billed = 0; // + if ($order['status'] == 'on-hold') $billed = 0; // + if ($order['status'] == 'completed') $billed = 1; // We are sure for complete that order is payed + if ($order['status'] == 'cancelled') $billed = 0; // We are sure for canceled that order was not payed + if ($order['status'] == 'refunded') $billed = 1; // + if ($order['status'] == 'failed') $billed = 0; // + // Note: with processing, billed can be 0 or 1, so we keep -1 + + // Add order content to array or orders + $orders[] = [ + 'last_update' => $last_update->format('Y-m-d H:i:s'), + 'remote_id' => $order['id'], + 'remote_increment_id' => $order['id'], + 'remote_id_societe' => $remoteCompanyID, + 'ref_client' => $order['id'], + 'date_commande' => $order['date_created'], + 'date_livraison' => $order['date_completed'], + 'items' => $items, + 'delivery' => $delivery, + 'note' => $order['customer_note'], + 'socpeopleCommande' => $contactBilling, + 'socpeopleFacture' => $contactInvoice, + 'socpeopleLivraison' => $contactShipping, + 'status' => $status, // dolibarr status + 'billed' => $billed, + 'remote_state' => $order['status'], // remote state, for information only (less accurate than status) + 'remote_status' => $order['status'], // remote status, for information only (more accurate than state) + 'remote_order' => $order, + 'payment_method' => $order['payment_method_title'], + 'extrafields' => [ + "ecommerceng_online_payment_{$conf->entity}" => empty($order['date_paid']) ? 0 : 1, + ], + ]; } } } //important - order by last update - if (count($commandes)) - { - $last_update = array(); - foreach ($commandes as $key => $row) - { + if (count($orders)) { + $last_update = []; + foreach ($orders as $key => $row) { $last_update[$key] = $row['last_update']; } - array_multisort($last_update, SORT_ASC, $commandes); + array_multisort($last_update, SORT_ASC, $orders); } - dol_syslog("convertRemoteObjectIntoDolibarrCommande end (found ".count($commandes)." array of orders filled with complete data from eCommerce)"); - return $commandes; + dol_syslog(__METHOD__ . ": end, converted " . count($orders) . " remote orders", LOG_DEBUG); + return $orders; } /** - * Put the remote data into facture dolibarr data from instantiated class - * Return array of invoices by update time. + * Desactivated because is not supported by woocommerce. * - * @param array $remoteObject array of remote invoices - * @param int $toNb Max nb - * @return array facture + * @param array $remoteObject List of id of remote orders to convert + * @param int $toNb Max nb + * @return array Empty list */ public function convertRemoteObjectIntoDolibarrFacture($remoteObject, $toNb=0) { - global $conf; - - $factures = array(); + dol_syslog(__METHOD__ . ": Desactivated for site ID {$this->site->id}", LOG_DEBUG); + return []; + } -/* $maxsizeofmulticall = (empty($conf->global->ECOMMERCENG_MAXSIZE_MULTICALL)?100:$conf->global->ECOMMERCENG_MAXSIZE_MULTICALL); // 1000 seems ok for multicall. - $nbsynchro = 0; - $nbremote = count($remoteObject); - if ($nbremote) - { - // Create n groups of $maxsizeofmulticall records max to call the multiCall - $callsgroup = array(); - $calls=array(); - foreach ($remoteObject as $rfacture) - { - if (($nbsynchro % $maxsizeofmulticall) == 0) - { - if (count($calls)) $callsgroup[]=$calls; // Add new group for lot of 1000 call arrays - $calls=array(); - } + /** + * Get remote category tree + * + * @return array|boolean Array with categories or false if error + */ + public function getRemoteCategoryTree() + { + dol_syslog(__METHOD__ . ": Get remote category tree for site ID {$this->site->id}", LOG_DEBUG); + global $conf, $langs; - $calls[] = $rfacture; + $categories = []; + $idxPage = 1; + $per_page = empty($conf->global->ECOMMERCENG_MAXSIZE_MULTICALL) ? 100 : min($conf->global->ECOMMERCENG_MAXSIZE_MULTICALL, 100); - $nbsynchro++; // nbsynchro is now number of calls to do + while (true) { + try { + $results = $this->client->get('products/categories', + [ + 'page' => $idxPage++, + 'per_page' => $per_page, + ] + ); + } catch (HttpClientException $fault) { + $this->errors[] = $langs->trans('ECommerceWoocommerceGetRemoteCategoryTree', $this->site->name, $fault->getCode() . ': ' . $fault->getMessage()); + dol_syslog(__METHOD__ . + ': Error:' . $langs->transnoentitiesnoconv('ECommerceWoocommerceGetRemoteCategoryTree', $this->site->name, $fault->getCode() . ': ' . $fault->getMessage()) . + ' - Request:' . json_encode($fault->getRequest()) . ' - Response:' . json_encode($fault->getResponse()), LOG_ERR); + return false; + } + if (count($results) == 0) break; + + foreach ($results as $category) { + $categories[$category['id']] = [ + 'category_id' => $category['id'], // id category + 'parent_id' => $category['parent'], + 'label' => $category['name'], + 'name' => $category['name'], + 'description' => $category['description'], + 'updated_at' => '', + ]; } - if (count($calls)) $callsgroup[]=$calls; // Add new group for the remain lot of calls not yet added + } - dol_syslog("convertRemoteObjectIntoDolibarrFacture Call WS to get detail for the ".count($remoteObject)." objects (".count($callsgroup)." calls with ".$maxsizeofmulticall." max of records each) then create a Dolibarr array for each object"); - //var_dump($callsgroup);exit; + // Set tree + foreach ($categories as $category) { + $parent_id = $category['parent_id']; - $results=array(); - $nbcall=0; - foreach ($callsgroup as $calls) - { - try { - $nbcall++; - dol_syslog("convertRemoteObjectIntoDolibarrFacture Call WS nb ".$nbcall." (".count($calls)." record)"); - $resulttmp = $this->client->get('orders', - [ - 'include' => implode(',', $calls), - ] - ); - $results=array_merge($results, $resulttmp); - } catch (HttpClientException $fault) { - dol_syslog('convertRemoteObjectIntoDolibarrFacture :'.$fault->getMessage().'-'.$fault->getCode().'-'.$fault->getTraceAsString(), LOG_WARNING); + if (!empty($parent_id)) { + if (!isset($categories[$parent_id]['children'])) { + $categories[$parent_id]['level'] = 0; + $categories[$parent_id]['children'] = []; } - } - - if (count($results)) - { - $i=0; - foreach ($results as $facture) - { - // Process invoice - dol_syslog("- Process invoice remote_id=".$facture['order_id']." last_update=".$facture['updated_at']." societe order_id=".$facture['order_id']); - - $i++; - - $configurableItems = array(); - //retrive remote order from invoice - $commande = $this->getRemoteCommande($facture['order_id']); - //set each invoice items - $items = array(); - if (count($facture['items'])) - { - foreach ($facture['items'] as $item) - { - //var_dump($item); // show invoice item as it is from magento - - $product_type = $this->getProductTypeOfItem($item, $commande, $facture); - $parent_item_id = $this->getParentItemOfItem($item, $commande, $facture); - - // If item is configurable, localMemCache it, to use its price and tax rate instead of the one of its child - if ($product_type == 'configurable') { - $configurableItems[$item['item_id']] = array( - 'item_id' => $item['item_id'], - 'id_remote_product' => $item['product_id'], - 'description' => $item['name'], - 'product_type' => $product_type, - 'price' => $item['price'], - 'qty' => $item['qty'], - 'tva_tx' => $this->getTaxRate($item['row_total'], $item['tax_amount']) - ); - } else { - // If item has a parent item id defined in $configurableItems, it's a child simple item so we get it's price and tax values instead of 0 - if (!array_key_exists($parent_item_id, $configurableItems)) { - $items[] = array( - 'item_id' => $item['item_id'], - 'id_remote_product' => $item['product_id'], - 'description' => $item['name'], - 'product_type' => $product_type, - 'price' => $item['price'], - 'qty' => $item['qty'], - 'tva_tx' => $this->getTaxRate($item['row_total'], $item['tax_amount']) - ); - } else { - $items[] = array( - 'item_id' => $item['item_id'], - 'id_remote_product' => $item['product_id'], - 'description' => $item['name'], - 'product_type' => $product_type, - 'price' => $configurableItems[$parent_item_id]['price'], - 'qty' => $item['qty'], - 'tva_tx' => $configurableItems[$parent_item_id]['tva_tx'] - ); - } - } - } - - //set shipping address - $shippingAddress = $commande["shipping_address"]; - $billingAddress = $commande["billing_address"]; - $socpeopleLivraison = array( - 'remote_id' => $shippingAddress['address_id'], - 'type' => eCommerceSocpeople::CONTACT_TYPE_DELIVERY, - 'last_update' => $shippingAddress['updated_at'], - 'name' => $shippingAddress['lastname'], - 'firstname' => $shippingAddress['firstname'], - 'ville' => $shippingAddress['city'], - //'fk_pays' => $commandeSocpeople['country_id'], - 'fax' => $shippingAddress['fax'], - 'cp' => $shippingAddress['postcode'], - //add wrap - 'address' => (trim($shippingAddress['company']) != '' ? trim($shippingAddress['company']) . ' - ' : '') . $shippingAddress['street'], - 'phone' => $shippingAddress['telephone'] - ); - //set invoice address - $socpeopleFacture = array( - 'remote_id' => $billingAddress['address_id'], - 'type' => eCommerceSocpeople::CONTACT_TYPE_INVOICE, - 'last_update' => $billingAddress['updated_at'], - 'name' => $billingAddress['lastname'], - 'firstname' => $billingAddress['firstname'], - 'ville' => $billingAddress['city'], - //'fk_pays' => $commandeSocpeople['country_id'], - 'fax' => $billingAddress['fax'], - 'cp' => $billingAddress['postcode'], - //add wrap - 'address' => (trim($billingAddress['company']) != '' ? trim($billingAddress['company']) . ' - ' : '') . $billingAddress['street'], - 'phone' => $billingAddress['telephone'] - ); - //set delivery as service - $delivery = array( - 'description' => $commande['shipping_description'], - 'price' => $facture['shipping_amount'], - 'qty' => 1, //0 to not show - 'tva_tx' => $this->getTaxRate($facture['shipping_amount'], $facture['shipping_tax_amount']) - ); - - $eCommerceTempSoc = new eCommerceSociete($this->db); - if ($commande['customer_id'] == null || $eCommerceTempSoc->fetchByRemoteId($commande['customer_id'], $this->site->id) < 0) - { - $remoteIdSociete = 0; - } - else - { - $remoteIdSociete = $commande['customer_id']; - } - - // load local order to be used to retreive some data for invoice - $eCommerceTempCommande = new eCommerceCommande($this->db); - $eCommerceTempCommande->fetchByRemoteId($commande['order_id'], $this->site->id); - $dbCommande = new Commande($this->db); - $dbCommande->fetch($eCommerceTempCommande->fk_commande); - - - // define status of invoice - $tmp = $facture['state']; // state from is 1, 2, 3 - - // try to match dolibarr status - $status = ''; - if ($tmp == 1) $status = Facture::STATUS_VALIDATED; // validated = pending - if ($tmp == 2) $status = Facture::STATUS_CLOSED; // complete - if ($tmp == 3) $status = Facture::STATUS_ABANDONED; // canceled = holded - if ($status == '') - { - dol_syslog("Status: We found an invoice id ".$commande['increment_id']." with ecommerce status '".$tmp."' that is unknown, not supported. We will use '0' for Dolibarr", LOG_WARNING); - $status = Facture::STATUS_DRAFT; // draft by default (draft does not exists with magento, so next line will set correct status) - } - else - { - dol_syslog("Status: We found an invoice id ".$commande['increment_id']." with ecommerce status '".$tmp."'. We convert it into Dolibarr status '".$status."'"); - } - - $close_code = ''; - $close_note = ''; - if ($tmp == 3) - { - $close_code = Facture::CLOSECODE_ABANDONED; - $close_note = 'Holded on ECommerce'; - } - - //add invoice to invoices - $factures[] = array( - 'last_update' => $facture['updated_at'], - 'remote_id' => $facture['invoice_id'], - 'remote_increment_id' => $facture['increment_id'], - 'ref_client' => $facture['increment_id'], - 'remote_order_id' => $facture['order_id'], - 'remote_order_increment_id' => $facture['order_increment_id'], - 'remote_id_societe' => $remoteIdSociete, - 'socpeopleLivraison' => $socpeopleLivraison, - 'socpeopleFacture' => $socpeopleFacture, - 'date' => $facture['created_at'], - 'code_cond_reglement' => $dbCommande->cond_reglement_code, // Take for local order - 'delivery' => $delivery, - 'items' => $items, - 'status' => $tmp, - 'close_code' => $close_code, - 'close_note' => $close_note, - 'remote_state' => $facture['state'], - 'remote_order' => $commande, - 'remote_invoice' => $facture - ); - } - else - { - dol_syslog("No items in this invoice", LOG_WARNING); - } - } + $categories[$parent_id]['children'][] = &$categories[$category['category_id']]; } } - //important - order by last update - if (count($factures)) - { - $last_update=array(); - foreach ($factures as $key => $row) - { - $last_update[$key] = $row['last_update']; + // Make tree + $categories_tree = ['level' => 0, 'children' => []]; + foreach ($categories as $category) { + if (empty($category['parent_id'])) { + $categories_tree['children'][] = $category; } - array_multisort($last_update, SORT_ASC, $factures); } - //var_dump($factures);exit; - */ - - dol_syslog("convertRemoteObjectIntoDolibarrFacture end (found ".count($products)." record)"); - return $factures; + dol_syslog(__METHOD__ . ": end, " . count($categories) . " remote category recovered", LOG_DEBUG); + return $categories_tree; } - /** - * Return if type of an item of invoice (information comue from item of order) + * Desactivated because is not supported by woocommerce. * - * @param array $item Item of invoice - * @param array $commande Commande with items - * @param array $facture Facture with items - * @return string + * @param int $remote_company_id Id of company + * + * @return array Array with address id */ - function getProductTypeOfItem($item, $commande, $facture) + public function getRemoteAddressIdForSociete($remote_company_id) { - $product_type = 'notfound'; // By default + dol_syslog(__METHOD__ . ": Desactivated for site ID {$this->site->id}", LOG_DEBUG); + return [$remote_company_id]; + } - //print "Try to find product type of invoice item_id=".$item['item_id']." (invoice ".$facture['increment_id'].") and order_item_id=".$item['order_item_id']." (order ".$commande['increment_id'].")\n"; + /** + * Return content of one category + * + * @param int $category_id Remote category id + * + * @return array|boolean Return category data + */ + public function getCategoryData($category_id) + { + dol_syslog(__METHOD__ . ": Get remote category for site ID {$this->site->id}", LOG_DEBUG); + global $langs; - $order_item_id = $item['order_item_id']; + $category = []; - // We scan item of order to find this order item id - foreach($commande['items'] as $itemorder) - { - if ($itemorder['item_id'] == $order_item_id) - { - // We've got it - $product_type = $itemorder['product_type']; - break; - } + try { + $result = $this->client->get('products/categories/' . $category_id); + } catch (HttpClientException $fault) { + $this->errors[] = $langs->trans('ECommerceWoocommerceGetCategoryData', $this->site->name, $fault->getCode() . ': ' . $fault->getMessage()); + dol_syslog(__METHOD__ . + ': Error:' . $langs->transnoentitiesnoconv('ECommerceWoocommerceGetCategoryData', $this->site->name, $fault->getCode() . ': ' . $fault->getMessage()) . + ' - Request:' . json_encode($fault->getRequest()) . ' - Response:' . json_encode($fault->getResponse()), LOG_ERR); + return false; } - //print "Found product type = ".$product_type."\n"; - - if ($product_type == 'notfound') $product_type = 'simple'; + if (isset($result)) { + $category = [ + 'category_id' => $result['id'], // id category + 'parent_id' => $result['parent'], + 'label' => $result['name'], + 'name' => $result['name'], + 'description' => $result['description'], + 'updated_at' => '', + ]; + } - return $product_type; + dol_syslog(__METHOD__ . ": end", LOG_DEBUG); + return $category; } /** - * Return if type of an item of invoice (information comue from item of order) + * Return content of one order * - * @param array $item Item of invoice - * @param array $commande Commande with items - * @param array $facture Facture with items - * @return string + * @param int $remoteOrderId Remote order id + * + * @return array Empty */ - function getParentItemOfItem($item, $commande, $facture) + public function getRemoteCommande($remoteOrderId) { - //print "Try to find invoice parent item id of invoice item_id=".$item['item_id']." (invoice ".$facture['increment_id'].") and order_item_id=".$item['order_item_id']." (order ".$commande['increment_id'].")\n"; + dol_syslog(__METHOD__ . ": Desactivated for site ID {$this->site->id}", LOG_DEBUG); + return []; + } - $parent_item_id = 0; // By default - $parent_item_id_in_order = 0; + /** + * Update the remote product + * + * @param int $remote_id Id of product on remote ecommerce + * @param Product $object Product object + * + * @return boolean True or false + */ + public function updateRemoteProduct($remote_id, $object) + { + dol_syslog(__METHOD__ . ": Update the remote product ID $remote_id for Dolibarr product ID {$object->id} for site ID {$this->site->id}", LOG_DEBUG); + global $conf, $langs; - $order_item_id = $item['order_item_id']; + $isProductVariation = false; + $remote_product_id = $remote_id; + $remote_product_variation_id = 0; + if (preg_match('/^(\d+)\|(\d+)$/', $remote_id, $idsProduct) == 1) { // Variations + $isProductVariation = true; + $remote_product_id = $idsProduct[1]; + $remote_product_variation_id = $idsProduct[2]; + } - // We scan item of order to find this order item id - foreach($commande['items'] as $itemorder) + // Set Weight + $totalWeight = $object->weight; + if ($object->weight_units < 50) // >50 means a standard unit (power of 10 of official unit), > 50 means an exotic unit (like inch) { - if ($itemorder['item_id'] == $order_item_id) - { - // We've got it - $product_type = $itemorder['product_type']; + $trueWeightUnit = pow(10, $object->weight_units); + $totalWeight = sprintf("%f", $object->weight * $trueWeightUnit); + } - $parent_item_id_in_order = $itemorder['parent_item_id']; - break; + try { + if ($isProductVariation) { // Variations + $results = $this->client->get("products/$remote_product_id/variations/$remote_product_variation_id"); + } else { + $results = $this->client->get("products/$remote_product_id"); } + } catch (HttpClientException $fault) { + $this->errors[] = $langs->trans('ECommerceWoocommerceUpdateRemoteProductGetRemoteProduct', $this->site->name, $fault->getCode() . ': ' . $fault->getMessage()); + dol_syslog(__METHOD__ . + ': Error:' . $langs->transnoentitiesnoconv('ECommerceWoocommerceUpdateRemoteProductGetRemoteProduct', $this->site->name, $fault->getCode() . ': ' . $fault->getMessage()) . + ' - Request:' . json_encode($fault->getRequest()) . ' - Response:' . json_encode($fault->getResponse()), LOG_ERR); + return false; } - // If the item is linked to an order item id that has a parent order item id - if ($parent_item_id_in_order) - { - // We scan now invoice items to find the item that is linked to order item id $parent_item_id_in_order - foreach($facture['items'] as $itemfacture) - { - if ($itemfacture['order_item_id'] == $parent_item_id_in_order) - { - // We've got it - $parent_item_id = $itemfacture['item_id']; + // images + $images = []; + if (!empty($conf->global->ECOMMERCENG_ENABLE_SYNCHRO_IMAGES)) { + // Get current images + $current_images = []; + if (!empty($results) && isset($results['images']) && is_array($results['images'])) { + foreach ($results['images'] as $image) { + $current_images[$image['name']] = $image['id']; + } + } + + // Product - Images properties + $entity = isset($object->entity) ? $object->entity : $conf->entity; + if (!empty($conf->global->PRODUCT_USE_OLD_PATH_FOR_PHOTO)) { // For backward compatiblity, we scan also old dirs + if ($object->type == Product::TYPE_PRODUCT) { + $dir = $conf->product->multidir_output[$entity] . '/' . substr(substr("000" . $object->id, -2), 1, 1) . '/' . substr(substr("000" . $object->id, -2), 0, 1) . '/' . $object->id . "/photos/"; + } else { + $dir = $conf->service->multidir_output[$entity] . '/' . substr(substr("000" . $object->id, -2), 1, 1) . '/' . substr(substr("000" . $object->id, -2), 0, 1) . '/' . $object->id . "/photos/"; + } + } else { + if ($object->type == Product::TYPE_PRODUCT) { + $dir = $conf->product->multidir_output[$entity] . '/' . get_exdir(0, 0, 0, 0, $object, 'product') . dol_sanitizeFileName($object->ref) . '/'; + } else { + $dir = $conf->service->multidir_output[$entity] . '/' . get_exdir(0, 0, 0, 0, $object, 'product') . dol_sanitizeFileName($object->ref) . '/'; + } + } + $photos = $object->liste_photos($dir); + foreach ($photos as $index => $photo) { + $img = []; + + $filename = ecommerceng_wordpress_sanitize_file_name($photo['photo']); + if (!isset($current_images[$filename])) { + $result = $this->worpressclient->postmedia("media", $dir . $photo['photo'], [ + 'slug' => $object->id . '_' . $filename, + 'ping_status' => 'closed', + 'comment_status' => 'closed', + ]); + + if ($result === null) { + $this->errors[] = $langs->trans('ECommerceWoocommerceUpdateRemoteProductSendImage', $remote_id, $this->site->name, implode('; ', $this->worpressclient->errors)); + dol_syslog(__METHOD__ . ': Error:' . $langs->transnoentitiesnoconv('ECommerceWoocommerceUpdateRemoteProductSendImage', $remote_id, $this->site->name, implode('; ', $this->worpressclient->errors)), LOG_ERR); + return false; + } + + $img['id'] = $result['id']; + } else { + $img['id'] = $current_images[$filename]; + } + + $img['name'] = $filename; + $img['position'] = $index; + $images[] = $img; + + if ($isProductVariation) { // Get only one image for variation break; } } } - //print "Found invoice parent_item_id=".$parent_item_id." and order parent_item_id=".$parent_item_id_in_order."\n"; + // Product - Meta data properties + $object->fetch_optionals(); - return $parent_item_id; - } + // Price + if (!empty($conf->global->PRODUIT_MULTIPRICES)) { + $price_level = !empty($this->site->price_level) ? $this->site->price_level : 1; + $price = $object->multiprices[$price_level]; + } else { + $price = $object->price; + } + if ($isProductVariation) { // Variations + /* + // Product variation - Downloads properties + $downloads = [ + [ + 'name' => '', // string File name. + 'file' => '', // string File URL. + ], + ]; + + // Product variation - Dimensions properties + $dimensions = [ + 'length' => '', // string Product length (cm). + 'width' => '', // string Product width (cm). + 'height' => '', // string Product height (cm). + ]; + + // Product variation - Image properties + $images = [ + [ + 'id' => 0, // integer Image ID. Not required + 'src' => '', // string Image URL. + 'name' => '', // string Image name. + 'alt' => '', // string Image alternative text. + 'position' => 0, // integer Image position. 0 means that the image is featured. + ], + ]; + + // Product variation - Attributes properties + $attributes = [ + [ + 'id' => 0, // integer Attribute ID. + 'name' => '', // string Attribute name. + 'option' => '', // string Selected attribute term name. + ], + ]; + + // Product variation - Meta data properties + $meta_data = [ + 'key' => '', // string Meta key. + 'value' => '', // string Meta value. + ]; + */ + + $variationData = [ + 'description' => nl2br($object->array_options["options_ecommerceng_description_{$conf->entity}"]), // string Variation description. + 'sku' => $object->ref, // string Unique identifier. + 'regular_price' => $price, // string Variation regular price. + //'sale_price' => '', // string Variation sale price. + //'date_on_sale_from' => '', // date-time Start date of sale price, in the site’s timezone. + //'date_on_sale_from_gmt' => '', // date-time Start date of sale price, as GMT. + //'date_on_sale_to' => '', // date-time End date of sale price, in the site’s timezone. + //'date_on_sale_to_gmt' => '', // date-time End date of sale price, in the site’s timezone. + //'visible' => '', // boolean Define if the attribute is visible on the “Additional information” tab in the product’s page. Default is true. + //'virtual' => $object->type == Product::TYPE_SERVICE, // boolean If the variation is virtual. Default is false. + //'downloadable' => '', // boolean If the variation is downloadable. Default is false. + //'downloads' => $downloads, // array List of downloadable files. See Product variation - Downloads properties + //'download_limit' => '', // integer Number of times downloadable files can be downloaded after purchase. Default is -1. + //'download_expiry' => '', // integer Number of days until access to downloadable files expires. Default is -1. + 'tax_status' => 'none', // string Tax status. Options: taxable, shipping and none. Default is taxable. + //'tax_class' => '', // string Tax class. + //'manage_stock' => '', // boolean Stock management at variation level. Default is false. + //'stock_quantity' => '', // integer Stock quantity. + //'in_stock' => '', // boolean Controls whether or not the variation is listed as “in stock” or “out of stock” on the frontend. Default is true. + //'backorders' => '', // string If managing stock, this controls if backorders are allowed. Options: no, notify and yes. Default is no. + 'weight' => (!empty($totalWeight) ? $totalWeight : ''), // string Variation weight (kg). + //'dimensions' => $dimensions, // object Variation dimensions. See Product variation - Dimensions properties + //'shipping_class' => '', // string Shipping class slug. + 'image' => (!empty($images) ? $images[0] : ''), // object Variation image data. See Product variation - Image properties + //'attributes' => $attributes, // array List of attributes. See Product variation - Attributes properties + //'menu_order' => '', // integer Menu order, used to custom sort products. + //'meta_data' => $meta_data, // array Meta data. See Product variation - Meta data properties + ]; + + // Set tax + if (!empty($object->array_options["options_ecommerceng_tax_class_{$this->site->id}_{$conf->entity}"])) { + $variationData['tax_status'] = 'taxable'; + $variationData['tax_class'] = $object->array_options["options_ecommerceng_tax_class_{$this->site->id}_{$conf->entity}"]; + } + // Product + // 'name' => $object->label, // string Product name. + // 'status' => $object->status ? 'publish' : 'pending', // string Product status (post status). Options: draft, pending, private and publish. Default is publish. + try { + $result = $this->client->put("products/$remote_product_id/variations/$remote_product_variation_id", $variationData); + } catch (HttpClientException $fault) { + $this->errors[] = $langs->trans('ECommerceWoocommerceUpdateRemoteProductVariation', $this->site->name, $fault->getCode() . ': ' . $fault->getMessage()); + dol_syslog(__METHOD__ . + ': Error:' . $langs->transnoentitiesnoconv('ECommerceWoocommerceUpdateRemoteProductVariation', $this->site->name, $fault->getCode() . ': ' . $fault->getMessage()) . + ' - Request:' . json_encode($fault->getRequest()) . ' - Response:' . json_encode($fault->getResponse()), LOG_ERR); + return false; + } + } else { // Product + /* + // Product - Downloads properties + $downloads = [ + [ + 'name' => '', // string File name. + 'file' => '', // string File URL. + ], + ]; + + // Product - Dimensions properties + $dimensions = [ + 'length' => '', // string Product length (cm). + 'width' => '', // string Product width (cm). + 'height' => '', // string Product height (cm). + ]; + + // Product - Categories properties + $categories = [ + [ + 'id' => 0, // integer Category ID. + ], + ]; - // Now functions to get data on remote shop, from the remote id. + // Product - Tags properties + $tags = [ + [ + 'id' => 0, // integer Tag ID. + ], + ]; + // Product - Images properties + $images = [ + [ + 'id' => 0, // integer Image ID. Not required + 'src' => '', // string Image URL. + 'name' => '', // string Image name. + 'alt' => '', // string Image alternative text. + 'position' => 0, // integer Image position. 0 means that the image is featured. + ], + ]; + + // Product - Attributes properties + $attributes = [ + [ + 'id' => 0, // integer Attribute ID. Not required + 'name' => '', // string Attribute name. + 'position' => 0, // integer Attribute position. + 'visible' => false, // boolean Define if the attribute is visible on the “Additional information” tab in the product’s page. Default is false. + 'variation' => false, // boolean Define if the attribute can be used as variation. Default is false. + 'options' => [], // array List of available term names of the attribute. + ], + ]; + + // Product - Default attributes properties + $default_attributes = [ + 'id' => 0, // integer Attribute ID. Not required + 'name' => '', // string Attribute name. + 'option' => '', // string Selected attribute term name. + ]; + + // Product - Meta data properties + $meta_data = [ + 'key' => '', // string Meta key. + 'value' => '', // string Meta value. + ]; + */ + + // Get categories + $eCommerceCategory = new eCommerceCategory($this->db); + $cat = new Categorie($this->db); + $categories_list = $cat->containing($object->id, 'product'); + $categories = []; + foreach ($categories_list as $category) { + if ($this->site->fk_cat_product != $category->id) { + $ret = $eCommerceCategory->fetchByFKCategory($category->id, $this->site->id); + if ($ret > 0) { + $categories[] = ['id' => $eCommerceCategory->remote_id]; + } + } + } + + $status = $object->array_options["options_ecommerceng_wc_status_{$this->site->id}_{$conf->entity}"]; + + $productData = [ + 'name' => $object->label, // string Product name. + //'slug' => '', // string Product slug. + //'type' => '', // string Product type. Options: simple, grouped, external and variable. Default is simple. + 'status' => (!empty($status) ? $status : ''), //$object->status ? 'publish' : 'pending', // string Product status (post status). Options: draft, pending, private and publish. Default is publish. + //'featured' => false, // boolean Featured product. Default is false. + //'catalog_visibility' => '', // string Catalog visibility. Options: visible, catalog, search and hidden. Default is visible. + 'description' => nl2br($object->array_options["options_ecommerceng_description_{$conf->entity}"]), // string Product description. + 'short_description' => nl2br($object->array_options["options_ecommerceng_short_description_{$conf->entity}"]), // string Product short description. + 'sku' => $object->ref, // string Unique identifier. + 'regular_price' => $price, // string Product regular price. + //'sale_price' => '', // string Product sale price. + //'date_on_sale_from' => '', // date-time Start date of sale price, in the site’s timezone. + //'date_on_sale_from_gmt' => '', // date-time Start date of sale price, as GMT. + //'date_on_sale_to' => '', // date-time End date of sale price, in the site’s timezone. + //'date_on_sale_to_gmt' => '', // date-time End date of sale price, in the site’s timezone. + //'virtual' => $object->type == Product::TYPE_SERVICE, // boolean If the product is virtual. Default is false. + //'downloadable' => false, // boolean If the product is downloadable. Default is false. + //'downloads' => $downloads, // array List of downloadable files. See Product - Downloads properties + //'download_limit' => -1, // integer Number of times downloadable files can be downloaded after purchase. Default is -1. + //'download_expiry' => -1, // integer Number of days until access to downloadable files expires. Default is -1. + //'external_url' => '', // string Product external URL. Only for external products. + //'button_text' => '', // string Product external button text. Only for external products. + 'tax_status' => 'none', // string Tax status. Options: taxable, shipping and none. Default is taxable. + //'tax_class' => '', // string Tax class. + //'manage_stock' => false, // boolean Stock management at product level. Default is false. + //'stock_quantity' => $object->stock_reel, // integer Stock quantity. + //'in_stock' => $object->stock_reel > 0, // boolean Controls whether or not the product is listed as “in stock” or “out of stock” on the frontend. Default is true. + //'backorders' => '', // string If managing stock, this controls if backorders are allowed. Options: no, notify and yes. Default is no. + //'sold_individually' => false, // boolean Allow one item to be bought in a single order. Default is false. + 'weight' => (!empty($totalWeight) ? $totalWeight : ''), // string Product weight (kg). + //'dimensions' => $dimensions, // object Product dimensions. See Product - Dimensions properties + //'shipping_class' => '', // string Shipping class slug. + //'reviews_allowed' => true, // boolean Allow reviews. Default is true. + //'upsell_ids' => [], // array List of up-sell products IDs. + //'cross_sell_ids' => [], // array List of cross-sell products IDs. + //'parent_id' => 0, // integer Product parent ID. + //'purchase_note' => '', // string Optional note to send the customer after purchase. + 'categories' => $categories, // array List of categories. See Product - Categories properties + //'tags' => $tags, // array List of tags. See Product - Tags properties + 'images' => (!empty($images) ? $images : ''), // object List of images. See Product - Images properties + //'attributes' => $attributes, // array List of attributes. See Product - Attributes properties + //'default_attributes' => $default_attributes, // array Defaults variation attributes. See Product - Default attributes properties + //'menu_order' => 0, // integer Menu order, used to custom sort products. + //'meta_data' => $meta_data, // array Meta data. See Product - Meta data properties + ]; + + // Set tax + if (!empty($object->array_options["options_ecommerceng_tax_class_{$this->site->id}_{$conf->entity}"])) { + $productData['tax_status'] = 'taxable'; + $productData['tax_class'] = $object->array_options["options_ecommerceng_tax_class_{$this->site->id}_{$conf->entity}"]; + } + + try { + $result = $this->client->put("products/$remote_product_id", $productData); + } catch (HttpClientException $fault) { + $this->errors[] = $langs->trans('ECommerceWoocommerceUpdateRemoteProduct', $this->site->name, $fault->getCode() . ': ' . $fault->getMessage()); + dol_syslog(__METHOD__ . + ': Error:' . $langs->transnoentitiesnoconv('ECommerceWoocommerceUpdateRemoteProduct', $this->site->name, $fault->getCode() . ': ' . $fault->getMessage()) . + ' - Request:' . json_encode($fault->getRequest()) . ' - Response:' . json_encode($fault->getResponse()), LOG_ERR); + return false; + } + } + + dol_syslog(__METHOD__ . ": end", LOG_DEBUG); + return true; + } /** - * Return the magento's category tree + * Update the remote stock of product * - * @return array|boolean Array with categories or false if error + * @param int $remote_id Id of product on remote ecommerce + * @param MouvementStock $object MouvementStock object, enhanced with property qty_after be the trigger STOCK_MOVEMENT. + * + * @return boolean True or false */ - public function getRemoteCategoryTree() + public function updateRemoteStockProduct($remote_id, $object) { - dol_syslog("eCommerceRemoteAccessWoocommerce getRemoteCategoryTree session=".$this->session); - /* try { - //$result = $this->client->call($this->session, 'auguria_dolibarrapi_catalog_category.tree'); - $result = $this->client->call($this->session, 'catalog_category.tree'); - return $result; - //dol_syslog($this->client->__getLastRequest()); - } catch (SoapFault $fault) { - $this->errors[]=$this->site->name.': '.$fault->getMessage().'-'.$fault->getCode(); - dol_syslog(__METHOD__.': '.$fault->getMessage().'-'.$fault->getCode().'-'.$fault->getTraceAsString(), LOG_WARNING); - return false; - }*/ - return array(); - //var_dump($result); - dol_syslog("eCommerceRemoteAccessWoocommerce getRemoteCategoryTree end. Nb of record of result = ".count($result)); + dol_syslog(__METHOD__ . ": Update stock of the remote product ID $remote_id for MouvementStock ID {$object->id}, new qty: {$object->qty_after} for site ID {$this->site->id}", LOG_DEBUG); + global $langs; + + if (preg_match('/^(\d+)\|(\d+)$/', $remote_id, $idsProduct) == 1) { + // Variations + $variationData = [ + //'manage_stock' => '', // boolean Stock management at variation level. Default is false. + 'stock_quantity' => $object->qty_after, // integer Stock quantity. + 'in_stock' => $object->qty_after > 0, // boolean Controls whether or not the variation is listed as “in stock” or “out of stock” on the frontend. Default is true. + //'backorders' => '', // string If managing stock, this controls if backorders are allowed. Options: no, notify and yes. Default is no. + ]; + + try { + $result = $this->client->put("products/$idsProduct[1]/variations/$idsProduct[2]", $variationData); + } catch (HttpClientException $fault) { + $this->errors[] = $langs->trans('ECommerceWoocommerceUpdateRemoteStockProductVariation', $this->site->name, $fault->getCode() . ': ' . $fault->getMessage()); + dol_syslog(__METHOD__ . + ': Error:' . $langs->transnoentitiesnoconv('ECommerceWoocommerceUpdateRemoteStockProductVariation', $this->site->name, $fault->getCode() . ': ' . $fault->getMessage()) . + ' - Request:' . json_encode($fault->getRequest()) . ' - Response:' . json_encode($fault->getResponse()), LOG_ERR); + return false; + } + } else { + $productData = [ + //'manage_stock' => false, // boolean Stock management at product level. Default is false. + 'stock_quantity' => $object->qty_after, // integer Stock quantity. + 'in_stock' => $object->qty_after > 0, // boolean Controls whether or not the product is listed as “in stock” or “out of stock” on the frontend. Default is true. + //'backorders' => '', // string If managing stock, this controls if backorders are allowed. Options: no, notify and yes. Default is no. + ]; + + try { + $result = $this->client->put("products/$remote_id", $productData); + } catch (HttpClientException $fault) { + $this->errors[] = $langs->trans('ECommerceWoocommerceUpdateRemoteStockProduct', $this->site->name, $fault->getCode() . ': ' . $fault->getMessage()); + dol_syslog(__METHOD__ . + ': Error:' . $langs->transnoentitiesnoconv('ECommerceWoocommerceUpdateRemoteStockProduct', $this->site->name, $fault->getCode() . ': ' . $fault->getMessage()) . + ' - Request:' . json_encode($fault->getRequest()) . ' - Response:' . json_encode($fault->getResponse()), LOG_ERR); + return false; + } + } + + dol_syslog(__METHOD__ . ": end", LOG_DEBUG); + return true; } /** - * Return the magento's category att + * Update the remote company * - * @return array|boolean Array with categories or false if error + * @param int $remote_id Id of company on remote ecommerce + * @param Societe $object Societe object + * + * @return boolean True or false */ - /*public function getRemoteCategoryAtt() + public function updateRemoteSociete($remote_id, $object) { - dol_syslog("eCommerceRemoteAccessWoocommerce getRemoteCategoryAtt session=".$this->session); + dol_syslog(__METHOD__ . ": Update the remote company ID $remote_id for Dolibarr company ID {$object->id} for site ID {$this->site->id}", LOG_DEBUG); + global $langs; + + /* + // Customer - Meta data properties + $meta_data = [ + 'key' => '', // string Meta key. + 'value' => '', // string Meta value. + ]; + */ + $companyData = [ + 'email' => $object->email, // string The email address for the customer. MANDATORY + //'first_name' => '', // string Customer first name. + //'last_name' => $object->name, // string Customer last name. + //'username' => '', // string Customer login name. + //'password' => '', // string Customer password. + //'meta_data' => $meta_data, // array Meta data. See Customer - Meta data properties + ]; + try { - //$result = $this->client->call($this->session, 'auguria_dolibarrapi_catalog_category.tree'); - $result = $this->client->call($this->session, 'catalog_category_attribute.list'); - } catch (SoapFault $fault) { - $this->errors[]=$this->site->name.': '.$fault->getMessage().'-'.$fault->getCode(); - dol_syslog(__METHOD__.': '.$fault->getMessage().'-'.$fault->getCode().'-'.$fault->getTraceAsString(), LOG_WARNING); + $result = $this->client->put("customers/$remote_id", $companyData); + } catch (HttpClientException $fault) { + $this->errors[] = $langs->trans('ECommerceWoocommerceUpdateRemoteSociete', $this->site->name, $fault->getCode() . ': ' . $fault->getMessage()); + dol_syslog(__METHOD__ . + ': Error:' . $langs->transnoentitiesnoconv('ECommerceWoocommerceUpdateRemoteSociete', $this->site->name, $fault->getCode() . ': ' . $fault->getMessage()) . + ' - Request:' . json_encode($fault->getRequest()) . ' - Response:' . json_encode($fault->getResponse()), LOG_ERR); return false; } - dol_syslog("eCommerceRemoteAccessWoocommerce getRemoteCategoryAtt end"); - return $result; - }*/ + + dol_syslog(__METHOD__ . ": end", LOG_DEBUG); + return true; + } /** - * Return the magento's address id + * Update the remote contact * - * @param int $remote_thirdparty_id Id of thirdparty - * @return array|boolean Array with address id + * @param int $remote_id Id of contact on remote ecommerce + * @param Contact $object Contact object + * + * @return boolean True or false */ - public function getRemoteAddressIdForSociete($remote_thirdparty_id) + public function updateRemoteSocpeople($remote_id, $object) { - dol_syslog("eCommerceRemoteAccessWoocommerce getRemoteAddressIdForSociete session=".$this->session); - $result = array($remote_thirdparty_id); - dol_syslog("eCommerceRemoteAccessWoocommerce getRemoteAddressIdForSociete end"); - return $result; - } + dol_syslog(__METHOD__ . ": Update the remote contact ID $remote_id for Dolibarr contact ID {$object->id} for site ID {$this->site->id}", LOG_DEBUG); + global $conf, $langs; + // Get societe + //require_once DOL_DOCUMENT_ROOT . '/societe/class/societe.class.php'; + //$societe = new Societe($this->db); + //$societe->fetch($object->socid); + + $billingName = (empty($conf->global->ECOMMERCENG_BILLING_CONTACT_NAME) ? 'Billing' : $conf->global->ECOMMERCENG_BILLING_CONTACT_NAME); // Contact name treated as billing address. + $shippingName = (empty($conf->global->ECOMMERCENG_SHIPPING_CONTACT_NAME) ? 'Shipping' : $conf->global->ECOMMERCENG_SHIPPING_CONTACT_NAME); // Contact name treated as shipping address. + + if ($object->lastname == $billingName) { + $address = explode("\n", $object->address); + // Billing + $contactData = [ + 'billing' => [ + //'first_name' => '', // string First name. + //'last_name' => '', // string Last name. + //'company' => $societe->name, // string Company name. + 'address_1' => isset($address[0]) ? $address[0] : '', // string Address line 1 + 'address_2' => isset($address[1]) ? implode(" ", array_slice($address, 1)) : '', // string Address line 2 + 'city' => $object->town, // string City name. + //'state' => '', // string ISO code or name of the state, province or district. + 'postcode' => $object->zip, // string Postal code. + 'country' => getCountry($object->country_id, 2), // string ISO code of the country. + 'email' => $object->email, // string Email address. + 'phone' => $object->phone_pro, // string Phone number. + ], + ]; + } elseif ($object->lastname == $shippingName) { + $address = explode("\n", $object->address); + // Shipping + $contactData = [ + 'shipping' => [ + //'first_name' => '', // string First name. + //'last_name' => '', // string Last name. + //'company' => $societe->name, // string Company name. + 'address_1' => isset($address[0]) ? $address[0] : '', // string Address line 1 + 'address_2' => isset($address[1]) ? implode(" ", array_slice($address, 1)) : '', // string Address line 2 + 'city' => $object->town, // string City name. + //'state' => '', // string ISO code or name of the state, province or district. + 'postcode' => $object->zip, // string Postal code. + 'country' => getCountry($object->country_id, 2), // string ISO code of the country. + ], + ]; + } + + if (isset($contactData)) { + if (preg_match('/^(\d+)\|(\d+)$/', $remote_id, $idsCustomer) == 1) { + try { + $result = $this->client->put("customers/$idsCustomer[1]", $contactData); + } catch (HttpClientException $fault) { + $this->errors[] = $langs->trans('ECommerceWoocommerceUpdateRemoteSocpeople', $this->site->name, $fault->getCode() . ': ' . $fault->getMessage()); + dol_syslog(__METHOD__ . + ': Error:' . $langs->transnoentitiesnoconv('ECommerceWoocommerceUpdateRemoteSocpeople', $this->site->name, $fault->getCode() . ': ' . $fault->getMessage()) . + ' - Request:' . json_encode($fault->getRequest()) . ' - Response:' . json_encode($fault->getResponse()), LOG_ERR); + return false; + } + } + } + + dol_syslog(__METHOD__ . ": end", LOG_DEBUG); + return true; + } /** - * Return content of one category + * Update the remote order * - * @param int $category_id Remote category id - * @return boolean|unknown Return + * @param int $remote_id Id of order on remote ecommerce + * @param Commande $object Commande object + * + * @return boolean True or false */ - public function getCategoryData($category_id) + public function updateRemoteCommande($remote_id, $object) { - $result = array(); - dol_syslog("eCommerceRemoteAccessWoocommerce getCategoryData session=".$this->session); -/* try { - //$result = $this->client->call($this->session, 'auguria_dolibarrapi_catalog_category.tree'); - $result = $this->client->call($this->session, 'catalog_category.info', array('categoryId'=>$category_id)); - //dol_syslog($this->client->__getLastRequest()); - } catch (SoapFault $fault) { - $this->errors[]=$this->site->name.': '.$fault->getMessage().'-'.$fault->getCode(); - dol_syslog($this->client->__getLastRequestHeaders(), LOG_WARNING); - dol_syslog($this->client->__getLastRequest(), LOG_WARNING); - dol_syslog(__METHOD__.': '.$fault->getMessage().'-'.$fault->getCode().'-'.$fault->getTraceAsString(), LOG_WARNING); - return false; - }*/ - dol_syslog("eCommerceRemoteAccessWoocommerce getCategoryData end"); - return $result; + dol_syslog(__METHOD__ . ": Update the remote order ID $remote_id for Dolibarr order ID {$object->id} for site ID {$this->site->id}", LOG_DEBUG); + global $langs; + + $status = ''; + switch ($object->statut) { + //case Commande::STOCK_NOT_ENOUGH_FOR_ORDER: $status = ''; break; + case Commande::STATUS_CANCELED: $status = 'cancelled'; break; + case Commande::STATUS_DRAFT: $status = 'on-hold'; break; + case Commande::STATUS_VALIDATED: $status = 'processing'; break; + //case Commande::STATUS_ACCEPTED: $status = 'processing'; break; + case Commande::STATUS_SHIPMENTONPROCESS: $status = 'processing'; break; + case Commande::STATUS_CLOSED: $status = 'completed'; break; + } + + if (!empty($status)) { + $orderData = [ + 'status' => $status, // string Order status. Options: pending, processing, on-hold, completed, cancelled, refunded and failed. + ]; + + try { + $result = $this->client->put("orders/$remote_id", $orderData); + } catch (HttpClientException $fault) { + $this->errors[] = $langs->trans('ECommerceWoocommerceUpdateRemoteCommande', $this->site->name, $fault->getCode() . ': ' . $fault->getMessage()); + dol_syslog(__METHOD__ . + ': Error:' . $langs->transnoentitiesnoconv('ECommerceWoocommerceUpdateRemoteCommande', $this->site->name, $fault->getCode() . ': ' . $fault->getMessage()) . + ' - Request:' . json_encode($fault->getRequest()) . ' - Response:' . json_encode($fault->getResponse()), LOG_ERR); + return false; + } + } + + dol_syslog(__METHOD__ . ": end", LOG_DEBUG); + return true; } /** - * Return the magento's order + * Desactivated because is not supported by woocommerce. * - * @param int $remoteCommandeId Id of remote order - * @return object Order + * @param int $remote_id Id of invoice on remote ecommerce + * @param Facture $object Invoice object + * + * @return boolean True or false */ - public function getRemoteCommande($remoteCommandeId) + public function updateRemoteFacture($remote_id, $object) { - $commande = array(); - try { - dol_syslog("getCommande begin"); - $commande = $this->client->get("orders/$remoteCommandeId"); - dol_syslog("getCommande end"); - } catch (HttpClientException $fault) { - if ($fault->getCode() != 404) { - $this->errors[]=$fault->getMessage().'-'.$fault->getCode(); - dol_syslog(__METHOD__.': '.$fault->getMessage().'-'.$fault->getCode().'-'.$fault->getTraceAsString(), LOG_WARNING); - } - } - return $commande; + dol_syslog(__METHOD__ . ": Desactivated for site ID {$this->site->id}", LOG_DEBUG); + return true; } - + /** + * Desactivated because is not supported by woocommerce. + * + * @param int $livraison Object shipment ? + * @param int $remote_order_id Id of remote order + * + * @return boolean True or false + */ + public function createRemoteLivraison($livraison, $remote_order_id) + { + dol_syslog(__METHOD__ . ": Desactivated for site ID {$this->site->id}", LOG_DEBUG); + return $remote_order_id; + } /** - * Update the remote product + * Create product + * + * @param Product $object Object product * - * @param int $remote_id Id of product on remote ecommerce - * @param Product $object Product object * @return boolean True or false */ - public function updateRemoteProduct($remote_id, $object) + public function createRemoteProduct($object) { - dol_syslog("eCommerceRemoteAccessWoocommerce updateRemoteProduct session=".$this->session." remote_id=".$remote_id." object->id=".$object->id); + dol_syslog(__METHOD__ . ": Create product from Dolibarr product ID {$object->id} for site ID {$this->site->id}", LOG_DEBUG); + global $conf, $langs, $user; + // Set weight $totalWeight = $object->weight; if ($object->weight_units < 50) // >50 means a standard unit (power of 10 of official unit), > 50 means an exotic unit (like inch) { - $trueWeightUnit=pow(10, $object->weight_units); + $trueWeightUnit = pow(10, $object->weight_units); $totalWeight = sprintf("%f", $object->weight * $trueWeightUnit); } + // Product - Meta data properties + $object->fetch_optionals(); - try { - if (preg_match('/^(\d+)\|(\d+)$/', $remote_id, $idsProduct) == 1) { - // Variations -/* - // Product variation - Downloads properties - $downloads = [ - [ - 'name' => '', // string File name. - 'file' => '', // string File URL. - ], - ]; -*/ -/* - // Product variation - Dimensions properties - $dimensions = [ - 'length' => '', // string Product length (cm). - 'width' => '', // string Product width (cm). - 'height' => '', // string Product height (cm). - ]; -*/ -/* - // Product variation - Image properties - $images = [ - [ - 'id' => 0, // integer Image ID. Not required - 'src' => '', // string Image URL. - 'name' => '', // string Image name. - 'alt' => '', // string Image alternative text. - 'position' => 0, // integer Image position. 0 means that the image is featured. - ], - ]; -*/ -/* - // Product variation - Attributes properties - $attributes = [ - [ - 'id' => 0, // integer Attribute ID. - 'name' => '', // string Attribute name. - 'option' => '', // string Selected attribute term name. - ], - ]; -*/ -/* - // Product variation - Meta data properties - $meta_data = [ - 'key' => '', // string Meta key. - 'value' => '', // string Meta value. - ]; -*/ - $variationData = array( - 'description' => $object->description, // string Variation description. - 'sku' => $object->ref, // string Unique identifier. - 'regular_price' => $object->price, // string Variation regular price. - //'sale_price' => '', // string Variation sale price. - //'date_on_sale_from' => '', // date-time Start date of sale price, in the site’s timezone. - //'date_on_sale_from_gmt' => '', // date-time Start date of sale price, as GMT. - //'date_on_sale_to' => '', // date-time End date of sale price, in the site’s timezone. - //'date_on_sale_to_gmt' => '', // date-time End date of sale price, in the site’s timezone. - //'visible' => '', // boolean Define if the attribute is visible on the “Additional information” tab in the product’s page. Default is true. - //'virtual' => $object->type == Product::TYPE_SERVICE, // boolean If the variation is virtual. Default is false. - //'downloadable' => '', // boolean If the variation is downloadable. Default is false. - //'downloads' => $downloads, // array List of downloadable files. See Product variation - Downloads properties - //'download_limit' => '', // integer Number of times downloadable files can be downloaded after purchase. Default is -1. - //'download_expiry' => '', // integer Number of days until access to downloadable files expires. Default is -1. - //'tax_status' => '', // string Tax status. Options: taxable, shipping and none. Default is taxable. - //'tax_class' => '', // string Tax class. - //'manage_stock' => '', // boolean Stock management at variation level. Default is false. - //'stock_quantity' => '', // integer Stock quantity. - //'in_stock' => '', // boolean Controls whether or not the variation is listed as “in stock” or “out of stock” on the frontend. Default is true. - //'backorders' => '', // string If managing stock, this controls if backorders are allowed. Options: no, notify and yes. Default is no. - 'weight' => $totalWeight, // string Variation weight (kg). - //'dimensions' => $dimensions, // object Variation dimensions. See Product variation - Dimensions properties - //'shipping_class' => '', // string Shipping class slug. - //'image' => $images, // object Variation image data. See Product variation - Image properties - //'attributes' => $attributes, // array List of attributes. See Product variation - Attributes properties - //'menu_order' => '', // integer Menu order, used to custom sort products. - //'meta_data' => $meta_data, // array Meta data. See Product variation - Meta data properties - ); + // Price + if (!empty($conf->global->PRODUIT_MULTIPRICES)) { + $price_level = !empty($this->site->price_level) ? $this->site->price_level : 1; + $price = $object->multiprices[$price_level]; + } else { + $price = $object->price; + } - // Product - // 'name' => $object->label, // string Product name. - // 'status' => $object->status ? 'publish' : 'pending', // string Product status (post status). Options: draft, pending, private and publish. Default is publish. + /* + // Product - Downloads properties + $downloads = [ + [ + 'name' => '', // string File name. + 'file' => '', // string File URL. + ], + ]; - $result = $this->client->put("products/$idsProduct[1]/variations/$idsProduct[2]", $variationData); - } else { - // Product -/* - // Product - Downloads properties - $downloads = [ - [ - 'name' => '', // string File name. - 'file' => '', // string File URL. - ], - ]; -*/ -/* - // Product - Dimensions properties - $dimensions = [ - 'length' => '', // string Product length (cm). - 'width' => '', // string Product width (cm). - 'height' => '', // string Product height (cm). - ]; -*/ -/* - // Product - Categories properties - $categories = [ - [ - 'id' => 0, // integer Category ID. - ], - ]; -*/ -/* - // Product - Tags properties - $tags = [ - [ - 'id' => 0, // integer Tag ID. - ], - ]; -*/ -/* - // Product - Images properties - $images = [ - [ - 'id' => 0, // integer Image ID. Not required - 'src' => '', // string Image URL. - 'name' => '', // string Image name. - 'alt' => '', // string Image alternative text. - 'position' => 0, // integer Image position. 0 means that the image is featured. - ], - ]; -*/ -/* - // Product - Attributes properties - $attributes = [ - [ - 'id' => 0, // integer Attribute ID. Not required - 'name' => '', // string Attribute name. - 'position' => 0, // integer Attribute position. - 'visible' => false, // boolean Define if the attribute is visible on the “Additional information” tab in the product’s page. Default is false. - 'variation' => false, // boolean Define if the attribute can be used as variation. Default is false. - 'options' => [], // array List of available term names of the attribute. - ], - ]; -*/ -/* - // Product - Default attributes properties - $default_attributes = [ - 'id' => 0, // integer Attribute ID. Not required - 'name' => '', // string Attribute name. - 'option' => '', // string Selected attribute term name. - ]; -*/ -/* - // Product - Meta data properties - $meta_data = [ - 'key' => '', // string Meta key. - 'value' => '', // string Meta value. - ]; -*/ - $productData = array( - 'name' => $object->label, // string Product name. - //'slug' => '', // string Product slug. - //'type' => '', // string Product type. Options: simple, grouped, external and variable. Default is simple. - 'status' => $object->status ? 'publish' : 'pending', // string Product status (post status). Options: draft, pending, private and publish. Default is publish. - //'featured' => false, // boolean Featured product. Default is false. - //'catalog_visibility' => '', // string Catalog visibility. Options: visible, catalog, search and hidden. Default is visible. - 'description' => $object->description, // string Product description. - //'short_description' => '', // string Product short description. - 'sku' => $object->ref, // string Unique identifier. - 'regular_price' => $object->price, // string Product regular price. - //'sale_price' => '', // string Product sale price. - //'date_on_sale_from' => '', // date-time Start date of sale price, in the site’s timezone. - //'date_on_sale_from_gmt' => '', // date-time Start date of sale price, as GMT. - //'date_on_sale_to' => '', // date-time End date of sale price, in the site’s timezone. - //'date_on_sale_to_gmt' => '', // date-time End date of sale price, in the site’s timezone. - //'virtual' => $object->type == Product::TYPE_SERVICE, // boolean If the product is virtual. Default is false. - //'downloadable' => false, // boolean If the product is downloadable. Default is false. - //'downloads' => $downloads, // array List of downloadable files. See Product - Downloads properties - //'download_limit' => -1, // integer Number of times downloadable files can be downloaded after purchase. Default is -1. - //'download_expiry' => -1, // integer Number of days until access to downloadable files expires. Default is -1. - //'external_url' => '', // string Product external URL. Only for external products. - //'button_text' => '', // string Product external button text. Only for external products. - //'tax_status' => '', // string Tax status. Options: taxable, shipping and none. Default is taxable. - //'tax_class' => '', // string Tax class. - //'manage_stock' => false, // boolean Stock management at product level. Default is false. - //'stock_quantity' => $object->stock_reel, // integer Stock quantity. - //'in_stock' => $object->stock_reel > 0, // boolean Controls whether or not the product is listed as “in stock” or “out of stock” on the frontend. Default is true. - //'backorders' => '', // string If managing stock, this controls if backorders are allowed. Options: no, notify and yes. Default is no. - //'sold_individually' => false, // boolean Allow one item to be bought in a single order. Default is false. - 'weight' => $totalWeight, // string Product weight (kg). - //'dimensions' => $dimensions, // object Product dimensions. See Product - Dimensions properties - //'shipping_class' => '', // string Shipping class slug. - //'reviews_allowed' => true, // boolean Allow reviews. Default is true. - //'upsell_ids' => [], // array List of up-sell products IDs. - //'cross_sell_ids' => [], // array List of cross-sell products IDs. - //'parent_id' => 0, // integer Product parent ID. - //'purchase_note' => '', // string Optional note to send the customer after purchase. - //'categories' => $categories, // array List of categories. See Product - Categories properties - //'tags' => $tags, // array List of tags. See Product - Tags properties - //'images' => $images, // object List of images. See Product - Images properties - //'attributes' => $attributes, // array List of attributes. See Product - Attributes properties - //'default_attributes' => $default_attributes, // array Defaults variation attributes. See Product - Default attributes properties - //'menu_order' => 0, // integer Menu order, used to custom sort products. - //'meta_data' => $meta_data, // array Meta data. See Product - Meta data properties - ); + // Product - Dimensions properties + $dimensions = [ + 'length' => '', // string Product length (cm). + 'width' => '', // string Product width (cm). + 'height' => '', // string Product height (cm). + ]; - $result = $this->client->put("products/$remote_id", $productData); + // Product - Categories properties + $categories = [ + [ + 'id' => 0, // integer Category ID. + ], + ]; + + // Product - Tags properties + $tags = [ + [ + 'id' => 0, // integer Tag ID. + ], + ]; + + // Product - Images properties + $images = [ + [ + 'id' => 0, // integer Image ID. Not required + 'src' => '', // string Image URL. + 'name' => '', // string Image name. + 'alt' => '', // string Image alternative text. + 'position' => 0, // integer Image position. 0 means that the image is featured. + ], + ]; + + // Product - Attributes properties + $attributes = [ + [ + 'id' => 0, // integer Attribute ID. Not required + 'name' => '', // string Attribute name. + 'position' => 0, // integer Attribute position. + 'visible' => false, // boolean Define if the attribute is visible on the “Additional information” tab in the product’s page. Default is false. + 'variation' => false, // boolean Define if the attribute can be used as variation. Default is false. + 'options' => [], // array List of available term names of the attribute. + ], + ]; + + // Product - Default attributes properties + $default_attributes = [ + 'id' => 0, // integer Attribute ID. Not required + 'name' => '', // string Attribute name. + 'option' => '', // string Selected attribute term name. + ]; + + // Product - Meta data properties + $meta_data = [ + 'key' => '', // string Meta key. + 'value' => '', // string Meta value. + ]; + */ + + // Get categories + $eCommerceCategory = new eCommerceCategory($this->db); + $cat = new Categorie($this->db); + $categories_list = $cat->containing($object->id, 'product'); + $categories = []; + foreach ($categories_list as $category) { + if ($this->site->fk_cat_product != $category->id) { + $ret = $eCommerceCategory->fetchByFKCategory($category->id, $this->site->id); + if ($ret > 0) { + $categories[] = ['id' => $eCommerceCategory->remote_id]; + } } + } + + $status = $object->array_options["options_ecommerceng_wc_status_{$this->site->id}_{$conf->entity}"]; + + // Product + $productData = [ + 'name' => $object->label, // string Product name. + //'slug' => '', // string Product slug. + //'type' => '', // string Product type. Options: simple, grouped, external and variable. Default is simple. + 'status' => (!empty($status) ? $status : ''), //$object->status ? 'publish' : 'pending', // string Product status (post status). Options: draft, pending, private and publish. Default is publish. + //'featured' => false, // boolean Featured product. Default is false. + //'catalog_visibility' => '', // string Catalog visibility. Options: visible, catalog, search and hidden. Default is visible. + 'description' => $object->array_options["options_ecommerceng_description_{$conf->entity}"], // string Product description. + 'short_description' => $object->array_options["options_ecommerceng_short_description_{$conf->entity}"], // string Product short description. + 'sku' => $object->ref, // string Unique identifier. + 'regular_price' => $price, // string Product regular price. + //'sale_price' => '', // string Product sale price. + //'date_on_sale_from' => '', // date-time Start date of sale price, in the site’s timezone. + //'date_on_sale_from_gmt' => '', // date-time Start date of sale price, as GMT. + //'date_on_sale_to' => '', // date-time End date of sale price, in the site’s timezone. + //'date_on_sale_to_gmt' => '', // date-time End date of sale price, in the site’s timezone. + //'virtual' => $object->type == Product::TYPE_SERVICE, // boolean If the product is virtual. Default is false. + //'downloadable' => false, // boolean If the product is downloadable. Default is false. + //'downloads' => $downloads, // array List of downloadable files. See Product - Downloads properties + //'download_limit' => -1, // integer Number of times downloadable files can be downloaded after purchase. Default is -1. + //'download_expiry' => -1, // integer Number of days until access to downloadable files expires. Default is -1. + //'external_url' => '', // string Product external URL. Only for external products. + //'button_text' => '', // string Product external button text. Only for external products. + 'tax_status' => 'none', // string Tax status. Options: taxable, shipping and none. Default is taxable. + //'tax_class' => '', // string Tax class. + //'manage_stock' => false, // boolean Stock management at product level. Default is false. + //'stock_quantity' => $object->stock_reel, // integer Stock quantity. + //'in_stock' => $object->stock_reel > 0, // boolean Controls whether or not the product is listed as “in stock” or “out of stock” on the frontend. Default is true. + //'backorders' => '', // string If managing stock, this controls if backorders are allowed. Options: no, notify and yes. Default is no. + //'sold_individually' => false, // boolean Allow one item to be bought in a single order. Default is false. + 'weight' => (!empty($totalWeight)?$totalWeight:''), // string Product weight (kg). + //'dimensions' => $dimensions, // object Product dimensions. See Product - Dimensions properties + //'shipping_class' => '', // string Shipping class slug. + //'reviews_allowed' => true, // boolean Allow reviews. Default is true. + //'upsell_ids' => [], // array List of up-sell products IDs. + //'cross_sell_ids' => [], // array List of cross-sell products IDs. + //'parent_id' => 0, // integer Product parent ID. + //'purchase_note' => '', // string Optional note to send the customer after purchase. + 'categories' => $categories, // array List of categories. See Product - Categories properties + //'tags' => $tags, // array List of tags. See Product - Tags properties + //'images' => $images, // object List of images. See Product - Images properties + //'attributes' => $attributes, // array List of attributes. See Product - Attributes properties + //'default_attributes' => $default_attributes, // array Defaults variation attributes. See Product - Default attributes properties + //'menu_order' => 0, // integer Menu order, used to custom sort products. + //'meta_data' => $meta_data, // array Meta data. See Product - Meta data properties + ]; + + // Set tax + if (!empty($object->array_options["options_ecommerceng_tax_class_{$this->site->id}_{$conf->entity}"])) { + $productData['tax_status'] = 'taxable'; + $productData['tax_class'] = $object->array_options["options_ecommerceng_tax_class_{$this->site->id}_{$conf->entity}"]; + } - dol_syslog("eCommerceRemoteAccessWoocommerce updateRemoteProduct end"); - return true; + try { + $res = $this->client->post("products", $productData); } catch (HttpClientException $fault) { - $this->errors[]=$fault->getMessage().'-'.$fault->getCode(); - dol_syslog(__METHOD__.': '.$fault->getMessage().'-'.$fault->getCode().'-'.$fault->getTraceAsString(), LOG_WARNING); + $this->errors[] = $langs->trans('ECommerceWoocommerceCreateRemoteProduct', $this->site->name, $fault->getCode() . ': ' . $fault->getMessage()); + dol_syslog(__METHOD__ . + ': Error:' . $langs->transnoentitiesnoconv('ECommerceWoocommerceCreateRemoteProduct', $this->site->name, $fault->getCode() . ': ' . $fault->getMessage()) . + ' - Request:' . json_encode($fault->getRequest()) . ' - Response:' . json_encode($fault->getResponse()), LOG_ERR); return false; } + + // Create remote link + $eCommerceProduct = new eCommerceProduct($this->db); + $eCommerceProduct->fk_product = $object->id; + $eCommerceProduct->fk_site = $this->site->id; + $eCommerceProduct->remote_id = $res['id']; + $res = $eCommerceProduct->create($user); + if ($res < 0) { + $this->errors[] = $langs->trans('ECommerceWoocommerceCreateRemoteProductLink', $object->id, $this->site->name, $eCommerceProduct->error); + dol_syslog(__METHOD__ . ': Error:' . $langs->transnoentitiesnoconv('ECommerceWoocommerceCreateRemoteProductLink', $object->id, $this->site->name, $eCommerceProduct->error), LOG_ERR); + return false; + } + + dol_syslog(__METHOD__ . ": end", LOG_DEBUG); + return true; } /** - * Update the remote stock of product + * Send a file for remote order * - * @param int $remote_id Id of product on remote ecommerce - * @param Movement $object Movement object, enhanced with property qty_after be the trigger STOCK_MOVEMENT. - * @return boolean True or false + * @param int $order_remote_id Id of order on remote ecommerce + * @param int $company_remote_id Id of company on remote ecommerce + * @param Object $object Object (invoice or shipping) + * @param string $file File path + * @param Translate $outputlangs Lang output object + * + * @return bool */ - public function updateRemoteStockProduct($remote_id, $object) + public function sendFileForCommande($order_remote_id, $company_remote_id, $object, $file, $outputlangs) { - dol_syslog("eCommerceRemoteAccessWoocommerce updateRemoteStockProduct session=".$this->session." product remote_id=".$remote_id." movement object->id=".$object->id.", new qty=".$object->qty_after); + dol_syslog(__METHOD__ . ": Send file '$file' for remote order ID $order_remote_id for site ID {$this->site->id}", LOG_DEBUG); + global $langs; + + // Send file to WordPress + $result = $this->worpressclient->postmedia("media", $file, [ + 'slug' => $order_remote_id . '_' . $object->element, + 'author' => $company_remote_id, + 'post' => $order_remote_id, + 'ping_status' => 'closed', + 'comment_status' => 'closed', + ]); + if ($result === null) { + $this->errors[] = $langs->trans('ECommerceWoocommerceSendFileForCommandeInWordpress', $order_remote_id, $this->site->name, implode('; ', $this->worpressclient->errors)); + dol_syslog(__METHOD__ . ': Error:' . $langs->transnoentitiesnoconv('ECommerceWoocommerceSendFileForCommandeInWordpress', $order_remote_id, $this->site->name, implode('; ', $this->worpressclient->errors)), LOG_ERR); + return false; + } - // $object->qty is the qty of movement + // Set meta data in remote commande + $commandeData = [ + 'meta_data' => [ + [ + 'key' => 'file_for_' . $object->element, + 'value' => [ + 'id' => $result['id'], + 'link' => $result['link'], + 'source_url' => $result['source_url'], + ], + ], + ] + ]; try { - if (preg_match('/^(\d+)\|(\d+)$/', $remote_id, $idsProduct) == 1) { - // Variations - $variationData = array( - //'manage_stock' => '', // boolean Stock management at variation level. Default is false. - 'stock_quantity' => $object->qty_after, // integer Stock quantity. - 'in_stock' => $object->qty_after > 0, // boolean Controls whether or not the variation is listed as “in stock” or “out of stock” on the frontend. Default is true. - //'backorders' => '', // string If managing stock, this controls if backorders are allowed. Options: no, notify and yes. Default is no. - ); + $result = $this->client->put("orders/$order_remote_id", $commandeData); + } catch (HttpClientException $fault) { + $this->errors[] = $langs->trans('ECommerceWoocommerceSendFileForCommande', $this->site->name, $fault->getCode() . ': ' . $fault->getMessage()); + dol_syslog(__METHOD__ . + ': Error:' . $langs->transnoentitiesnoconv('ECommerceWoocommerceSendFileForCommande', $this->site->name, $fault->getCode() . ': ' . $fault->getMessage()) . + ' - Request:' . json_encode($fault->getRequest()) . ' - Response:' . json_encode($fault->getResponse()), LOG_ERR); + return false; + } - // Product - // 'name' => $object->label, // string Product name. - // 'status' => $object->status ? 'publish' : 'pending', // string Product status (post status). Options: draft, pending, private and publish. Default is publish. + dol_syslog(__METHOD__ . ": end", LOG_DEBUG); + return true; + } - $result = $this->client->put("products/$idsProduct[1]/variations/$idsProduct[2]", $variationData); - } else { - $productData = array( - //'manage_stock' => false, // boolean Stock management at product level. Default is false. - 'stock_quantity' => $object->qty_after, // integer Stock quantity. - 'in_stock' => $object->qty_after > 0, // boolean Controls whether or not the product is listed as “in stock” or “out of stock” on the frontend. Default is true. - //'backorders' => '', // string If managing stock, this controls if backorders are allowed. Options: no, notify and yes. Default is no. - ); + /** + * Get tax rate from tax class name + * + * @param string $tax_class Tax class name + * @param string $tax_status Tax status + * + * @return float Tax rate + */ + private function getTaxRate($tax_class, $tax_status = 'taxable') + { + //dol_syslog(__METHOD__ . ": Get tax rate, tax_classe: $tax_class, tax_status: $tax_status", LOG_DEBUG); + global $conf, $mysoc; - $result = $this->client->put("products/$remote_id", $productData); + $tax_rate = 0; + + // $tax_status => Tax status. Options: taxable, shipping and none. Default is taxable + if ($tax_status != 'none') { + $tax_class = !empty($tax_class) ? $tax_class : 'standard'; + $tax_rate = ''; + + // Retrieve all woocommerce tax classes + if (!isset($this->woocommerceTaxes) || !isset($this->woocommerceTaxes['classes'][$tax_class])) { + $this->setWoocommerceTaxes(); } - dol_syslog("eCommerceRemoteAccessWoocommerce updateRemoteStockProduct end"); - return true; - } catch (HttpClientException $fault) { - $this->errors[]=$fault->getMessage().'-'.$fault->getCode(); - dol_syslog(__METHOD__.': '.$fault->getMessage().'-'.$fault->getCode().'-'.$fault->getTraceAsString(), LOG_WARNING); - return false; + // Get woocommerce tax if one only + if (isset($this->woocommerceTaxes['classes'][$tax_class]) && count($this->woocommerceTaxes['classes'][$tax_class]) == 1) { + $tax_rate = $this->woocommerceTaxes['classes'][$tax_class]; + $tax_rate = array_values($tax_rate); + $tax_rate = doubleval($tax_rate[0]['rate']); + + // Get near dolibarr tax for woocommerce tax rate + $tax = $this->_getClosestDolibarrTaxRate($tax_rate); + if (isset($tax)) { + $tax_rate = $tax; + } + } + + if ($tax_rate == '') { + $tax_rate = $conf->global->ECOMMERCE_WOOCOMMERCE_DEFAULT_TVA; + } } + + //dol_syslog(__METHOD__ . ": end, return $tax_rate", LOG_DEBUG); + return $tax_rate; } /** - * Update the remote societe + * Get tax class for show in extrafields * - * @param int $remote_id Id of societe on remote ecommerce - * @param Societe $object Societe object - * @return boolean True or false + * @param string $tax_class Tax class name + * @param string $tax_status Tax status + * + * @return string Tax class name */ - public function updateRemoteSociete($remote_id, $object) + private function getTaxClass($tax_class, $tax_status = 'taxable') { - dol_syslog("eCommerceRemoteAccessWoocommerce updateRemoteSociete session=".$this->session." remote_id=".$remote_id." object->id=".$object->id); -/* - // Customer - Meta data properties - $meta_data = [ - 'key' => '', // string Meta key. - 'value' => '', // string Meta value. - ]; -*/ - try { - $societeData = array( - 'email' => $object->email, // string The email address for the customer. MANDATORY - //'first_name' => '', // string Customer first name. - //'last_name' => $object->name, // string Customer last name. - //'username' => '', // string Customer login name. - //'password' => '', // string Customer password. - //'meta_data' => $meta_data, // array Meta data. See Customer - Meta data properties - ); - - $result = $this->client->put("customers/$remote_id", $societeData); + //dol_syslog(__METHOD__ . ": Get tax class name, tax_class: $tax_class, tax_status: $tax_status", LOG_DEBUG); - dol_syslog("eCommerceRemoteAccessWoocommerce updateRemoteSociete end"); - return true; - } catch (HttpClientException $fault) { - $this->errors[]=$fault->getMessage().'-'.$fault->getCode(); - dol_syslog(__METHOD__.': '.$fault->getMessage().'-'.$fault->getCode().'-'.$fault->getTraceAsString(), LOG_WARNING); - return false; + // $tax_status => Tax status. Options: taxable, shipping and none. Default is taxable + if ($tax_status != 'none') { + $tax_class = !empty($tax_class) ? $tax_class : 'standard'; + } else { + $tax_class = ''; } + + //dol_syslog(__METHOD__ . ": end, return $tax_class", LOG_DEBUG); + return $tax_class; } /** - * Update the remote contact + * Calcul tax rate and return the closest dolibarr tax rate. * - * @param int $remote_id Id of contact on remote ecommerce - * @param Contact $object Contact object - * @return boolean True or false + * @param float $priceHT Price HT + * @param float $taxAmount Tax amount + * + * @return float Tax rate */ - public function updateRemoteSocpeople($remote_id, $object) + private function getClosestDolibarrTaxRate($priceHT, $taxAmount) { - global $conf; + //dol_syslog(__METHOD__ . ": Get closest dolibarr tax rate, priceHT: $priceHT, priceHT: $taxAmount", LOG_DEBUG); + $tax_rate = 0; + if ($taxAmount != 0) { + //calcul tax rate from remote site + $shipping_tax_rate = ($taxAmount / $priceHT) * 100; - dol_syslog("eCommerceRemoteAccessWoocommerce updateRemoteSocpeople session=".$this->session." remote_id=".$remote_id." object->id=".$object->id); + // Get near dolibarr tax for woocommerce tax rate + $tax = $this->_getClosestDolibarrTaxRate($shipping_tax_rate); + if (isset($tax)) { + $tax_rate = $tax; + } + } - // Get societe - require_once DOL_DOCUMENT_ROOT . '/societe/class/societe.class.php'; - $societe = new Societe($this->db); - $societe->fetch($object->socid); + //dol_syslog(__METHOD__ . ": end, return $tax_rate", LOG_DEBUG); + return $tax_rate; + } - $billingName = (empty($conf->global->ECOMMERCENG_BILLING_CONTACT_NAME)?'Billing':$conf->global->ECOMMERCENG_BILLING_CONTACT_NAME); // Contact name treated as billing address. - $shippingName = (empty($conf->global->ECOMMERCENG_SHIPPING_CONTACT_NAME)?'Shipping':$conf->global->ECOMMERCENG_SHIPPING_CONTACT_NAME); // Contact name treated as shipping address. + /** + * Retrieve all Dolibarr tax rates + * + * @return void + */ + private function setDolibarrTaxes() + { + //dol_syslog(__METHOD__ . ": Retrieve all Dolibarr tax rates", LOG_DEBUG); - try { - if ($object->lastname == $billingName) { - $address = explode("\n", $object->address); - // Billing - $contactData = [ - 'billing' => [ - //'first_name' => '', // string First name. - //'last_name' => '', // string Last name. - //'company' => $societe->name, // string Company name. - 'address_1' => isset($address[0])?$address[0]:'', // string Address line 1 - 'address_2' => isset($address[1])?implode(" ", array_slice($address, 1)):'', // string Address line 2 - 'city' => $object->town, // string City name. - //'state' => '', // string ISO code or name of the state, province or district. - 'postcode' => $object->zip, // string Postal code. - 'country' => getCountry($object->country_id, 2), // string ISO code of the country. - 'email' => $object->email, // string Email address. - 'phone' => $object->phone_pro, // string Phone number. - ], - ]; - } elseif ($object->lastname == $shippingName) { - $address = explode("\n", $object->address); - // Shipping - $contactData = [ - 'shipping' => [ - //'first_name' => '', // string First name. - //'last_name' => '', // string Last name. - //'company' => $societe->name, // string Company name. - 'address_1' => isset($address[0])?$address[0]:'', // string Address line 1 - 'address_2' => isset($address[1])?implode(" ", array_slice($address, 1)):'', // string Address line 2 - 'city' => $object->town, // string City name. - //'state' => '', // string ISO code or name of the state, province or district. - 'postcode' => $object->zip, // string Postal code. - 'country' => getCountry($object->country_id, 2), // string ISO code of the country. - ], - ]; - } + $resql = $this->db->query("SELECT DISTINCT taux FROM ".MAIN_DB_PREFIX."c_tva ORDER BY taux DESC"); + if ($resql) { + $taxesTable = []; - if (isset($contactData)) { - if (preg_match('/^(\d+)\|(\d+)$/', $remote_id, $idsCustomer) == 1) { - $result = $this->client->put("customers/$idsCustomer[1]", $contactData); - } + while ($tax = $this->db->fetch_object($resql)) { + $taxesTable[] = $tax->taux; } - dol_syslog("eCommerceRemoteAccessWoocommerce updateRemoteSocpeople end"); - return true; - } catch (HttpClientException $fault) { - $this->errors[]=$fault->getMessage().'-'.$fault->getCode(); - dol_syslog(__METHOD__.': '.$fault->getMessage().'-'.$fault->getCode().'-'.$fault->getTraceAsString(), LOG_WARNING); - return false; + $this->dolibarrTaxes = $taxesTable; } + + //dol_syslog(__METHOD__ . ": end", LOG_DEBUG); } /** - * Update the remote order + * Retrieve all Dolibarr tax rates * - * @param int $remote_id Id of order on remote ecommerce - * @param Commande $object Commande object - * @return boolean True or false + * @return void */ - public function updateRemoteCommande($remote_id, $object) + /* private function setDolibarrTaxes() { - dol_syslog("eCommerceRemoteAccessWoocommerce updateRemoteCommande session=".$this->session." remote_id=".$remote_id." object->id=".$object->id); + dol_syslog(__METHOD__ . ": Retrieve all Dolibarr tax rates", LOG_DEBUG); - try { - switch ($object->statut) { - //case Commande::STOCK_NOT_ENOUGH_FOR_ORDER: $status = ''; break; - case Commande::STATUS_CANCELED: $status = 'cancelled'; break; - //case Commande::STATUS_DRAFT: $status = ''; break; - case Commande::STATUS_VALIDATED: $status = 'pending'; break; - case Commande::STATUS_ACCEPTED: $status = 'processing'; break; - case Commande::STATUS_SHIPMENTONPROCESS: $status = 'processing'; break; - case Commande::STATUS_CLOSED: $status = 'completed'; break; + $resql = $this->db->query("SELECT t.*, c.code AS country FROM ".MAIN_DB_PREFIX."c_tva AS t LEFT JOIN ".MAIN_DB_PREFIX."c_country AS c ON t.fk_pays = c.rowid"); + if ($resql) { + $taxesTable = [ 'taxes' => [], 'countries' => [] ]; + + while ($tax = $this->db->fetch_array($resql)) { + $taxesTable['taxes'][] = $tax; + if (!empty($tax['country'])) { + $taxesTable['countries'][$tax['country']][] = $tax; + } } - if (isset($status)) { - $commandeData = array( - 'status' => $status, // string Order status. Options: pending, processing, on-hold, completed, cancelled, refunded and failed. - ); + $this->dolibarrTaxes = $taxesTable; + } - $result = $this->client->put("orders/$remote_id", $commandeData); - } + dol_syslog(__METHOD__ . ": end", LOG_DEBUG); + }*/ - dol_syslog("eCommerceRemoteAccessWoocommerce updateRemoteCommande end"); + /** + * Update all Woocommerce tax classes in dict + * + * @return array|false List of woocommerce tax class or false if error + */ + public function getAllWoocommerceTaxClass() + { + dol_syslog(__METHOD__ . ": Retrieve all Woocommerce tax classes", LOG_DEBUG); + global $langs; - return true; + try { + $tax_classes = $this->client->get('taxes/classes'); } catch (HttpClientException $fault) { - $this->errors[]=$fault->getMessage().'-'.$fault->getCode(); - dol_syslog(__METHOD__.': '.$fault->getMessage().'-'.$fault->getCode().'-'.$fault->getTraceAsString(), LOG_WARNING); + $this->errors[] = $langs->trans('ECommerceWoocommerceGetAllWoocommerceTaxClass', $this->site->name, $fault->getCode() . ': ' . $fault->getMessage()); + dol_syslog(__METHOD__ . + ': Error:' . $langs->transnoentitiesnoconv('ECommerceWoocommerceGetAllWoocommerceTaxClass', $this->site->name, $fault->getCode() . ': ' . $fault->getMessage()) . + ' - Request:' . json_encode($fault->getRequest()) . ' - Response:' . json_encode($fault->getResponse()), LOG_ERR); return false; } + + $taxClassesTable = []; + foreach ($tax_classes as $tax_class) { + unset($tax_class['_links']); + $taxClassesTable[$tax_class['slug']] = $tax_class; + } + + dol_syslog(__METHOD__ . ": end, return: ".json_encode($taxClassesTable), LOG_DEBUG); + return $taxClassesTable; } /** - * Update the remote invoice + * Retrieve all Woocommerce tax rates * - * @param int $remote_id Id of invoice on remote ecommerce - * @param Facture $object Invoice object - * @return boolean True or false + * @return boolean */ - public function updateRemoteFacture($remote_id, $object) + private function setWoocommerceTaxes() { - dol_syslog("eCommerceRemoteAccessWoocommerce updateRemoteFacture session=".$this->session." remote_id=".$remote_id." object->id=".$object->id); + dol_syslog(__METHOD__ . ": Retrieve all Woocommerce tax rates", LOG_DEBUG); + global $conf, $langs; - $result = false; - /* - try { - $factureData = array( - 'status' => $object->status, - ); + $nb_max_by_request = empty($conf->global->ECOMMERCENG_MAXSIZE_MULTICALL) ? 100 : min($conf->global->ECOMMERCENG_MAXSIZE_MULTICALL, 100); - $result = $this->client->call($this->session, 'invoice.update', array($remote_id, $factureData, null, 'order_id')); - //dol_syslog($this->client->__getLastRequest()); - } catch (SoapFault $fault) { - $this->errors[]=$fault->getMessage().'-'.$fault->getCode(); - dol_syslog(__METHOD__.': '.$fault->getMessage().'-'.$fault->getCode().'-'.$fault->getTraceAsString(), LOG_WARNING); - return false; - }*/ - dol_syslog("eCommerceRemoteAccessWoocommerce updateRemoteFacture end"); - return $result; + $taxesTable = [ 'taxes' => [], 'classes' => [], 'countries' => [], 'states' => [], 'postcodes' => [], 'cities' => []]; + $idxPage = 0; + do { + $idxPage++; + try { + $taxes = $this->client->get('taxes', + [ + 'page' => $idxPage, + 'per_page' => $nb_max_by_request, + ] + ); + } catch (HttpClientException $fault) { + $this->errors[] = $langs->trans('ECommerceWoocommerceGetWoocommerceTaxes', $this->site->name, $fault->getCode() . ': ' . $fault->getMessage()); + dol_syslog(__METHOD__ . + ': Error:' . $langs->transnoentitiesnoconv('ECommerceWoocommerceGetWoocommerceTaxes', $this->site->name, $fault->getCode() . ': ' . $fault->getMessage()) . + ' - Request:' . json_encode($fault->getRequest()) . ' - Response:' . json_encode($fault->getResponse()), LOG_ERR); + return false; + } + + foreach ($taxes as $tax) { + $id = $tax['id']; + unset($tax['_links']); + + $taxesTable['taxes'][$id] = $tax; + if (!empty($tax['class'])) { + $taxesTable['classes'][$tax['class']][$id] = $tax; + } + if (!empty($tax['country'])) { + $taxesTable['countries'][$tax['country']][$id] = $tax; + } + if (!empty($tax['state'])) { + $taxesTable['states'][$tax['state']][$id] = $tax; + } + if (!empty($tax['postcode'])) { + $taxesTable['postcodes'][$tax['postcode']][$id] = $tax; + } + if (!empty($tax['city'])) { + $taxesTable['cities'][$tax['city']][$id] = $tax; + } + } + } while (!empty($taxes)); + + $this->woocommerceTaxes = $taxesTable; + + dol_syslog(__METHOD__ . ": end", LOG_DEBUG); + return true; } /** - * Create shipment + * Get closest dolibarr tax rate * - * @param int $livraison Object shipment ? - * @param int $remote_order_id Id of order - * @return boolean True or false + * @param string $tax_rate Tax rate + * + * @return float Closest dolibarr tax rate */ - public function createRemoteLivraison($livraison, $remote_order_id) + private function _getClosestDolibarrTaxRate($tax_rate) { - $result = false; + //dol_syslog(__METHOD__ . ": Get closest dolibarr tax rate, tax_rate: $tax_rate", LOG_DEBUG); - dol_syslog("eCommerceRemoteAccessWoocommerce createRemoteLivraison session=" . $this->session . " dolibarr shipment id = " . $livraison->id . ", ref = " . $livraison->ref . ", order remote id = " . $remote_order_id); -/* $remoteCommande = $this->getRemoteCommande($remote_order_id); // SOAP request to get data - $livraisonArray = get_object_vars($livraison); - try { - $orderItemQty = array(); - foreach ($remoteCommande['items'] as $productWoocommerce) { - foreach ($livraisonArray['lines'] as $lines) { - if ($lines->product_ref == $productWoocommerce['sku']) { - $orderItemQty[$productWoocommerce['item_id']] = $lines->qty_shipped; - } + $tax = null; + + // Retrieve all dolibarr tax + if (!isset($this->dolibarrTaxes)) { + $this->setDolibarrTaxes(); + } + + // Get closest dolibarr tax for woocommerce tax + if (is_array($this->dolibarrTaxes) && count($this->dolibarrTaxes) > 0) { + $closestTax = 0; + foreach ($this->dolibarrTaxes as $tax) { + if (abs($tax - $tax_rate) < abs($tax_rate - $closestTax)) { + $closestTax = $tax; } } - $result = $this->client->call($this->session, 'sales_order_shipment.create', array( - $remoteCommande['increment_id'], - $orderItemQty, - 'Shipment Created from ' . ($livraison->newref ? $livraison->newref : $livraison->ref), - true, - true - )); - //dol_syslog($this->client->__getLastResponse()); - } catch (SoapFault $fault) { - $this->errors[] = $this->site->name . ': ' . $fault->getMessage() . ' - ' . $fault->getCode(); - dol_syslog(__METHOD__ . ': ' . $fault->getMessage() . '-' . $fault->getCode() . '-' . $fault->getTraceAsString(), LOG_WARNING); - return false; - }*/ - dol_syslog("eCommerceRemoteAccessWoocommerce createRemoteLivraison end"); - return $result; + $tax = $closestTax; + } + + //dol_syslog(__METHOD__ . ": end, return ".(isset($tax)?json_encode($tax):'null'), LOG_DEBUG); + return $tax; } + /** + * Get closest dolibarr tax + * + * @param string $country_code Country code + * @param string $tax_rate Tax rate + * + * @return float Near dolibarr tax + */ + /*private function getClosestDolibarCountryTax($country_code, $tax_rate) + { + dol_syslog(__METHOD__ . ": Get closest dolibarr tax rate, country_code: $country_code, tax_rate: $tax_rate", LOG_DEBUG); + global $langs; + + $tax = null; + // Get country code from default language if empty + if (empty($country_code)) $country_code = substr($langs->defaultlang, -2); + + // Retrieve all dolibarr tax + if (!isset($this->dolibarrTaxes) || !isset($this->dolibarrTaxes['countries'][$country_code])) { + $this->setDolibarrTaxes(); + } + + // Get closest dolibarr tax for woocommerce tax + $dolibarrTaxes = $this->dolibarrTaxes['countries'][$country_code]; + if (is_array($dolibarrTaxes)) { + $closestTaxes = []; + foreach ($dolibarrTaxes as $tax) { + $near = $tax['taux'] - $tax_rate; + if (!isset($closestTaxes[$near]) || $closestTaxes[$near]['taux'] < $tax['taux']) { + $nearTaxes[$near] = $tax; + } + } + ksort($closestTaxes); + reset($closestTaxes); + $tax = $closestTaxes[0]; + } + dol_syslog(__METHOD__ . ": end, return ".(isset($tax)?json_encode($tax):'null'), LOG_DEBUG); + return $tax; + }*/ /** - * Calcul tax rate and return the closest dolibarr tax rate. + * Get request groups of ID for get datas of remotes objects. * - * @param float $priceHT Price HT - * @param float $taxAmount Tax amount + * @param array $remoteObject List of ids of remote objects + * @param int $nb_max_by_request Nb remote ID by request + * @param int $toNb Max nb + * @return array List of request groups of ID */ - private function getTaxRate($priceHT, $taxAmount) + private function getRequestGroups($remoteObject, $nb_max_by_request, $toNb=0) { - $taxRate = 0; - if ($taxAmount != 0) - { - //calcul tax rate from remote site - $tempTaxRate = ($taxAmount / $priceHT) * 100; - //get all dolibarr tax rates - if (!isset($this->taxRates)) - $this->setTaxRates(); - if (count($this->taxRates)) - { - $min = 1; - $rate = 0; - foreach ($this->taxRates as $dolibarrTaxRate) - { - $diff = $tempTaxRate - $dolibarrTaxRate['taux']; - if ($diff < 0) - $diff = (-1 * $diff); - if ($diff < $min) - { - $min = $diff; - $rate = $dolibarrTaxRate['taux']; - } - } - if ($rate > 0) - $taxRate = $rate; + //dol_syslog(__METHOD__ . ": Get request groups of ID: " . implode(', ', $remoteObject), LOG_DEBUG); + + $idx = 0; + $request = []; + $request_groups = []; + + foreach ($remoteObject as $remote_object_id) { + if ($toNb > 0 && $idx > $toNb) break; + + if (($idx++ % $nb_max_by_request) == 0) { + if (count($request)) $request_groups[] = $request; + $request = []; } + + $request[] = $remote_object_id; } - return $taxRate; + if (count($request)) $request_groups[] = $request; + + //dol_syslog(__METHOD__ . ": end", LOG_DEBUG); + return $request_groups; } /** - * Retrieve all Dolibarr tax rates + * Get DateTime object in current timezone from gmt date time. + * + * @param string $datetime GMT date time + * + * @return DateTime DateTime in current Time Zone */ - private function setTaxRates() + private function getDateTimeFromGMTDateTime($datetime) { - $taxTable = new eCommerceDict($this->db, MAIN_DB_PREFIX . "c_tva"); - $this->taxRates = $taxTable->getAll(); + //dol_syslog(__METHOD__ . ": Get DateTime object in current timezone from gmt date time: $datetime", LOG_DEBUG); + + $dt = new DateTime($datetime, $this->gmtTimeZone); + $dt->setTimezone($this->currentTimeZone); + + //dol_syslog(__METHOD__ . ": end", LOG_DEBUG); + return $dt; } public function __destruct() { - //ini_set("memory_limit", "528M"); + ini_set("memory_limit", "528M"); } - } - diff --git a/htdocs/ecommerceng/core/modules/modECommerceNg.class.php b/htdocs/ecommerceng/core/modules/modECommerceNg.class.php index 9982f98..1da64e0 100644 --- a/htdocs/ecommerceng/core/modules/modECommerceNg.class.php +++ b/htdocs/ecommerceng/core/modules/modECommerceNg.class.php @@ -19,10 +19,12 @@ require_once(DOL_DOCUMENT_ROOT.'/core/modules/DolibarrModules.class.php'); +require_once(DOL_DOCUMENT_ROOT.'/core/lib/files.lib.php'); require_once(DOL_DOCUMENT_ROOT.'/societe/class/societe.class.php'); dol_include_once('/ecommerceng/admin/class/gui/eCommerceMenu.class.php'); dol_include_once('/ecommerceng/admin/class/data/eCommerceDict.class.php'); +dol_include_once('/ecommerceng/class/data/eCommerceSite.class.php'); /** @@ -55,11 +57,11 @@ function __construct($db) // Module description, used if translation string 'ModuleXXXDesc' not found (where XXX is value of numeric property 'numero' of module) $this->description = "Module to synchronise Dolibarr with ECommerce platform (currently ecommerce supported: Magento, WooCommerce)"; $this->descriptionlong = "See page https://wiki.dolibarr.org/index.php/Module_Magento_EN for more information"; - $this->editor_name = 'TecLib'; + $this->editor_name = 'TecLib, Open-Dsi'; $this->editor_url = 'http://www.teclib.com'; // Possible values for version are: 'development', 'experimental', 'dolibarr' or version - $this->version = '4.0.0'; + $this->version = '4.0.3'; // Key used in llx_const table to save module status enabled/disabled (where MYMODULE is value of property name of module in uppercase) $this->const_name = 'MAIN_MODULE_'.strtoupper($this->name); // Where to store the module in setup page (0=common,1=interface,2=others,3=very specific) @@ -85,8 +87,9 @@ function __construct($db) // 'workflow' => array('order' => array('WORKFLOW_ORDER_AUTOCREATE_INVOICE')) // Set here all workflow context managed by module // ); $this->module_parts = array( - 'triggers' => 1 - ); + 'triggers' => 1, + 'hooks' => array('expeditioncard','invoicecard','productdocuments'), + ); // Data directories to create when module is enabled. // Example: this->dirs = array("/mymodule/temp"); @@ -102,9 +105,9 @@ function __construct($db) // Dependencies $this->depends = array("modSociete","modProduct","modCategorie","modWebServices"); // List of modules id that must be enabled if this module is enabled $this->requiredby = array(); // List of modules id to disable if this one is disabled - $this->phpmin = array(4,3); // Minimum version of PHP required by module + $this->phpmin = array(5,3); // Minimum version of PHP required by module $this->need_dolibarr_version = array(3,9); // Minimum version of Dolibarr required by module - $this->langfiles = array("ecommerce@ecommerceng"); + $this->langfiles = array("ecommerce@ecommerceng", "woocommerce@ecommerceng"); // Constants // List of particular constants to add when module is enabled @@ -130,6 +133,42 @@ function __construct($db) // 'member' to add a tab in fundation member view // 'contract' to add a tab in contract view + if (! isset($conf->ecommerceng) || ! isset($conf->ecommerceng->enabled)) + { + $conf->ecommerceng=new stdClass(); + $conf->ecommerceng->enabled=0; + } + + $eCommerceSite = new eCommerceSite($this->db); + + // Dictionaries + $this->dictionaries=array( + 'langs'=>'woocommerce@ecommerceng', + 'tabname'=>array(MAIN_DB_PREFIX."c_ecommerceng_tax_class"), + 'tablib'=>array("ECommercengWoocommerceDictTaxClass"), + 'tabsql'=>array('SELECT f.rowid as rowid, f.site_id, f.code, f.label, f.entity, f.active FROM '.MAIN_DB_PREFIX.'c_ecommerceng_tax_class as f WHERE f.entity='.$conf->entity), + 'tabsqlsort'=>array("site_id ASC, label ASC"), + 'tabfield'=>array("code,label,site_id"), + 'tabfieldvalue'=>array("code,label,site_id"), + 'tabfieldinsert'=>array("code,label,site_id"), + 'tabrowid'=>array("rowid"), + 'tabcond'=>array($conf->ecommerceng->enabled && $eCommerceSite->hasTypeSite(2)) + ); + + /* Example: + $this->dictionaries=array( + 'langs'=>'mylangfile@mymodule', + 'tabname'=>array(MAIN_DB_PREFIX."table1",MAIN_DB_PREFIX."table2",MAIN_DB_PREFIX."table3"), // List of tables we want to see into dictonnary editor + 'tablib'=>array("Table1","Table2","Table3"), // Label of tables + 'tabsql'=>array('SELECT f.rowid as rowid, f.code, f.label, f.active FROM '.MAIN_DB_PREFIX.'table1 as f','SELECT f.rowid as rowid, f.code, f.label, f.active FROM '.MAIN_DB_PREFIX.'table2 as f','SELECT f.rowid as rowid, f.code, f.label, f.active FROM '.MAIN_DB_PREFIX.'table3 as f'), // Request to select fields + 'tabsqlsort'=>array("label ASC","label ASC","label ASC"), // Sort order + 'tabfield'=>array("code,label","code,label","code,label"), // List of fields (result of select to show dictionary) + 'tabfieldvalue'=>array("code,label","code,label","code,label"), // List of fields (list of fields to edit a record) + 'tabfieldinsert'=>array("code,label","code,label","code,label"), // List of fields (list of fields for insert) + 'tabrowid'=>array("rowid","rowid","rowid"), // Name of columns with primary key (try to always name it 'rowid') + 'tabcond'=>array($conf->mymodule->enabled,$conf->mymodule->enabled,$conf->mymodule->enabled) // Condition to show each dictionary + ); + */ // Boxes $this->boxes = array(); // List of boxes @@ -226,6 +265,7 @@ function init($options = '') $result=$this->load_tables($options); $this->addSettlementTerms(); $this->addAnonymousCompany(); + $this->addFiles(); return $this->_init($sql, $options); } @@ -338,5 +378,18 @@ private function AddSettlementTerms() } } } + + /** + * Add files need for dolibarr + */ + private function addFiles() + { + $srcFile = dol_buildpath('/ecommerceng/patchs/dolibarr/includes/OAuth/OAuth2/Service/WordPress.php'); + $destFile = DOL_DOCUMENT_ROOT . '/includes/OAuth/OAuth2/Service/WordPress.php'; + + if (dol_copy($srcFile, $destFile) < 0) { + setEventMessages("Error copy file '$srcFile' to '$destFile'", null, 'errors'); + } + } } diff --git a/htdocs/ecommerceng/core/modules/oauth/wordpress_oauthcallback.php b/htdocs/ecommerceng/core/modules/oauth/wordpress_oauthcallback.php new file mode 100644 index 0000000..3e1fcbb --- /dev/null +++ b/htdocs/ecommerceng/core/modules/oauth/wordpress_oauthcallback.php @@ -0,0 +1,194 @@ + + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +/** + * \file htdocs/core/modules/oauth/wordpress_oauthcallback.php + * \ingroup oauth + * \brief Page to get oauth callback + */ + +$res=0; +if (! $res && ! empty($_SERVER["CONTEXT_DOCUMENT_ROOT"])) $res=@include($_SERVER["CONTEXT_DOCUMENT_ROOT"]."/main.inc.php"); +if (! $res && file_exists("../main.inc.php")) $res=@include("../main.inc.php"); +if (! $res && file_exists("../../main.inc.php")) $res=@include("../../main.inc.php"); +if (! $res && file_exists("../../../main.inc.php")) $res=@include("../../../main.inc.php"); +if (! $res && file_exists("../../../../main.inc.php")) $res=@include("../../../../main.inc.php"); +if (! $res && file_exists("../../../../../main.inc.php")) $res=@include("../../../../../main.inc.php"); +if (! $res && preg_match('/\/nltechno([^\/]*)\//',$_SERVER["PHP_SELF"],$reg)) $res=@include("../../../dolibarr".$reg[1]."/htdocs/main.inc.php"); // Used on dev env only +if (! $res && preg_match('/\/teclib([^\/]*)\//',$_SERVER["PHP_SELF"],$reg)) $res=@include("../../../dolibarr".$reg[1]."/htdocs/main.inc.php"); // Used on dev env only +if (! $res) die("Include of main fails"); +require_once DOL_DOCUMENT_ROOT.'/includes/OAuth/bootstrap.php'; +dol_include_once('/ecommerceng/includes/CurlClientEx.php'); +dol_include_once('/ecommerceng/class/data/eCommerceSite.class.php'); +use OAuth\Common\Storage\DoliStorage; +use OAuth\Common\Consumer\Credentials; +use OAuth\Common\Http\Uri\Uri; +use OAuth\Common\Http\Client\CurlClient; +use OAuth\OAuth2\Service\WordPress; + +// Define $urlwithroot +$urlwithouturlroot=preg_replace('/'.preg_quote(DOL_URL_ROOT,'/').'$/i','',trim($dolibarr_main_url_root)); +$urlwithroot=$urlwithouturlroot.DOL_URL_ROOT; // This is to use external domain name found into config file +//$urlwithroot=DOL_MAIN_URL_ROOT; // This is to use same domain name than current + + + +$action = GETPOST('action', 'alpha'); +$backtourl = GETPOST('backtourl', 'alpha'); +$siteId = GETPOST('ecommerce_id', 'int'); + +//LOAD SELECTED SITE +$siteDb = new eCommerceSite($db); +$res = $siteDb->fetch($siteId); +if ($res < 0) { + setEventMessage("Error: site ecommerce not found", 'errors'); + header('Location: ' . $backtourl); + exit(); +} + +/** + * Create a new instance of the URI class with the current URI, stripping the query string + */ +$uriFactory = new \OAuth\Common\Http\Uri\UriFactory(); +//$currentUri = $uriFactory->createFromSuperGlobalArray($_SERVER); +//$currentUri->setQuery(''); +$currentUri = $uriFactory->createFromAbsolute($urlwithroot.'/custom/ecommerceng/core/modules/oauth/wordpress_oauthcallback.php?ecommerce_id='.$siteId); + + +/** + * Load the credential for the service + */ + +/** @var $serviceFactory \OAuth\ServiceFactory An OAuth service factory. */ +$serviceFactory = new \OAuth\ServiceFactory(); +$httpClient = new CurlClient(); +// TODO Set options for proxy and timeout +// $params=array('CURLXXX'=>value, ...) +//$httpClient->setCurlParameters($params); +$serviceFactory->setHttpClient($httpClient); + +// Dolibarr storage +$storage = new DoliStorage($db, $conf); + +// Setup the credentials for the requests +$credentials = new Credentials( + $siteDb->oauth_id, + $siteDb->oauth_secret, + $currentUri->getAbsoluteUri() +); + +$requestedpermissionsarray=array(); +if (GETPOST('state')) $requestedpermissionsarray=explode(',', GETPOST('state')); // Example: 'userinfo_email,userinfo_profile,cloud_print'. 'state' parameter is standard to retrieve some parameters back +/*if ($action != 'delete' && empty($requestedpermissionsarray)) +{ + print 'Error, parameter state is not defined'; + exit; +}*/ +//var_dump($requestedpermissionsarray);exit; + +// Instantiate the Api service using the credentials, http client and storage mechanism for the token +/** @var $apiService Service */ +$apiService = $serviceFactory->createService('WordPress', $credentials, $storage, array(), new Uri($siteDb->webservice_address)); + +// access type needed to have oauth provider refreshing token +// alos note that a refresh token is sent only after a prompt +//$apiService->setAccessType('offline'); + +//$apiService->setApprouvalPrompt('force'); + +$langs->load("oauth"); + + +/* + * Actions + */ + + +if ($action == 'delete') +{ + $storage->clearToken('ECommerce_'.$siteId); + + setEventMessages($langs->trans('TokenDeleted'), null, 'mesgs'); + + header('Location: ' . $backtourl); + exit(); +} + +if (! empty($_GET['code'])) // We are coming from oauth provider page +{ + $backtourl = $_SESSION["backtourlsavedbeforeoauthjump"]; + unset($_SESSION["backtourlsavedbeforeoauthjump"]); + + //llxHeader('',$langs->trans("OAuthSetup")); + + //$linkback=''.$langs->trans("BackToModuleList").''; + //print load_fiche_titre($langs->trans("OAuthSetup"),$linkback,'title_setup'); + + //dol_fiche_head(); + // retrieve the CSRF state parameter + $state = isset($_GET['state']) ? $_GET['state'] : null; + //print ''; + + // This was a callback request from service, get the token + try { + //var_dump($_GET['code']); + //var_dump($state); + //var_dump($apiService); // OAuth\OAuth2\Service\WordPress + + $token = $apiService->requestAccessToken($_GET['code'], $state); + $storage->storeAccessToken('ECommerce_'.$siteId, $token); + +// $token = $apiService->refreshAccessToken($token); + + setEventMessages($langs->trans('NewTokenStored'), null, 'mesgs'); // Stored into object managed by class DoliStorage so into table oauth_token + } catch (Exception $e) { + setEventMessage($e->getMessage(), 'errors'); + } + + header('Location: ' . $backtourl); + exit(); +} +else // If entry on page with no parameter, we arrive here +{ + $_SESSION["backtourlsavedbeforeoauthjump"]=$backtourl; + + // This may create record into oauth_state before the header redirect. + // Creation of record with state in this tables depend on the Provider used (see its constructor). + if (GETPOST('state')) + { + $url = $apiService->getAuthorizationUri(array('state'=>GETPOST('state'))); + } + else + { + $url = $apiService->getAuthorizationUri(); // Parameter state will be randomly generated + } + + // we go on oauth provider authorization page + header('Location: ' . $url); + exit(); +} + + +/* + * View + */ + +// No view at all, just actions + +$db->close(); + diff --git a/htdocs/ecommerceng/core/triggers/interface_90_modECommerceng_ECommerceng.class.php b/htdocs/ecommerceng/core/triggers/interface_90_modECommerceng_ECommerceng.class.php old mode 100755 new mode 100644 index 3702cdc..f39a912 --- a/htdocs/ecommerceng/core/triggers/interface_90_modECommerceng_ECommerceng.class.php +++ b/htdocs/ecommerceng/core/triggers/interface_90_modECommerceng_ECommerceng.class.php @@ -98,6 +98,19 @@ function run_trigger($action,$object,$user,$langs,$conf) { $error=0; + if ($action == 'CATEGORY_LINK') { + if (isset($object->linkto->element)) { + switch ($object->linkto->element) { + case 'product': + //$cat_link_action_old = $action; + //$cat_link_object_old = $object; + $action = 'PRODUCT_MODIFY'; + $object = $object->linkto; + break; + } + } + } + if ($action == 'COMPANY_CREATE') { @@ -274,11 +287,18 @@ function run_trigger($action,$object,$user,$langs,$conf) { $this->db->begin(); + $categories = GETPOST('categories'); + $eCommerceSite = new eCommerceSite($this->db); $sites = $eCommerceSite->listSites('object'); foreach($sites as $site) { + if (!in_array($site->fk_cat_product, $categories)) { + dol_syslog("Product not in categorie now, so we won't run code to sync from dolibarr to ecommerce"); + continue; + } + if ($object->context['fromsyncofecommerceid'] && $object->context['fromsyncofecommerceid'] == $site->id) { dol_syslog("Triggers was ran from a create/update to sync from ecommerce to dolibarr, so we won't run code to sync from dolibarr to ecommerce"); @@ -317,21 +337,35 @@ function run_trigger($action,$object,$user,$langs,$conf) } else { - // Get current categories - require_once DOL_DOCUMENT_ROOT . '/categories/class/categorie.class.php'; - $c = new Categorie($this->db); - $catids = $c->containing($object->id, Categorie::TYPE_PRODUCT, 'id'); + // Get current categories + require_once DOL_DOCUMENT_ROOT . '/categories/class/categorie.class.php'; + $c = new Categorie($this->db); + $catids = $c->containing($object->id, Categorie::TYPE_PRODUCT, 'id'); - if (in_array($site->fk_cat_product, $catids)) - { - dol_syslog("Product with id ".$object->id." is not linked to an ecommerce record but has category flag to push on eCommerce. So we push it"); - // TODO - //$result = $eCommerceSynchro->eCommerceRemoteAccess->updateRemoteProduct($eCommerceProduct->remote_id); - } - else - { - dol_syslog("Product with id ".$object->id." is not linked to an ecommerce record and does not has category flag to push on eCommerce."); - } + if (in_array($site->fk_cat_product, $catids)) { + dol_syslog("Product with id ".$object->id." is not linked to an ecommerce record but has category flag to push on eCommerce. So we push it"); + + $eCommerceSynchro = new eCommerceSynchro($this->db, $site); + dol_syslog("Trigger " . $action . " try to connect to eCommerce site " . $site->name); + $eCommerceSynchro->connect(); + if (count($eCommerceSynchro->errors)) { + $error++; + setEventMessages($eCommerceSynchro->error, $eCommerceSynchro->errors, 'errors'); + } + + if (!empty($conf->global->PRODUIT_MULTIPRICES)) { + $object->price = $object->multiprices[$site->price_level]; + } + + $result = $eCommerceSynchro->eCommerceRemoteAccess->createRemoteProduct($object); + if (!$result) { + $error++; + $this->error = $eCommerceSynchro->eCommerceRemoteAccess->error; + $this->errors = $eCommerceSynchro->eCommerceRemoteAccess->errors; + } + } else { + dol_syslog("Product with id ".$object->id." is not linked to an ecommerce record and does not has category flag to push on eCommerce."); + } } } } @@ -350,10 +384,20 @@ function run_trigger($action,$object,$user,$langs,$conf) - if ($action == 'ORDER_MODIFY' || $action == 'ORDER_CLOSE' || $action == 'ORDER_CLASSIFY_BILLED') + if ($action == 'ORDER_MODIFY' || $action == 'ORDER_CLOSE' || $action == 'ORDER_CLASSIFY_BILLED' || + $action == 'ORDER_VALIDATE' || $action == 'ORDER_UNVALIDATE' || $action == 'ORDER_REOPEN' || + $action == 'ORDER_CANCEL' || $action == 'ORDER_CLASSIFY_UNBILLED') { $this->db->begin(); + switch ($action) { + case 'ORDER_VALIDATE': $object->statut = Commande::STATUS_VALIDATED; break; + case 'ORDER_UNVALIDATE': $object->statut = Commande::STATUS_DRAFT; break; + case 'ORDER_REOPEN': $object->statut = Commande::STATUS_DRAFT; break; + case 'ORDER_CLOSE': $object->statut = Commande::STATUS_CLOSED; break; + case 'ORDER_CANCEL': $object->statut = Commande::STATUS_CANCELED; break; + } + $eCommerceSite = new eCommerceSite($this->db); $sites = $eCommerceSite->listSites('object'); diff --git a/htdocs/ecommerceng/doc/Interface Dolibarr-Woocommerce.odt b/htdocs/ecommerceng/doc/Interface Dolibarr-Woocommerce.odt deleted file mode 100644 index e17f32a..0000000 Binary files a/htdocs/ecommerceng/doc/Interface Dolibarr-Woocommerce.odt and /dev/null differ diff --git a/htdocs/ecommerceng/doc/Interface Dolibarr-Woocommerce.pdf b/htdocs/ecommerceng/doc/Interface Dolibarr-Woocommerce.pdf deleted file mode 100644 index 336be59..0000000 Binary files a/htdocs/ecommerceng/doc/Interface Dolibarr-Woocommerce.pdf and /dev/null differ diff --git a/htdocs/ecommerceng/doc/hack_wordpress/readme.txt b/htdocs/ecommerceng/doc/hack_wordpress/readme.txt deleted file mode 100644 index 9a77c7d..0000000 --- a/htdocs/ecommerceng/doc/hack_wordpress/readme.txt +++ /dev/null @@ -1,4 +0,0 @@ -# Hack for woocommerce. -For authentification with api legacy v3 for the url with index.php in it. - -Override the file in the wordpress folder. \ No newline at end of file diff --git a/htdocs/ecommerceng/doc/hack_wordpress/wordpress/wp-content/plugins/woocommerce/includes/api/legacy/v3/class-wc-api-authentication.php b/htdocs/ecommerceng/doc/hack_wordpress/wordpress/wp-content/plugins/woocommerce/includes/api/legacy/v3/class-wc-api-authentication.php deleted file mode 100755 index c5d486d..0000000 --- a/htdocs/ecommerceng/doc/hack_wordpress/wordpress/wp-content/plugins/woocommerce/includes/api/legacy/v3/class-wc-api-authentication.php +++ /dev/null @@ -1,421 +0,0 @@ -api->server->path ) { - return new WP_User( 0 ); - } - - try { - if ( is_ssl() ) { - $keys = $this->perform_ssl_authentication(); - } else { - $keys = $this->perform_oauth_authentication(); - } - - // Check API key-specific permission - $this->check_api_key_permissions( $keys['permissions'] ); - - $user = $this->get_user_by_id( $keys['user_id'] ); - - $this->update_api_key_last_access( $keys['key_id'] ); - - } catch ( Exception $e ) { - $user = new WP_Error( 'woocommerce_api_authentication_error', $e->getMessage(), array( 'status' => $e->getCode() ) ); - } - - return $user; - } - - /** - * SSL-encrypted requests are not subject to sniffing or man-in-the-middle - * attacks, so the request can be authenticated by simply looking up the user - * associated with the given consumer key and confirming the consumer secret - * provided is valid - * - * @since 2.1 - * @return array - * @throws Exception - */ - private function perform_ssl_authentication() { - $params = WC()->api->server->params['GET']; - - // if the $_GET parameters are present, use those first - if ( ! empty( $params['consumer_key'] ) && ! empty( $params['consumer_secret'] ) ) { - $keys = $this->get_keys_by_consumer_key( $params['consumer_key'] ); - - if ( ! $this->is_consumer_secret_valid( $keys['consumer_secret'], $params['consumer_secret'] ) ) { - throw new Exception( __( 'Consumer secret is invalid.', 'woocommerce' ), 401 ); - } - - return $keys; - } - - // if the above is not present, we will do full basic auth - if ( empty( $_SERVER['PHP_AUTH_USER'] ) || empty( $_SERVER['PHP_AUTH_PW'] ) ) { - $this->exit_with_unauthorized_headers(); - } - - $keys = $this->get_keys_by_consumer_key( $_SERVER['PHP_AUTH_USER'] ); - - if ( ! $this->is_consumer_secret_valid( $keys['consumer_secret'], $_SERVER['PHP_AUTH_PW'] ) ) { - $this->exit_with_unauthorized_headers(); - } - - return $keys; - } - - /** - * If the consumer_key and consumer_secret $_GET parameters are NOT provided - * and the Basic auth headers are either not present or the consumer secret does not match the consumer - * key provided, then return the correct Basic headers and an error message. - * - * @since 2.4 - */ - private function exit_with_unauthorized_headers() { - $auth_message = __( 'WooCommerce API. Use a consumer key in the username field and a consumer secret in the password field.', 'woocommerce' ); - header( 'WWW-Authenticate: Basic realm="' . $auth_message . '"' ); - header( 'HTTP/1.0 401 Unauthorized' ); - throw new Exception( __( 'Consumer Secret is invalid.', 'woocommerce' ), 401 ); - } - - /** - * Perform OAuth 1.0a "one-legged" (http://oauthbible.com/#oauth-10a-one-legged) authentication for non-SSL requests - * - * This is required so API credentials cannot be sniffed or intercepted when making API requests over plain HTTP - * - * This follows the spec for simple OAuth 1.0a authentication (RFC 5849) as closely as possible, with two exceptions: - * - * 1) There is no token associated with request/responses, only consumer keys/secrets are used - * - * 2) The OAuth parameters are included as part of the request query string instead of part of the Authorization header, - * This is because there is no cross-OS function within PHP to get the raw Authorization header - * - * @link http://tools.ietf.org/html/rfc5849 for the full spec - * @since 2.1 - * @return array - * @throws Exception - */ - private function perform_oauth_authentication() { - - $params = WC()->api->server->params['GET']; - - $param_names = array( 'oauth_consumer_key', 'oauth_timestamp', 'oauth_nonce', 'oauth_signature', 'oauth_signature_method' ); - - // Check for required OAuth parameters - foreach ( $param_names as $param_name ) { - - if ( empty( $params[ $param_name ] ) ) { - throw new Exception( sprintf( __( '%s parameter is missing', 'woocommerce' ), $param_name ), 404 ); - } - } - - // Fetch WP user by consumer key - $keys = $this->get_keys_by_consumer_key( $params['oauth_consumer_key'] ); - - // Perform OAuth validation - $this->check_oauth_signature( $keys, $params ); - $this->check_oauth_timestamp_and_nonce( $keys, $params['oauth_timestamp'], $params['oauth_nonce'] ); - - // Authentication successful, return user - return $keys; - } - - /** - * Return the keys for the given consumer key - * - * @since 2.4.0 - * @param string $consumer_key - * @return array - * @throws Exception - */ - private function get_keys_by_consumer_key( $consumer_key ) { - global $wpdb; - - $consumer_key = wc_api_hash( sanitize_text_field( $consumer_key ) ); - - $keys = $wpdb->get_row( $wpdb->prepare( " - SELECT key_id, user_id, permissions, consumer_key, consumer_secret, nonces - FROM {$wpdb->prefix}woocommerce_api_keys - WHERE consumer_key = '%s' - ", $consumer_key ), ARRAY_A ); - - if ( empty( $keys ) ) { - throw new Exception( __( 'Consumer key is invalid.', 'woocommerce' ), 401 ); - } - - return $keys; - } - - /** - * Get user by ID - * - * @since 2.4.0 - * @param int $user_id - * @return WC_User - * @throws Exception - */ - private function get_user_by_id( $user_id ) { - $user = get_user_by( 'id', $user_id ); - - if ( ! $user ) { - throw new Exception( __( 'API user is invalid', 'woocommerce' ), 401 ); - } - - return $user; - } - - /** - * Check if the consumer secret provided for the given user is valid - * - * @since 2.1 - * @param string $keys_consumer_secret - * @param string $consumer_secret - * @return bool - */ - private function is_consumer_secret_valid( $keys_consumer_secret, $consumer_secret ) { - return hash_equals( $keys_consumer_secret, $consumer_secret ); - } - - /** - * Verify that the consumer-provided request signature matches our generated signature, this ensures the consumer - * has a valid key/secret - * - * @param array $keys - * @param array $params the request parameters - * @throws Exception - */ - private function check_oauth_signature( $keys, $params ) { - /*$http_method = strtoupper( WC()->api->server->method ); - - $server_path = WC()->api->server->path; - - // if the requested URL has a trailingslash, make sure our base URL does as well - if ( isset( $_SERVER['REDIRECT_URL'] ) && '/' === substr( $_SERVER['REDIRECT_URL'], -1 ) ) { - $server_path .= '/'; - } - - $base_request_uri = rawurlencode( untrailingslashit( get_woocommerce_api_url( '' ) ) . $server_path );*/ - // Hack for url // url pretty permalinks error for localhost/index.php/wc-api/v3/... for check signature - $http_method = strtoupper( $_SERVER['REQUEST_METHOD'] ); - $request_path = parse_url( $_SERVER['REQUEST_URI'], PHP_URL_PATH ); - $wp_base = get_home_url( null, '/', 'relative' ); - if ( substr( $request_path, 0, strlen( $wp_base ) ) === $wp_base ) { - $request_path = substr( $request_path, strlen( $wp_base ) ); - } - $base_request_uri = rawurlencode( get_home_url( null, $request_path ) ); - - // Get the signature provided by the consumer and remove it from the parameters prior to checking the signature - $consumer_signature = rawurldecode( str_replace( ' ', '+', $params['oauth_signature'] ) ); - unset( $params['oauth_signature'] ); - - // Sort parameters - if ( ! uksort( $params, 'strcmp' ) ) { - throw new Exception( __( 'Invalid signature - failed to sort parameters.', 'woocommerce' ), 401 ); - } - - // Normalize parameter key/values - $params = $this->normalize_parameters( $params ); - $query_parameters = array(); - foreach ( $params as $param_key => $param_value ) { - if ( is_array( $param_value ) ) { - foreach ( $param_value as $param_key_inner => $param_value_inner ) { - $query_parameters[] = $param_key . '%255B' . $param_key_inner . '%255D%3D' . $param_value_inner; - } - } else { - $query_parameters[] = $param_key . '%3D' . $param_value; // join with equals sign - } - } - $query_string = implode( '%26', $query_parameters ); // join with ampersand - - $string_to_sign = $http_method . '&' . $base_request_uri . '&' . $query_string; - - if ( 'HMAC-SHA1' !== $params['oauth_signature_method'] && 'HMAC-SHA256' !== $params['oauth_signature_method'] ) { - throw new Exception( __( 'Invalid signature - signature method is invalid.', 'woocommerce' ), 401 ); - } - - $hash_algorithm = strtolower( str_replace( 'HMAC-', '', $params['oauth_signature_method'] ) ); - - $secret = $keys['consumer_secret'] . '&'; - $signature = base64_encode( hash_hmac( $hash_algorithm, $string_to_sign, $secret, true ) ); - - if ( ! hash_equals( $signature, $consumer_signature ) ) { - throw new Exception( __( 'Invalid signature - provided signature does not match.', 'woocommerce' ), 401 ); - } - } - - /** - * Normalize each parameter by assuming each parameter may have already been - * encoded, so attempt to decode, and then re-encode according to RFC 3986 - * - * Note both the key and value is normalized so a filter param like: - * - * 'filter[period]' => 'week' - * - * is encoded to: - * - * 'filter%5Bperiod%5D' => 'week' - * - * This conforms to the OAuth 1.0a spec which indicates the entire query string - * should be URL encoded - * - * @since 2.1 - * @see rawurlencode() - * @param array $parameters un-normalized pararmeters - * @return array normalized parameters - */ - private function normalize_parameters( $parameters ) { - $keys = WC_API_Authentication::urlencode_rfc3986( array_keys( $parameters ) ); - $values = WC_API_Authentication::urlencode_rfc3986( array_values( $parameters ) ); - $parameters = array_combine( $keys, $values ); - return $parameters; - } - - /** - * Encodes a value according to RFC 3986. Supports multidimensional arrays. - * - * @since 2.4 - * @param string|array $value The value to encode - * @return string|array Encoded values - */ - public static function urlencode_rfc3986( $value ) { - if ( is_array( $value ) ) { - return array_map( array( 'WC_API_Authentication', 'urlencode_rfc3986' ), $value ); - } else { - // Percent symbols (%) must be double-encoded - return str_replace( '%', '%25', rawurlencode( rawurldecode( $value ) ) ); - } - } - - /** - * Verify that the timestamp and nonce provided with the request are valid. This prevents replay attacks where - * an attacker could attempt to re-send an intercepted request at a later time. - * - * - A timestamp is valid if it is within 15 minutes of now - * - A nonce is valid if it has not been used within the last 15 minutes - * - * @param array $keys - * @param int $timestamp the unix timestamp for when the request was made - * @param string $nonce a unique (for the given user) 32 alphanumeric string, consumer-generated - * @throws Exception - */ - private function check_oauth_timestamp_and_nonce( $keys, $timestamp, $nonce ) { - global $wpdb; - - $valid_window = 15 * 60; // 15 minute window - - if ( ( $timestamp < time() - $valid_window ) || ( $timestamp > time() + $valid_window ) ) { - throw new Exception( __( 'Invalid timestamp.', 'woocommerce' ), 401 ); - } - - $used_nonces = maybe_unserialize( $keys['nonces'] ); - - if ( empty( $used_nonces ) ) { - $used_nonces = array(); - } - - if ( in_array( $nonce, $used_nonces ) ) { - throw new Exception( __( 'Invalid nonce - nonce has already been used.', 'woocommerce' ), 401 ); - } - - $used_nonces[ $timestamp ] = $nonce; - - // Remove expired nonces - foreach ( $used_nonces as $nonce_timestamp => $nonce ) { - if ( $nonce_timestamp < ( time() - $valid_window ) ) { - unset( $used_nonces[ $nonce_timestamp ] ); - } - } - - $used_nonces = maybe_serialize( $used_nonces ); - - $wpdb->update( - $wpdb->prefix . 'woocommerce_api_keys', - array( 'nonces' => $used_nonces ), - array( 'key_id' => $keys['key_id'] ), - array( '%s' ), - array( '%d' ) - ); - } - - /** - * Check that the API keys provided have the proper key-specific permissions to either read or write API resources - * - * @param string $key_permissions - * @throws Exception if the permission check fails - */ - public function check_api_key_permissions( $key_permissions ) { - switch ( WC()->api->server->method ) { - - case 'HEAD': - case 'GET': - if ( 'read' !== $key_permissions && 'read_write' !== $key_permissions ) { - throw new Exception( __( 'The API key provided does not have read permissions.', 'woocommerce' ), 401 ); - } - break; - - case 'POST': - case 'PUT': - case 'PATCH': - case 'DELETE': - if ( 'write' !== $key_permissions && 'read_write' !== $key_permissions ) { - throw new Exception( __( 'The API key provided does not have write permissions.', 'woocommerce' ), 401 ); - } - break; - } - } - - /** - * Updated API Key last access datetime - * - * @since 2.4.0 - * - * @param int $key_id - */ - private function update_api_key_last_access( $key_id ) { - global $wpdb; - - $wpdb->update( - $wpdb->prefix . 'woocommerce_api_keys', - array( 'last_access' => current_time( 'mysql' ) ), - array( 'key_id' => $key_id ), - array( '%s' ), - array( '%d' ) - ); - } -} diff --git a/htdocs/ecommerceng/includes/CurlClientEx.php b/htdocs/ecommerceng/includes/CurlClientEx.php new file mode 100644 index 0000000..a216fc7 --- /dev/null +++ b/htdocs/ecommerceng/includes/CurlClientEx.php @@ -0,0 +1,157 @@ + value` pairs) to be passed to `curl_setopt` + * + * @var array + */ + private $parameters = array(); + + /** + * Additional `curl_setopt` parameters + * + * @param array $parameters + */ + public function setCurlParameters(array $parameters) + { + $this->parameters = $parameters; + } + + /** + * @param bool $buildHttpQuery + */ + public function setHttpPostBuildQuery($buildHttpQuery) + { + $this->httpPostBuildQuery = $buildHttpQuery; + } + + /** + * @param bool $force + * + * @return CurlClientEx + */ + public function setForceSSL3($force) + { + $this->forceSSL3 = $force; + + return $this; + } + + /** + * Any implementing HTTP providers should send a request to the provided endpoint with the parameters. + * They should return, in string form, the response body and throw an exception on error. + * + * @param UriInterface $endpoint + * @param mixed $requestBody + * @param array $extraHeaders + * @param string $method + * + * @return string + * + * @throws TokenResponseException + * @throws \InvalidArgumentException + */ + public function retrieveResponse( + UriInterface $endpoint, + $requestBody, + array $extraHeaders = array(), + $method = 'POST' + ) { + // Normalize method name + $method = strtoupper($method); + + $this->normalizeHeaders($extraHeaders); + + if ($method === 'GET' && !empty($requestBody)) { + throw new \InvalidArgumentException('No body expected for "GET" request.'); + } + + if (!isset($extraHeaders['Content-Type']) && $method === 'POST' && is_array($requestBody)) { + $extraHeaders['Content-Type'] = 'Content-Type: application/x-www-form-urlencoded'; + } + + $extraHeaders['Host'] = 'Host: '.$endpoint->getHost(); + $extraHeaders['Connection'] = 'Connection: close'; + + $ch = curl_init(); + + curl_setopt($ch, CURLOPT_URL, $endpoint->getAbsoluteUri()); + + if ($method === 'POST' || $method === 'PUT') { + if ($requestBody && is_array($requestBody) && $this->httpPostBuildQuery) { + $requestBody = http_build_query($requestBody, '', '&'); + } + + if ($method === 'PUT') { + curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'PUT'); + } else { + curl_setopt($ch, CURLOPT_POST, true); + } + + curl_setopt($ch, CURLOPT_POSTFIELDS, $requestBody); + } else { + curl_setopt($ch, CURLOPT_CUSTOMREQUEST, $method); + } + + if ($this->maxRedirects > 0) { + curl_setopt($ch, CURLOPT_FOLLOWLOCATION, true); + curl_setopt($ch, CURLOPT_MAXREDIRS, $this->maxRedirects); + } + + curl_setopt($ch, CURLOPT_TIMEOUT, $this->timeout); + curl_setopt($ch, CURLOPT_RETURNTRANSFER, true); + curl_setopt($ch, CURLOPT_HEADER, false); + curl_setopt($ch, CURLOPT_HTTPHEADER, $extraHeaders); + curl_setopt($ch, CURLOPT_USERAGENT, $this->userAgent); + + foreach ($this->parameters as $key => $value) { + curl_setopt($ch, $key, $value); + } + + if ($this->forceSSL3) { + curl_setopt($ch, CURLOPT_SSLVERSION, 3); + } + + $response = curl_exec($ch); + $responseCode = curl_getinfo($ch, CURLINFO_HTTP_CODE); + + if (false === $response) { + $errNo = curl_errno($ch); + $errStr = curl_error($ch); + curl_close($ch); + if (empty($errStr)) { + throw new TokenResponseException('Failed to request resource.', $responseCode); + } + throw new TokenResponseException('cURL Error # '.$errNo.': '.$errStr, $responseCode); + } + + curl_close($ch); + + return $response; + } +} diff --git a/htdocs/ecommerceng/includes/WooCommerce/Client.php b/htdocs/ecommerceng/includes/WooCommerce/Client.php old mode 100755 new mode 100644 diff --git a/htdocs/ecommerceng/includes/WooCommerce/HttpClient/BasicAuth.php b/htdocs/ecommerceng/includes/WooCommerce/HttpClient/BasicAuth.php old mode 100755 new mode 100644 diff --git a/htdocs/ecommerceng/includes/WooCommerce/HttpClient/HttpClient.php b/htdocs/ecommerceng/includes/WooCommerce/HttpClient/HttpClient.php old mode 100755 new mode 100644 index b6754e4..48d8f5a --- a/htdocs/ecommerceng/includes/WooCommerce/HttpClient/HttpClient.php +++ b/htdocs/ecommerceng/includes/WooCommerce/HttpClient/HttpClient.php @@ -313,12 +313,17 @@ protected function lookForErrors($parsedResponse) if (!\in_array($this->response->getCode(), ['200', '201', '202'])) { $errors = !empty($parsedResponse['errors']) ? $parsedResponse['errors'] : $parsedResponse; - if (!empty($errors[0])) { - $errorMessage = $errors[0]['message']; - $errorCode = $errors[0]['code']; + if (is_array($errors)) { + if (!empty($errors[0])) { + $errorMessage = $errors[0]['message']; + $errorCode = $errors[0]['code']; + } else { + $errorMessage = $errors['message']; + $errorCode = $errors['code']; + } } else { - $errorMessage = $errors['message']; - $errorCode = $errors['code']; + $errorMessage = 'N/A'; + $errorCode = 'N/A'; } throw new HttpClientException(\sprintf('Error: %s [%s]', $errorMessage, $errorCode), $this->response->getCode(), $this->request, $this->response); @@ -335,7 +340,7 @@ protected function processResponse() $parsedResponse = \json_decode($this->response->getBody(), true); // Test if return a valid JSON. - if (JSON_ERROR_NONE !== json_last_error()) { + if ($parsedResponse === null || (function_exists('json_last_error') && JSON_ERROR_NONE !== json_last_error())) { $message = function_exists('json_last_error_msg') ? json_last_error_msg() : 'Invalid JSON returned'; throw new HttpClientException($message, $this->response->getCode(), $this->request, $this->response); } diff --git a/htdocs/ecommerceng/includes/WooCommerce/HttpClient/HttpClientException.php b/htdocs/ecommerceng/includes/WooCommerce/HttpClient/HttpClientException.php old mode 100755 new mode 100644 diff --git a/htdocs/ecommerceng/includes/WooCommerce/HttpClient/OAuth.php b/htdocs/ecommerceng/includes/WooCommerce/HttpClient/OAuth.php old mode 100755 new mode 100644 diff --git a/htdocs/ecommerceng/includes/WooCommerce/HttpClient/Options.php b/htdocs/ecommerceng/includes/WooCommerce/HttpClient/Options.php old mode 100755 new mode 100644 diff --git a/htdocs/ecommerceng/includes/WooCommerce/HttpClient/Request.php b/htdocs/ecommerceng/includes/WooCommerce/HttpClient/Request.php old mode 100755 new mode 100644 diff --git a/htdocs/ecommerceng/includes/WooCommerce/HttpClient/Response.php b/htdocs/ecommerceng/includes/WooCommerce/HttpClient/Response.php old mode 100755 new mode 100644 diff --git a/htdocs/ecommerceng/includes/WordPressClient.php b/htdocs/ecommerceng/includes/WordPressClient.php new file mode 100644 index 0000000..212a703 --- /dev/null +++ b/htdocs/ecommerceng/includes/WordPressClient.php @@ -0,0 +1,251 @@ +errors = array(); + + // Token storage + $this->storage = new DoliStorage($db, $conf); + + // Setup the credentials for the requests + $this->credentials = new Credentials( + $oauth_id, + $oauth_secret, + $callbackUrl + ); + + // Setup the api service + $this->baseApiUri = $baseApiUri.(substr($baseApiUri, -1, 1)!='/'?'/':'').'wp-json/wp/v2'; + $serviceFactory = new \OAuth\ServiceFactory(); + $this->httpClient = new CurlClientEx(); + $serviceFactory->setHttpClient($this->httpClient); + $this->apiService = $serviceFactory->createService(self::OAUTH_SERVICENAME_WORDPRESS, $this->credentials, $this->storage, array(), new Uri($baseApiUri)); + } + + /** + * POST method. + * + * @param string $endpoint API endpoint. + * @param array $data Request data. + * + * @return array + */ + public function post($endpoint, $data) + { + $this->errors = array(); + return $this->request($endpoint, 'POST', $data); + } + + /** + * POST media method. + * + * @param string $endpoint API endpoint. + * @param string $filepath File path. + * @param array $data Request data. + * + * @return array + */ + public function postmedia($endpoint, $filepath, $data) + { + $responseData = null; + $this->errors = array(); + + // Set File + if (file_exists($filepath)) { + if (function_exists('curl_file_create')) { // php 5.5+ + $cFile = curl_file_create($filepath); + } else { + $cFile = '@' . realpath($filepath); + } + $data['file'] = $cFile; + } else { + $this->errors[] = array('File not found ("'.$filepath.'").'); + return $responseData; + } + + $extraHeaders = array("Content-Type" => "multipart/form-data"); + + return $this->request($endpoint, 'POST', $data, $extraHeaders); + } + + /** + * PUT method. + * + * @param string $endpoint API endpoint. + * @param array $data Request data. + * + * @return array + */ + public function put($endpoint, $data) + { + $this->errors = array(); + return $this->request($endpoint, 'PUT', $data); + } + + /** + * GET method. + * + * @param string $endpoint API endpoint. + * @param array $parameters Request parameters. + * + * @return array + */ + public function get($endpoint, $parameters = []) + { + $this->errors = array(); + return $this->request($endpoint, 'GET', $parameters); + } + + /** + * DELETE method. + * + * @param string $endpoint API endpoint. + * @param array $parameters Request parameters. + * + * @return array + */ + public function delete($endpoint, $parameters = []) + { + $this->errors = array(); + return $this->request($endpoint, 'DELETE', $parameters); + } + + /** + * OPTIONS method. + * + * @param string $endpoint API endpoint. + * + * @return array + */ + public function options($endpoint) + { + $this->errors = array(); + return $this->request($endpoint, 'OPTIONS'); + } + + /** + * Request. + * + * @param string $endpoint API endpoint. + * @param string $method HTTP method + * @param array $body Request body if applicable (an associative array will + * automatically be converted into a urlencoded body) + * @param array $extraHeaders Extra headers if applicable. These will override service-specific + * any defaults. + * @return array + */ + public function request($endpoint, $method, $body = [], $extraHeaders = []) + { + $responseData = null; + + // Check if we have auth token + try { + $token = $this->storage->retrieveAccessToken(self::OAUTH_SERVICENAME_WORDPRESS); + } catch (Exception $e) { + $this->errors[] = $e->getMessage(); + return $responseData; + } + + // Is token expired or will token expire in the next 30 seconds + $expire = ($token->getEndOfLife() !== -9002 && $token->getEndOfLife() !== -9001 && time() > ($token->getEndOfLife() - 30)); + + // Token expired so we refresh it + if ($expire) { + try { + $this->httpClient->setHttpPostBuildQuery(true); + $token = $this->apiService->refreshAccessToken($token); + } catch (Exception $e) { + $this->errors[] = $e->getMessage(); + return $responseData; + } + } + + // Send a request with api + try { + $this->httpClient->setHttpPostBuildQuery(false); + $response = $this->apiService->request($this->baseApiUri.(substr($this->baseApiUri, -1, 1)!='/'?'/':'').$endpoint, $method, $body, $extraHeaders); + $responseData = json_decode($response, true); + } catch (Exception $e) { + $this->errors[] = $e->getMessage(); + } + + return $responseData; + } +} diff --git a/htdocs/ecommerceng/js/form.js b/htdocs/ecommerceng/js/form.js old mode 100755 new mode 100644 index e63cc24..ab864bf --- a/htdocs/ecommerceng/js/form.js +++ b/htdocs/ecommerceng/js/form.js @@ -12,6 +12,15 @@ function eCommerceConfirmDelete(id_form, confirmation) } } +function eCommerceConfirmWoocommerceUpdateDictTaxClass(id_form, confirmation) +{ + if (confirm(confirmation)) + { + document.getElementById(id_form+'_action').value = 'update_woocommerce_tax_class'; + eCommerceSubmitForm(id_form); + } +} + function eCommerceConfirmUpdatePriceLevel(id_form, confirmation, price_level) { jQuery('#'+id_form).on('submit', function(e) { diff --git a/htdocs/ecommerceng/langs/en_US/ecommerce.lang b/htdocs/ecommerceng/langs/en_US/ecommerce.lang old mode 100755 new mode 100644 index ee47849..75b8595 --- a/htdocs/ecommerceng/langs/en_US/ecommerce.lang +++ b/htdocs/ecommerceng/langs/en_US/ecommerce.lang @@ -1,9 +1,9 @@ # Dolibarr language file - en_Us - magento CHARSET=UTF-8 -ECommerceMagentoPriceType=Imported prices' types -ECommerceMagentoPriceTypeHT=Tax excluded -ECommerceMagentoPriceTypeTTC=Tax included -ECommerceMagentoPriceTypeDescription=Define the VAT application on Magento's imported products' prices (TE or TI) +ECommercePriceType=Imported prices' types +ECommercePriceTypeHT=Tax excluded +ECommercePriceTypeTTC=Tax included +ECommercePriceTypeDescription=Define the VAT application on E-Commerce's imported products' prices (TE or TI) ECommerceMagentoUseSpecialPriceDescription=Check to import Magento's special prices instead of public prices (public price will be imported only if special price is not defined) ECommerceMagentoUseSpecialPrice=Use Magento's special price ECommerceDashboard=ECommerce synchronization management dashboard @@ -33,15 +33,15 @@ ECommerceCatProductDescription=Select a product category to use to tags products ECommerceCatSocieteDescription=Select a customer category to use to tags thirparties in Doliarr to mark them as customers presents in Ecommerce too
Note: Module Tags/Categories must be activated. ECommerceFilterLabelDescription=Indicate a filter (exemple : store_id) ECommerceFilterValueDescription=Indicate a value du filtre (exemple : *) -ECommerceSiteTypeDescription =Select a site type +ECommerceSiteTypeDescription=Select a site type ECommerceSiteAddressDescription=Check before into your eCommerce admin that services are enabled.
Exemple : http://www.localhost.com/api/?wsdl, http://mymagentoserver/index.php/api/?wsdl ECommerceUserNameDescription=Exemple : soapuser ECommerceUserPasswordDescription=Exemple : myapikey ECommerceSite=Site ECommerceLastUpdate=Last Ecommerce->Dolibarr sync ECommerceNoUpdateSite=This site has never been synchronized -ECommerceUpdateSite=Synchronization detail of this site -ECommerceUpdateAll=Synchronize all objects +ECommerceUpdateSite=Detailed update of this site +ECommerceUpdateAll=Update all for this site ECommerceReset=Delete all links and Dolibarr linked record ECommerceResetLink=Delete all link records between Dolibarr and Ecommerce ECommerceMenu=ECommerce Sync @@ -86,11 +86,11 @@ ECommerceSynchSocieteSuccess=companies have been synchronized. ECommerceSynchProductSuccess=products have been synchronized. ECommerceSynchCommandeSuccess=orders have been synchronized. ECommerceSynchFactureSuccess=bills have been synchronized. -ECommerceSynchCommandeError=Error during synchronization of orders +ECommerceSynchCommandeError=Error during synchronization of orders ECommerceCheckIfCategoryDoesNotExistsTwice=Try to make name of your categories unique on your eCommerce platform ECommerceSynchSocieteErrorCreateUpdateSociete=Failed to synchronize thirdparty -ECommerceSyncheCommerceSocpeopleCreateError=An error occured while saving contact. +ECommerceSyncheCommerceSocpeopleCreateError=An error occured while saving contact (company ID: %s; first name: %s; last name: %s). ECommerceSyncheCommerceProductCreateError=Error creating the link between Dolibarr product and eCommerce product ECommerceSyncheCommerceCommandeCreateError=Error creating the link between Dolibarr order and eCommerce order ECommerceSyncheCommerceCategoryUpdateError=Error while updating a category. @@ -159,3 +159,44 @@ RestrictNbInSync=Restrict number of record of same object to synchronize in same SynchUnkownCustomersOnThirdParty=Use the following thirdparty for orders done on unknown customers (non logged customers) ThirdPartyForNonLoggedUsers=Third party for anonymous/non logged users +## OAuth Setup ## +ECommerceOAuthWordpressSetup=Connexion OAuth2 sur WordPress ( Allez sur cette page , puis "Ajouter un nouveau client" pour créer les informations d'identification OAuth2 ) +ECommerceSiteOAuthRedirectUri=Url de redirection +ECommerceSiteOAuthRedirectUriDescription=Utilisez l'URL suivante comme URI de redirection (Callback) quand vous créez une nouvelle application (ou un nouveau client) +ECommerceSiteOAuthId=OAuth ID +ECommerceSiteOAuthIdDescription=Id de connexion au service OAuth +ECommerceSiteOAuthSecret=OAuth secret +ECommerceSiteOAuthSecretDescription=Secret de connexion au service OAuth +ECommerceOAuthCheckToken=Allez sur cette page pour vérifier/effacer les autorisations sauvées par le fournisseur OAuth2. + +ECommerceSynchronizeProductImages=Synchronize images +ECommerceProductImagesSynchronized=Images synchronized. + +ECommerceRemoteAccessConnect=Erreur de connection au site '%s': %s +ECommerceRemoteAccessConvertRemoteObjectIntoDolibarrCommande=Erreur de recuperation des commandes sur le site '%s': %s +ECommerceRemoteAccessConvertRemoteObjectIntoDolibarrProduct=Erreur de recuperation des produits sur le site '%s': %s +ECommerceRemoteAccessConvertRemoteObjectIntoDolibarrProductVariations=Erreur de recuperation des variations du produit distant 'id:%s' sur le site '%s': %s +ECommerceRemoteAccessConvertRemoteObjectIntoDolibarrSociete=Erreur de recuperation des sociétés sur le site '%s': %s +ECommerceRemoteAccessConvertRemoteObjectIntoDolibarrSocpeople=Erreur de recuperation des sociétés (pour les contacts/adresses) sur le site '%s': %s +ECommerceRemoteAccessCreateRemoteProduct=Erreur de creation du produit 'id:%s' sur le site '%s': %s +ECommerceRemoteAccessCreateRemoteProductLink=Erreur de creation du lien pour le produit 'id:%s' pour le site '%s': %s +ECommerceRemoteAccessGetCategoryData=Erreur de récupération des données de la catégorie distante 'id:%s' sur le site '%s': %s +ECommerceRemoteAccessGetCommandeToUpdate=Erreur de récupération de la liste des commandes à mettre à jour sur le site '%s': %s +ECommerceRemoteAccessGetProductToUpdate=Erreur de récupération de la liste des produits à mettre à jour sur le site '%s': %s +ECommerceRemoteAccessGetRemoteCategoryTree=Erreur de récupération de la liste des catégories à mettre à jour sur le site '%s': %s +ECommerceRemoteAccessGetSocieteToUpdate=Erreur de récupération de la liste des sociétés à mettre à jour sur le site '%s': %s +ECommerceRemoteAccessSendFileForCommande=Erreur pour lier le fichier avec la commande distante 'id:%s' sur le site '%s': %s +ECommerceRemoteAccessSendFileForCommandeInWordpress=Erreur de l'envoi du fichier pour la commande distante 'id:%s' dans les média de Wordpress sur le site '%s': %s +ECommerceRemoteAccessUpdateRemoteCommande=Erreur de mise à jour de la commande distante 'id:%s' sur le site '%s': %s +ECommerceRemoteAccessUpdateRemoteProduct=Erreur de mise à jour du produit distant 'id:%s' sur le site '%s': %s +ECommerceRemoteAccessUpdateRemoteProductGetRemoteProduct=Erreur de récupération du produit distant 'id:%s' sur le site '%s': %s +ECommerceRemoteAccessUpdateRemoteProductSendImage=Erreur de l'envoi d'une image pour le produit distante 'id:%s' dans les média de Wordpress sur le site '%s': %s +ECommerceRemoteAccessUpdateRemoteProductVariation=Erreur de mise à jour de la variation 'id:%s' du produit distant 'id:%s' sur le site '%s': %s +ECommerceRemoteAccessUpdateRemoteSociete=Erreur de mise à jour de la société distante 'id:%s' sur le site '%s': %s +ECommerceRemoteAccessUpdateRemoteSocpeople=Erreur de mise à jour des contacts/adresses de la société distante 'id:%s' sur le site '%s': %s +ECommerceRemoteAccessUpdateRemoteStockProduct=Erreur de mise à jour des stocks de la variation 'id:%s' du produit distant 'id:%s' sur le site '%s': %s +ECommerceRemoteAccessUpdateRemoteStockProductVariation=Erreur de mise à jour des stocks du produit distant 'id:%s' sur le site '%s': %s + +ECommerceSyncheCommerceProductDownloadImageError=Erreur de récupération de l'image (%s) pour le produit 'id:%s' du produit distant 'id:%s' sur le site '%s' + +ECommerceNoDescForProductLine=L'api n'a pas pu récupérer la description du produit diff --git a/htdocs/ecommerceng/langs/en_US/woocommerce.lang b/htdocs/ecommerceng/langs/en_US/woocommerce.lang new file mode 100644 index 0000000..a4ea380 --- /dev/null +++ b/htdocs/ecommerceng/langs/en_US/woocommerce.lang @@ -0,0 +1,50 @@ +# Dolibarr language file - en_Us - woocommerce +CHARSET=UTF-8 + +ECommerceWoocommerceConnect=Erreur de connection au site '%s': %s +ECommerceWoocommerceConvertRemoteObjectIntoDolibarrCommande=Erreur de recuperation des commandes sur le site '%s': %s +ECommerceWoocommerceConvertRemoteObjectIntoDolibarrProduct=Erreur de recuperation des produits sur le site '%s': %s +ECommerceWoocommerceConvertRemoteObjectIntoDolibarrProductVariations=Erreur de recuperation des variations du produit distant 'id:%s' sur le site '%s': %s +ECommerceWoocommerceConvertRemoteObjectIntoDolibarrSociete=Erreur de recuperation des sociétés sur le site '%s': %s +ECommerceWoocommerceConvertRemoteObjectIntoDolibarrSocpeople=Erreur de recuperation des sociétés (pour les contacts/adresses) sur le site '%s': %s +ECommerceWoocommerceCreateRemoteProduct=Erreur de creation du produit 'id:%s' sur le site '%s': %s +ECommerceWoocommerceCreateRemoteProductLink=Erreur de creation du lien pour le produit 'id:%s' pour le site '%s': %s +ECommerceWoocommerceGetCategoryData=Erreur de récupération des données de la catégorie distante 'id:%s' sur le site '%s': %s +ECommerceWoocommerceGetCommandeToUpdate=Erreur de récupération de la liste des commandes à mettre à jour sur le site '%s': %s +ECommerceWoocommerceGetProductToUpdate=Erreur de récupération de la liste des produits à mettre à jour sur le site '%s': %s +ECommerceWoocommerceGetRemoteCategoryTree=Erreur de récupération de la liste des catégories à mettre à jour sur le site '%s': %s +ECommerceWoocommerceGetSocieteToUpdate=Erreur de récupération de la liste des sociétés à mettre à jour sur le site '%s': %s +ECommerceWoocommerceSendFileForCommande=Erreur pour lier le fichier avec la commande distante 'id:%s' sur le site '%s': %s +ECommerceWoocommerceSendFileForCommandeInWordpress=Erreur de l'envoi du fichier pour la commande distante 'id:%s' dans les média de Wordpress sur le site '%s': %s +ECommerceWoocommerceUpdateRemoteCommande=Erreur de mise à jour de la commande distante 'id:%s' sur le site '%s': %s +ECommerceWoocommerceUpdateRemoteProduct=Erreur de mise à jour du produit distant 'id:%s' sur le site '%s': %s +ECommerceWoocommerceUpdateRemoteProductGetRemoteProduct=Erreur de récupération du produit distant 'id:%s' sur le site '%s': %s +ECommerceWoocommerceUpdateRemoteProductSendImage=Erreur de l'envoi d'une image pour le produit distante 'id:%s' dans les média de Wordpress sur le site '%s': %s +ECommerceWoocommerceUpdateRemoteProductVariation=Erreur de mise à jour de la variation 'id:%s' du produit distant 'id:%s' sur le site '%s': %s +ECommerceWoocommerceUpdateRemoteSociete=Erreur de mise à jour de la société distante 'id:%s' sur le site '%s': %s +ECommerceWoocommerceUpdateRemoteSocpeople=Erreur de mise à jour des contacts/adresses de la société distante 'id:%s' sur le site '%s': %s +ECommerceWoocommerceUpdateRemoteStockProduct=Erreur de mise à jour des stocks de la variation 'id:%s' du produit distant 'id:%s' sur le site '%s': %s +ECommerceWoocommerceUpdateRemoteStockProductVariation=Erreur de mise à jour des stocks du produit distant 'id:%s' sur le site '%s': %s +ECommerceWoocommerceGetAllWoocommerceTaxClass=Erreur de récupération des données des classes de taxes sur le site '%s': %s +ECommerceWoocommerceGetWoocommerceTaxes=Erreur de récupération des données des taxes sur le site '%s': %s + +ECommercengWoocommerceStatus=Statut pour le site "%s" +ECommercengWoocommerceDescription=Description boutique +ECommercengWoocommerceShortDescription=Description courte boutique +ECommercengWoocommerceOnlinePayment=Paiement en ligne +ECommercengWoocommerceTaxClass=Classe de TVA pour le site "%s" + +ECommercengWoocommerceDictTaxClass=Liste des classes de TVA sur Woocommerce +ECommercengWoocommerceDictTaxClassUpdated=Dictionnaire des classes de TVA sur Woocommerce mise à jour. +ECommerceWoocommerceConfirmUpdateDictTaxClasses=Confirmer la mise à jour du dictionnaire des classes de TVA pour ce site ? +ECommerceWoocommerceUpdateDictTaxClasses=Mise à jour du dictionnaire des classes de TVA +ECommerceWoocommerceErrorDisableDictTaxClass=Erreur de déactivation d'une classe de TVA, code:"%s", erreur:"%s" +ECommerceWoocommerceErrorAddDictTaxClass=Erreur d'ajout d'une classe de TVA, code:"%s", name:"%s", erreur:"%s" + +ECommercengWoocommerceStatusDraft=Draft +ECommercengWoocommerceStatusPending=Pending +ECommercengWoocommerceStatusPrivate=Private +ECommercengWoocommerceStatusPublish=Publish + +ECommercengWoocommerceWithoutFirstnameLastname=No firstname/lastname informed +ECommercengWoocommerceLastnameNotInformed=[Lastname not informed] diff --git a/htdocs/ecommerceng/langs/fr_FR/ecommerce.lang b/htdocs/ecommerceng/langs/fr_FR/ecommerce.lang old mode 100755 new mode 100644 index a7a8fd1..e78e49d --- a/htdocs/ecommerceng/langs/fr_FR/ecommerce.lang +++ b/htdocs/ecommerceng/langs/fr_FR/ecommerce.lang @@ -1,20 +1,38 @@ # Dolibarr language file - fr_FR - ECommerce CHARSET=UTF-8 -ECommerceMagentoPriceType=Type de prix importés -ECommerceMagentoPriceTypeHT=Hors Taxes -ECommerceMagentoPriceTypeTTC=Toutes Taxes Comprises -ECommerceMagentoPriceTypeDescription=Définit l'application de la TVA sur les prix des produits Magento importés (HT ou TTC) + +##PERMISSIONS## +Permission107101=Lire les statuts de synchronisation des sites e-commerce +Permission107102=Synchroniser les sites e-commerce +Permission107103=Configurer les sites e-commerce + +##ADMIN## +Module107100Name=EcommerceNG +Module107100Desc=Module de synchronisation entre Dolibarr et les plateformes e-commerce Magento et WooCommerce +BackToListOfSites=Retour à la liste des sites +NothingToSetup=Activez le module pour accéder à sa configuration + +##SETUP## +magento=Magento +woocommerce=WooCommerce +ECommercetoDolibarr=De E-Commerce vers Dolibarr +DolibarrToeCommerce=De Dolibarr vers E-Commerce +ECommercePriceType=Type de prix importés +ECommercePriceTypeHT=Hors Taxes +ECommercePriceTypeTTC=Toutes Taxes Comprises +ECommercePriceTypeDescription=Définit l'application de la TVA sur les prix des produits E-Commerce importés (HT ou TTC) ECommerceMagentoUseSpecialPriceDescription=Cocher pour importer en priorité les prix spéciaux Magento des produits au lieu des prix publics (le prix public ne sera importé que si le prix spécial n'est pas renseigné) +WarningStockProductNotFilled=L'entrepôt n'est pas renseigné ECommerceMagentoUseSpecialPrice=Utiliser le prix spécial Magento -ECommerceDashboard=Gestion des synchronisations ECommerce -ECommerceSetup=Configuration du module Ecommerce +ECommerceDashboard=Gestion des synchronisations E-Commerce +ECommerceSetup=Configuration du module E-Commerce ECommerceAddNewSite=Ajouter un nouveau site ECommerceCreateSite=Création d'un nouveau site ECommerceSetupSite=Configuration du site ECommerceSiteName=Nom du site ECommerceCatProduct=Catégorie des produits ECommerceCatSociete=Catégorie des clients -ECommerceFilterLabel=Label du filtre +ECommerceFilterLabel=Libellé du filtre ECommerceFilterValue=Valeur du filtre ECommerceSiteType=Type de site ECommerceSiteAddress=Adresse du site @@ -22,29 +40,31 @@ ECommerceUserName=Nom d'utilisateur ECommerceUserPassword=Mot de passe - API key ECommerceUserPasswordRetype=Retapez le mot de passe - API key ECommerceConfirmDelete=Êtes vous sûre de vouloir supprimer ce site ? -ECommerceConfirmReset=Êtes vous sûre de vouloir purger les données synchronisées liées à ce site? (Attention, cette action est irréversible, toute les données synchronisées seront perdues) +ECommerceConfirmReset=Êtes vous sûre de vouloir purger les données synchronisées liées à ce site? (Attention : cette action est irréversible, toute les données synchronisées seront perdues) ECommerceSetupSaved=La configuration a bien été enregistrée. ECommerceSetupErrorDb=Une erreur est survenue lors de l'enregistrement dans la base de données. ECommerceDeleteOk=Le site a bien été supprimé. ECommerceSetupPasswordNotSame=Le mot de passe doit être identique dans les 2 champs. ECommerceSetupNameEmpty=Le nom du site est obligatoire. -ECommerceSiteNameDescription=Indiquez le nom qui sera affiché dans Dolibarr (exemple : Mon Site) -ECommerceCatProductDescription=Sélectionnez la catégorie Produit mère Dolibarr qui accueille les catégories Produit à synchroniser avec Magento (à créer une fois le module Catégories Dolibarr activé). Exemple: "Catégories Produits Magento" -ECommerceCatSocieteDescription=Sélectionnez la catégorie Tiers client Dolibarr pour marquer les clients Magento (à créer une fois le module Catégories Dolibarr activé). Exemple: "Clients Eboutique Magento" +ECommerceSiteNameDescription=Indiquez le nom qui sera affiché dans la liste des site e-commerce dans Dolibarr (exemple : Mon Site) +ECommerceCatProductDescription=Sélectionnez la catégorie Produit mère Dolibarr qui accueille les catégories Produit à synchroniser avec la plateforme e-commerce (à créer une fois le module Catégories de Dolibarr activé). Exemple: "Catégories Produits Magento" +ECommerceCatSocieteDescription=Sélectionnez la catégorie Tiers client Dolibarr pour marquer les clients e-commerce (à créer une fois le module Catégories Dolibarr activé). Exemple: "Clients Eboutique Magento" ECommerceFilterLabelDescription=Indiquez un filtre (exemple : store_id) ECommerceFilterValueDescription=Indiquez la valeur du filtre (exemple : *) ECommerceSiteTypeDescription =Sélectionnez un type de site -ECommerceSiteAddressDescription=URL des API v1 Magento. Exemple : http://localhost/api/?wsdl, http://www.example.com/magento/index.php/api/?wsdl, http://www.example.com/magento/index.php/api/soap/?wsdl (Note: L'accès par localhost peut ne pas fonctionner) -ECommerceUserNameDescription=Compte à créer dans Magento via le menu SOAP/XML-RPC users. Ce compte doit aussi être placé dans un role créé dans magento via le menu SOAP/XML-RPC roles (donnez de préférence tous les droits à ce role). Exemple : soapuser -ECommerceUserPasswordDescription=Compte à créer dans magento via le menu SOAP/XML-RPC users. Ce compte doit aussi être placé dans un role créé dans magento via le menu SOAP/XML-RPC roles (donnez de préférence tous les droits à ce role). Exemple : myapikey +ECommerceSiteAddressDescription=URL des API v1 Magento. Exemples : http://localhost/api/?wsdl, http://www.example.com/magento/index.php/api/?wsdl ou encore http://www.example.com/magento/index.php/api/soap/?wsdl (Note: L'accès par localhost peut ne pas fonctionner) +ECommerceUserNameDescription=Compte à créer dans Magento via le menu SOAP/XML-RPC users. Ce compte doit aussi être placé dans un rôle créé dans magento via le menu SOAP/XML-RPC roles (donnez de préférence tous les droits à ce role). Exemple : soapuser.
Dans WooCommerce, Clé API Client +ECommerceUserPasswordDescription=Compte à créer dans magento via le menu SOAP/XML-RPC users. Ce compte doit aussi être placé dans un role créé dans magento via le menu SOAP/XML-RPC roles (donnez de préférence tous les droits à ce rôle). Exemple : myapikey.
Dans WooCommerce, Clé API Secret Client ECommerceSite=Site ECommerceLastUpdate=Dernière mise à jour -ECommerceNoUpdateSite=Ce site n'a jamais été synchronisé ENTIEREMENT (Entierement = plus aucun élément à synchroniser) +ECommerceNoUpdateSite=Ce site n'a jamais été synchronisé ENTIÈREMENT (Entièrement = plus aucun élément à synchroniser) ECommerceUpdateSite=Synchronisation détaillée du site ECommerceUpdateAll=Tout synchroniser pour ce site +ECommerceStockProduct=Entrepôt +ECommerceStockProductDescription=Si vous définissez l'option précédente de %s à E-Commerce vers Dolibarr, les produits créés ou modifiés depuis la plateforme e-commerce seront synchronisés dans Dolibarr dans cet entrepôt.
Attention : Les stocks de Dolibarr sont modifiés lors de la synchronisation des produits et non pas pendant la synchronisation de commandes ou des factures. ECommerceReset=Supprimer de Dolibarr toutes les données issues de synchronisation de la boutique Ecommerce ECommerceResetLink=Supprimer les enregistrements liens entre Dolibarr et la boutique Ecommerce -ECommerceMenu=ECommerce synchro. +ECommerceMenu=Synchro. E-Commerce ECommerceSetupCatProductEmpty=Une catégorie produit est obligatoire. ECommerceSetupCatSocieteEmpty=Une catégorie client est obligatoire. ECommerceSetupTypeEmpty=Le type de site est obligatoire. @@ -55,7 +75,7 @@ ECommerceSiteSynchro=Synchronisation du site ECommerceProducts=Produits ECommerceCommande=Commandes ECommerceFacture=Factures -ECommerceObjectToUpdate=Eléments à synchroniser +ECommerceObjectToUpdate=Éléments à synchroniser ECommerceCountToUpdate=Nombre d'éléments à synchroniser (modifié côté Ecommerce et non à jour dans Dolibarr) ECommerceSiteErrorConnect=Un problème de connection est survenue. Merci de réessayer. ECommerceSynchronizeSociete = Synchroniser les tiers clients et contacts de tiers @@ -63,13 +83,13 @@ ECommerceSynchronizeProduct = Synchroniser les produits ECommerceSynchronizeCommande = Synchroniser les commandes ECommerceSynchronizeFacture = Synchroniser les factures ECommerceSynchSocieteErrorCreateUpdateSociete=Erreur dans le synchronisation du client -ECommerceErrorGetSocieteToUpdate=Un problème de connection est survenue lors de la récupération des sociétés. Merci de réessayer. +ECommerceErrorGetSocieteToUpdate=Un problème de connection est survenue lors de la récupération des clients. Merci de réessayer. ECommerceErrorGetProduitsToUpdate=Un problème de connection est survenue lors de la récupération des produits. Merci de réessayer. ECommerceErrorGetCategoryToUpdate=Un problème de connection est survenue lors de la récupération des catégories. Merci de réessayer. ECommerceErrorGetCommandeToUpdate=Un problème de connection est survenue lors de la récupération des commandes. Merci de réessayer. ECommerceErrorGetFactureToUpdate=Un problème de connection est survenue lors de la récupération des factures. Merci de réessayer. ECommerceReboot=Veuillez désactiver le module et le réactiver avant de réessayer. -ECommerceAnonymousCreateFailed=Echec de création de l'objet de synchronisation correspondant au tiers anonyme. +ECommerceAnonymousCreateFailed=Échec de création de l'objet de synchronisation correspondant au tiers anonyme. ECommerceNoDbAnonymous=Le tiers anonyme n'existe pas dans la base de donnée. ECommerceResetDolFactureSuccess=factures importées ont bien été supprimées. ECommerceResetSynchFactureSuccess=références de synchronisation de factures ont bien été supprimées. @@ -79,8 +99,8 @@ ECommerceResetDolProductSuccess=produits importées ont bien été supprimées. ECommerceResetSynchProductSuccess=références de synchronisation de produits ont bien été supprimées. ECommerceResetDolSocpeopleSuccess=contacts importées ont bien été supprimées. ECommerceResetSynchSocpeopleSuccess=références de synchronisation de contacts ont bien été supprimées. -ECommerceResetDolSocieteSuccess=sociétés importées ont bien été supprimées. -ECommerceResetSynchSocieteSuccess=références de synchronisation de sociétés ont bien été supprimées. +ECommerceResetDolSocieteSuccess=tiers clients importés ont bien été supprimées. +ECommerceResetSynchSocieteSuccess=références de synchronisation de tiers clients ont bien été supprimées. ECommerceResetDolCategorySuccess=catégories importées ont bien été supprimées (ce nombre peut-être supérieur à celui indiqué en incluant les enfants des catégories supprimées). ECommerceResetSynchCategorySuccess=références de synchronisation de catégories ont bien été supprimées (ce nombre peut-être supérieur à celui indiqué en incluant les enfants des références supprimées). ECommerceSyncheCommerceCategoryUpdateError=Erreur dans la mise à jour d'une catégorie. @@ -88,53 +108,96 @@ ECommerceSyncheCommerceCategoryCreateError=Erreur lors de la création d'une cat ECommerceSynchronizeCategoryProduct=Synchroniser les catégories de produits ECommerceSynchCategoryError=Erreur lors de la synchronisation des catégories. ECommerceSynchCategorySuccess=catégories ont bien été synchronisées. -ECommerceSynchCategoryNoImportRoot=La synchronization des catégories a été annulée car la catégorie d'import de Dolibarr n'est pas définies. Veuillez la définir dans la configuration de synchronisation avant de réessayer. +ECommerceSynchCategoryNoImportRoot=La synchronisation des catégories a été annulée car la catégorie d'import de Dolibarr n'est pas définie. Veuillez la définir dans la configuration de synchronisation avant de réessayer. ECommerceSynchCategoryConnectError=Une erreur de connexion est survenue durant la synchronisation des catégories. Veuillez réessayer. -ECommerceSynchSocieteSuccess=sociétés ont bien été synchronisées. +ECommerceSynchSocieteSuccess=clients ont bien été synchronisées. ECommerceSynchProductSuccess=produits ont bien été synchronisés. ECommerceSynchCommandeSuccess=commandes ont bien été synchronisées. ECommerceSynchFactureSuccess=factures ont bien été synchronisées. -ECommerceSynchECommerceSocpeopleCreateError=Une erreur est survenue lors de l'enregistrement du contact. +ECommerceSynchECommerceSocpeopleCreateError=Une erreur est survenue lors de l'enregistrement du contact (societe ID: %s; prénom: %s; nom: %s). ECommerceSetupSites=Configuration des sites ECommerceTimeout=Timeout des accès APIS +ECommerceClickUrlToTestUrl=Cliquez ici pour tester l'URL (un fichier XML ou JSON doit s'afficher) ECommerceTimeoutDescription=Timeout pour les accès aux APIs, en secondes ECommerceSetupTimeoutEmpty=Durée de connexion vide ECommerceSetupTimeoutMustBeInt=La durée de connexion doit-être un entier ECommerceCategoriesProducts=Catégories de produits -SyncCategFirst=Synchroniser les catégories d'abord +SyncCategFirst=Synchroniser d'abord les catégories ECommerceSynchCommandeError=Erreur durant la synchronisation des commandes -OnlyThirdPartyWithTags=Nb de tiers ayant le tag '%s' (donc sensé être commun avec la boutique en ligne) +OnlyThirdPartyWithTags=Nb de tiers rattachés à la catégorie %s (donc sensé être commun avec la boutique en ligne) NbInDolibarr=Nb dans l'ERP -NbInDolibarrLinkedToE=Nb dans l'ERP lié à la boutique +NbInDolibarrLinkedToE=Nb dans l'ERP liés à la boutique ModuleCustomerOrderDisabled=Non géré (module Commande Client désactivé) -ECommerceLastCompleteSync=Date de la dernière synchro de %s vers Dolibarr réalisée ENTIEREMENT (=plus aucun élément à synchroniser) -WithMagentoThirdIsModifiedIfAddressModified=Avec Magento, un tiers sera vue modifié aussi si un de ces contacts/address a été modifié +ECommerceLastCompleteSync=Date de la dernière synchronisation de %s vers Dolibarr réalisée ENTIÈREMENT (Entièrement = plus aucun élément à synchroniser) +WithMagentoThirdIsModifiedIfAddressModified=Avec Magento et WooCommerce, un tiers sera aussi considéré comme modifié si un de ces contacts/adresses a été modifié. SyncSocieteFirst=Synchroniser d'abord les tiers SyncCommandeFirst=Synchroniser d'abord les commandes -OnlyProductsIn=Only products with category %s or sub-categories of '%s' are synchronized from Dolibarr to Magento -NbInDolibarr=Nb into ERP -NbInDolibarrLinkedToE=Nb into ERP linked to ecommerce -RefreshCount=Raffraichir le comptage +OnlyProductsIn=Seuls les produits de la catégorie %s et de ses sous-catégories sont synchronisées de Dolibarr vers Magento ou WooCommerce. +NbInDolibarr=Nb dans l'ERP +NbInDolibarrLinkedToE=NB dans l'ERP liés à la boutique +RefreshCount=Rafraîchir le comptage ModuleCustomerOrderDisabled=Not supported (module Customer Order disabled) ECommerceLastCompleteSync=Last synchronize from %s to Dolibarr done completely (no more elements to synchronize) -SyncIsAutomaticInRealTime=Synchronization from Dolibarr to %s is done automatically in real time. +SyncIsAutomaticInRealTime=La synchronisation De Dolibarr vers %s est effectuée en temps réel. WithMagentoThirdIsModifiedIfAddressModified=With Magento, a third party is also flagged as modified if one of its contact/address is modified -OnlyProductCategIn=Only sub-categories of product category '%s' are synchronized from Dolibarr to Magento -OnlyThirdPartyIn=Only thirdparties with thirdparty category '%s' are synchronized from Dolibarr to Magento +OnlyProductCategIn=Seule la catégorie %s et ses sous-catégories sont synchronisée de Dolibarr vers Magento ou WooCommerce. +OnlyThirdPartyIn=Seuls les tiers de la catégorie %s et de ses sous-catégories sont synchronisées de Dolibarr vers Magento ou WooCommerce.² OnlyThirdPartyWithTags=Nb of thirdparties with tag '%s' (so that should be common with the online ecommerce shop) -WarningSoapCacheIsOn=Warning: WSDL cache is on into directory '%s'. +WarningSoapCacheIsOn=Attention : WSDL cache is on into directory %s. WarningSoapCacheIsOn2=Changer %s ou son contenu peut ne pas être vu par le PHP. SoapCacheIsOff=Votre cache PHP WSDL est inactif. SetupOfWarehouseNotDefinedForThisSite=A new or modified product were found on eCommerce shop with a different stock level than into Dolibarr. To be able to create/update them in Dolibarr, you must setup the warehouse to initialize. -ECommerceStockSyncDirection=Stock synchronisation direction -ECommerceStockSyncDirectionDescription=Define if you want to see stock decrease in Dolibarr when stock decrease on Magento (rare), or overwrite Magento stock when stock in Dolibarr is modified (common choice) -MainSyncSetup=Configuration synchronisation générale -StockSyncSetup=Configuration synchronisation stock -ErrorModuleSoapRequired=Error: PHP module SOAP is required to have this module working. +ECommerceStockSyncDirection=Synchronisation des stocks +ECommerceStockSyncDirectionDescription=Ce paramètre détermine :
- Si le stock doit être décrémenté dans Dolibarr suite à une décrémentation du stock e-commerce (rare) ou
- Si le stock e-commerce doit être remplacé par le stock Dolibarr suite à sa modification (choix commun) +MainSyncSetup=Synchronisation générale +StockSyncSetup=Synchronisation des stocks +ErrorModuleSoapRequired=Erreur: Le module PHP SOAP est requis pour le bon fonctionnement du module. SyncAll=Synchroniser tout -DangerZone=Zone dangeureuse +DangerZone=Zone dangereuse ShowDebugTools=Afficher les outils d'init/purge/debug ECommerceShipping=Frais de port ECommercePriceLevel=Niveau de prix -ECommercePriceLevelDescription=Seulement ce niveau de prix sera shynchronisé entre Dolibarr et Ecommerce -ECommerceConfirmUpdatePriceLevel=Vous avez modifié le niveau de prix et les produits sur le eCommerce vont être mises à jour.\nCette opération peut être longue, merci de confirmer. +ECommercePriceLevelDescription=Seul ce niveau de prix sera synchronisé entre Dolibarr et Ecommerce +ECommerceConfirmUpdatePriceLevel=Vous avez modifié le niveau de prix. Les produits sur le site e-commerce vont être mis à jour.\nCette opération peut être longue, merci de confirmer. + +## OAuth Setup ## +ECommerceOAuthWordpressSetup=Connexion OAuth2 sur WordPress ( Allez sur cette page , puis "Ajouter un nouveau client" pour créer les informations d'identification OAuth2 ) +ECommerceSiteOAuthRedirectUri=Url de redirection +ECommerceSiteOAuthRedirectUriDescription=Utilisez l'URL suivante comme URI de redirection (Callback) quand vous créez une nouvelle application (ou un nouveau client) +ECommerceSiteOAuthId=OAuth ID +ECommerceSiteOAuthIdDescription=Id de connexion au service OAuth +ECommerceSiteOAuthSecret=OAuth secret +ECommerceSiteOAuthSecretDescription=Secret de connexion au service OAuth +ECommerceOAuthCheckToken=Allez sur cette page pour vérifier/effacer les autorisations sauvées par le fournisseur OAuth2. + +ECommerceSynchronizeProductImages=Synchroniser les images +ECommerceProductImagesSynchronized=Les images ont été synchronisées. + +ECommerceRemoteAccessConnect=Erreur de connection au site '%s': %s +ECommerceRemoteAccessConvertRemoteObjectIntoDolibarrCommande=Erreur de recuperation des commandes sur le site '%s': %s +ECommerceRemoteAccessConvertRemoteObjectIntoDolibarrProduct=Erreur de recuperation des produits sur le site '%s': %s +ECommerceRemoteAccessConvertRemoteObjectIntoDolibarrProductVariations=Erreur de recuperation des variations du produit distant 'id:%s' sur le site '%s': %s +ECommerceRemoteAccessConvertRemoteObjectIntoDolibarrSociete=Erreur de recuperation des sociétés sur le site '%s': %s +ECommerceRemoteAccessConvertRemoteObjectIntoDolibarrSocpeople=Erreur de recuperation des sociétés (pour les contacts/adresses) sur le site '%s': %s +ECommerceRemoteAccessCreateRemoteProduct=Erreur de creation du produit 'id:%s' sur le site '%s': %s +ECommerceRemoteAccessCreateRemoteProductLink=Erreur de creation du lien pour le produit 'id:%s' pour le site '%s': %s +ECommerceRemoteAccessGetCategoryData=Erreur de récupération des données de la catégorie distante 'id:%s' sur le site '%s': %s +ECommerceRemoteAccessGetCommandeToUpdate=Erreur de récupération de la liste des commandes à mettre à jour sur le site '%s': %s +ECommerceRemoteAccessGetProductToUpdate=Erreur de récupération de la liste des produits à mettre à jour sur le site '%s': %s +ECommerceRemoteAccessGetRemoteCategoryTree=Erreur de récupération de la liste des catégories à mettre à jour sur le site '%s': %s +ECommerceRemoteAccessGetSocieteToUpdate=Erreur de récupération de la liste des sociétés à mettre à jour sur le site '%s': %s +ECommerceRemoteAccessSendFileForCommande=Erreur pour lier le fichier avec la commande distante 'id:%s' sur le site '%s': %s +ECommerceRemoteAccessSendFileForCommandeInWordpress=Erreur de l'envoi du fichier pour la commande distante 'id:%s' dans les média de Wordpress sur le site '%s': %s +ECommerceRemoteAccessUpdateRemoteCommande=Erreur de mise à jour de la commande distante 'id:%s' sur le site '%s': %s +ECommerceRemoteAccessUpdateRemoteProduct=Erreur de mise à jour du produit distant 'id:%s' sur le site '%s': %s +ECommerceRemoteAccessUpdateRemoteProductGetRemoteProduct=Erreur de récupération du produit distant 'id:%s' sur le site '%s': %s +ECommerceRemoteAccessUpdateRemoteProductSendImage=Erreur de l'envoi d'une image pour le produit distante 'id:%s' dans les média de Wordpress sur le site '%s': %s +ECommerceRemoteAccessUpdateRemoteProductVariation=Erreur de mise à jour de la variation 'id:%s' du produit distant 'id:%s' sur le site '%s': %s +ECommerceRemoteAccessUpdateRemoteSociete=Erreur de mise à jour de la société distante 'id:%s' sur le site '%s': %s +ECommerceRemoteAccessUpdateRemoteSocpeople=Erreur de mise à jour des contacts/adresses de la société distante 'id:%s' sur le site '%s': %s +ECommerceRemoteAccessUpdateRemoteStockProduct=Erreur de mise à jour des stocks de la variation 'id:%s' du produit distant 'id:%s' sur le site '%s': %s +ECommerceRemoteAccessUpdateRemoteStockProductVariation=Erreur de mise à jour des stocks du produit distant 'id:%s' sur le site '%s': %s + +ECommerceSyncheCommerceProductDownloadImageError=Erreur de récupération de l'image (%s) pour le produit 'id:%s' du produit distant 'id:%s' sur le site '%s' + +ECommerceNoDescForProductLine=L'api n'a pas pu récupérer la description du produit diff --git a/htdocs/ecommerceng/langs/fr_FR/woocommerce.lang b/htdocs/ecommerceng/langs/fr_FR/woocommerce.lang new file mode 100644 index 0000000..6eb493c --- /dev/null +++ b/htdocs/ecommerceng/langs/fr_FR/woocommerce.lang @@ -0,0 +1,50 @@ +# Dolibarr language file - fr_FR - ECommerce +CHARSET=UTF-8 + +ECommerceWoocommerceConnect=Erreur de connection au site '%s': %s +ECommerceWoocommerceConvertRemoteObjectIntoDolibarrCommande=Erreur de recuperation des commandes sur le site '%s': %s +ECommerceWoocommerceConvertRemoteObjectIntoDolibarrProduct=Erreur de recuperation des produits sur le site '%s': %s +ECommerceWoocommerceConvertRemoteObjectIntoDolibarrProductVariations=Erreur de recuperation des variations du produit distant 'id:%s' sur le site '%s': %s +ECommerceWoocommerceConvertRemoteObjectIntoDolibarrSociete=Erreur de recuperation des sociétés sur le site '%s': %s +ECommerceWoocommerceConvertRemoteObjectIntoDolibarrSocpeople=Erreur de recuperation des sociétés (pour les contacts/adresses) sur le site '%s': %s +ECommerceWoocommerceCreateRemoteProduct=Erreur de creation du produit 'id:%s' sur le site '%s': %s +ECommerceWoocommerceCreateRemoteProductLink=Erreur de creation du lien pour le produit 'id:%s' pour le site '%s': %s +ECommerceWoocommerceGetCategoryData=Erreur de récupération des données de la catégorie distante 'id:%s' sur le site '%s': %s +ECommerceWoocommerceGetCommandeToUpdate=Erreur de récupération de la liste des commandes à mettre à jour sur le site '%s': %s +ECommerceWoocommerceGetProductToUpdate=Erreur de récupération de la liste des produits à mettre à jour sur le site '%s': %s +ECommerceWoocommerceGetRemoteCategoryTree=Erreur de récupération de la liste des catégories à mettre à jour sur le site '%s': %s +ECommerceWoocommerceGetSocieteToUpdate=Erreur de récupération de la liste des sociétés à mettre à jour sur le site '%s': %s +ECommerceWoocommerceSendFileForCommande=Erreur pour lier le fichier avec la commande distante 'id:%s' sur le site '%s': %s +ECommerceWoocommerceSendFileForCommandeInWordpress=Erreur de l'envoi du fichier pour la commande distante 'id:%s' dans les média de Wordpress sur le site '%s': %s +ECommerceWoocommerceUpdateRemoteCommande=Erreur de mise à jour de la commande distante 'id:%s' sur le site '%s': %s +ECommerceWoocommerceUpdateRemoteProduct=Erreur de mise à jour du produit distant 'id:%s' sur le site '%s': %s +ECommerceWoocommerceUpdateRemoteProductGetRemoteProduct=Erreur de récupération du produit distant 'id:%s' sur le site '%s': %s +ECommerceWoocommerceUpdateRemoteProductSendImage=Erreur de l'envoi d'une image pour le produit distante 'id:%s' dans les média de Wordpress sur le site '%s': %s +ECommerceWoocommerceUpdateRemoteProductVariation=Erreur de mise à jour de la variation 'id:%s' du produit distant 'id:%s' sur le site '%s': %s +ECommerceWoocommerceUpdateRemoteSociete=Erreur de mise à jour de la société distante 'id:%s' sur le site '%s': %s +ECommerceWoocommerceUpdateRemoteSocpeople=Erreur de mise à jour des contacts/adresses de la société distante 'id:%s' sur le site '%s': %s +ECommerceWoocommerceUpdateRemoteStockProduct=Erreur de mise à jour des stocks de la variation 'id:%s' du produit distant 'id:%s' sur le site '%s': %s +ECommerceWoocommerceUpdateRemoteStockProductVariation=Erreur de mise à jour des stocks du produit distant 'id:%s' sur le site '%s': %s +ECommerceWoocommerceGetAllWoocommerceTaxClass=Erreur de récupération des données des classes de taxes sur le site '%s': %s +ECommerceWoocommerceGetWoocommerceTaxes=Erreur de récupération des données des taxes sur le site '%s': %s + +ECommercengWoocommerceStatus=Statut pour le site "%s" +ECommercengWoocommerceDescription=Description boutique +ECommercengWoocommerceShortDescription=Description courte boutique +ECommercengWoocommerceOnlinePayment=Paiement en ligne +ECommercengWoocommerceTaxClass=Classe de TVA pour le site "%s" + +ECommercengWoocommerceDictTaxClass=Liste des classes de TVA sur Woocommerce +ECommercengWoocommerceDictTaxClassUpdated=Dictionnaire des classes de TVA sur Woocommerce mise à jour. +ECommerceWoocommerceConfirmUpdateDictTaxClasses=Confirmer la mise à jour du dictionnaire des classes de TVA pour ce site ? +ECommerceWoocommerceUpdateDictTaxClasses=Mise à jour du dictionnaire des classes de TVA +ECommerceWoocommerceErrorDisableDictTaxClass=Erreur de déactivation d'une classe de TVA, code:"%s", erreur:"%s" +ECommerceWoocommerceErrorAddDictTaxClass=Erreur d'ajout d'une classe de TVA, code:"%s", name:"%s", erreur:"%s" + +ECommercengWoocommerceStatusDraft=Brouillon +ECommercengWoocommerceStatusPending=En attente de relecture +ECommercengWoocommerceStatusPrivate=Privé +ECommercengWoocommerceStatusPublish=Publié + +ECommercengWoocommerceWithoutFirstnameLastname=Pas de nom/prénom renseigné +ECommercengWoocommerceLastnameNotInformed=[Nom non renseigné] diff --git a/htdocs/ecommerceng/lib/eCommerce.lib.php b/htdocs/ecommerceng/lib/eCommerce.lib.php index 7131da7..7f194fb 100644 --- a/htdocs/ecommerceng/lib/eCommerce.lib.php +++ b/htdocs/ecommerceng/lib/eCommerce.lib.php @@ -17,8 +17,10 @@ */ +include_once DOL_DOCUMENT_ROOT.'/core/lib/files.lib.php'; dol_include_once('/ecommerceng/class/data/eCommerceProduct.class.php'); dol_include_once('/ecommerceng/class/business/eCommerceSynchro.class.php'); +dol_include_once('/ecommerceng/class/data/woocommerce/eCommerceRemoteAccessWoocommerce.class.php'); /** * Update the price for all product in the ecommerce product category for this site price level @@ -85,3 +87,399 @@ function updatePriceLevel($siteDb) return 1; } +function ecommerceng_wordpress_sanitize_file_name( $filename ) { + //$filename_raw = $filename; + $special_chars = array("?", "[", "]", "/", "\\", "=", "<", ">", ":", ";", ",", "'", "\"", "&", "$", "#", "*", "(", ")", "|", "~", "`", "!", "{", "}", "%", "+", chr(0)); + /** + * Filters the list of characters to remove from a filename. + * + * @since 2.8.0 + * + * @param array $special_chars Characters to remove. + * @param string $filename_raw Filename as it was passed into sanitize_file_name(). + */ + $filename = preg_replace( "#\x{00a0}#siu", ' ', $filename ); + $filename = str_replace( $special_chars, '', $filename ); + $filename = str_replace( array( '%20', '+' ), '-', $filename ); + $filename = preg_replace( '/[\r\n\t -]+/', '-', $filename ); + $filename = trim( $filename, '.-_' ); + + /*if ( false === strpos( $filename, '.' ) ) { + $mime_types = wp_get_mime_types(); + $filetype = wp_check_filetype( 'test.' . $filename, $mime_types ); + if ( $filetype['ext'] === $filename ) { + $filename = 'unnamed-file.' . $filetype['ext']; + } + }*/ + + // Split the filename into a base and extension[s] + //$parts = explode('.', $filename); + + // Return if only one extension + /*if ( count( $parts ) <= 2 ) { + /** + * Filters a sanitized filename string. + * + * @since 2.8.0 + * + * @param string $filename Sanitized filename. + * @param string $filename_raw The filename prior to sanitization. + */ + /* return apply_filters( 'sanitize_file_name', $filename, $filename_raw ); + }*/ + + // Process multiple extensions + /*$filename = array_shift($parts); + $extension = array_pop($parts); + $mimes = get_allowed_mime_types(); + + /* + * Loop over any intermediate extensions. Postfix them with a trailing underscore + * if they are a 2 - 5 character long alpha string not in the extension whitelist. + */ + /*foreach ( (array) $parts as $part) { + $filename .= '.' . $part; + + if ( preg_match("/^[a-zA-Z]{2,5}\d?$/", $part) ) { + $allowed = false; + foreach ( $mimes as $ext_preg => $mime_match ) { + $ext_preg = '!^(' . $ext_preg . ')$!i'; + if ( preg_match( $ext_preg, $part ) ) { + $allowed = true; + break; + } + } + if ( !$allowed ) + $filename .= '_'; + } + } + $filename .= '.' . $extension; + /** This filter is documented in wp-includes/formatting.php */ + //return apply_filters('sanitize_file_name', $filename, $filename_raw); + return $filename; +} + +function ecommerceng_download_image($image, $product, &$error_message) +{ + dol_syslog(__METHOD__.': image=' . implode(',',$image) . ' product_id=' . $product->id, LOG_DEBUG); + global $db, $conf, $maxwidthsmall, $maxheightsmall, $maxwidthmini, $maxheightmini; + + if ($product->type != Product::TYPE_PRODUCT && $product->type != Product::TYPE_SERVICE) { + $error_message = "Error the product is not a product or service type"; + dol_syslog(__METHOD__.': '.$error_message, LOG_ERR); + return false; + } + + $entity = isset($product->entity) ? $product->entity : $conf->entity; + + // Set upload directory + if (!empty($conf->global->PRODUCT_USE_OLD_PATH_FOR_PHOTO)) { // For backward compatiblity, we scan also old dirs + if ($product->type == Product::TYPE_PRODUCT) { + $upload_dir = $conf->product->multidir_output[$entity] . '/' . substr(substr("000" . $product->id, -2), 1, 1) . '/' . substr(substr("000" . $product->id, -2), 0, 1) . '/' . $product->id . "/photos"; + } else { + $upload_dir = $conf->service->multidir_output[$entity] . '/' . substr(substr("000" . $product->id, -2), 1, 1) . '/' . substr(substr("000" . $product->id, -2), 0, 1) . '/' . $product->id . "/photos"; + } + } else { + if ($product->type == Product::TYPE_PRODUCT) { + $upload_dir = $conf->product->multidir_output[$entity] . '/' . get_exdir(0, 0, 0, 0, $product, 'product') . dol_sanitizeFileName($product->ref); + } else { + $upload_dir = $conf->service->multidir_output[$entity] . '/' . get_exdir(0, 0, 0, 0, $product, 'product') . dol_sanitizeFileName($product->ref); + } + } + + // Define $destpath (path to file including filename) and $destfile (only filename) + $file_name = basename(parse_url($image['url'], PHP_URL_PATH)); + $destpath = $upload_dir . "/" . $file_name; + $destfile = $file_name; + + // lowercase extension + $info = pathinfo($destpath); + $destpath = $info['dirname'] . '/' . $info['filename'] . '.' . strtolower($info['extension']); + $info = pathinfo($destfile); + $destfile = $info['filename'] . '.' . strtolower($info['extension']); + + // Security: + // Disallow file with some extensions. We rename them. + // Because if we put the documents directory into a directory inside web root (very bad), this allows to execute on demand arbitrary code. + if (preg_match('/\.htm|\.html|\.php|\.pl|\.cgi|\.exe$/i', $destfile) && empty($conf->global->MAIN_DOCUMENT_IS_OUTSIDE_WEBROOT_SO_NOEXE_NOT_REQUIRED)) { + $destfile .= '.noexe'; + $destpath .= '.noexe'; + } + + // Check if image is modified + if (file_exists($destpath)) { + $local_image_date = new DateTime(); + $local_image_date->setTimestamp(filectime($destpath)); + $remote_image_date = new DateTime($image['date_modified']); + + if ($local_image_date >= $remote_image_date) { + return true; + } + } + + dol_syslog(__METHOD__.': upload_dir=' . $upload_dir . ' image=' . implode(',',$image) . ' product_id=' . $product->id . ' dest_path=' . $destpath, LOG_DEBUG); + + if (dol_mkdir($upload_dir) < 0) { + $error_message = "Error create product images directory ($upload_dir)"; + dol_syslog(__METHOD__.': '.$error_message, LOG_ERR); + return false; + } + + // Get file + $timeout = 5; + $ch = curl_init(); + curl_setopt($ch, CURLOPT_URL, $image['url']); + curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1); + curl_setopt($ch, CURLOPT_CONNECTTIMEOUT, $timeout); + $data = curl_exec($ch); + curl_close($ch); + if ($data === false) { + $error_message = curl_error($ch); + dol_syslog(__METHOD__.': '.$error_message, LOG_ERR); + return false; + } + + // Get in temporary file name + if (version_compare(phpversion(), '5.2.1', '<')) { + if ($conf->global->ECOMMERCE_DOWNLOAD_TMP_DIRECTORY_PATH) { + $tmp_path = $conf->global->ECOMMERCE_DOWNLOAD_TMP_DIRECTORY_PATH; + } else { + $error_message = "Error ECOMMERCE_DOWNLOAD_TMP_DIRECTORY_PATH not defined"; + dol_syslog(__METHOD__.': '.$error_message, LOG_ERR); + return false; + } + } else { + $tmp_path = sys_get_temp_dir(); + } + + if (dol_mkdir($tmp_path) < 0) { + $error_message = "Error create download temporary directory ($tmp_path)"; + dol_syslog(__METHOD__.': '.$error_message, LOG_ERR); + return false; + } + + // Save temporary file + $temp_file = tempnam($tmp_path, $destfile); + $fh = @fopen($temp_file, "w"); + if ($fh === false) { + $error_message = "Error open temporary file ($temp_file)"; + dol_syslog(__METHOD__.': '.$error_message, LOG_ERR); + return false; + } + $ret = fwrite($fh, $data); + if ($ret === false) { + $error_message = "Error write data in temporary file ($temp_file)"; + dol_syslog(__METHOD__.': '.$error_message, LOG_ERR); + return false; + } + $ret = fclose($fh); + if ($ret === false) { + $error_message = "Error close temporary file ($temp_file)"; + dol_syslog(__METHOD__.': '.$error_message, LOG_ERR); + return false; + } + + // If we need to make a virus scan + if (empty($disablevirusscan) && file_exists($temp_file) && !empty($conf->global->MAIN_ANTIVIRUS_COMMAND)) { + if (!class_exists('AntiVir')) { + require_once DOL_DOCUMENT_ROOT . '/core/class/antivir.class.php'; + } + $antivir = new AntiVir($db); + $result = $antivir->dol_avscan_file($temp_file); + if ($result < 0) // If virus or error, we stop here + { + $error_message = 'Error file is infected with a virus: ' . join(',', $antivir->errors); + dol_syslog('Files.lib::dol_move_uploaded_file File "' . $temp_file . '" (target name "' . $temp_file . '") KO with antivirus: result=' . $result . ' errors=' . join(',', $antivir->errors), LOG_ERR); + return false; + } + } + + if (!dol_move($temp_file, $destpath)) { + unlink($temp_file); + $error_message = "Error move temporary file ($temp_file) in product image directory ($destpath)"; + dol_syslog(__METHOD__.': '.$error_message, LOG_ERR); + return false; + } + + include_once DOL_DOCUMENT_ROOT . '/core/lib/images.lib.php'; + if (image_format_supported($destpath) == 1) { + // Create thumbs + // We can't use $object->addThumbs here because there is no $object known + + // Used on logon for example + $imgThumbSmall = vignette($destpath, $maxwidthsmall, $maxheightsmall, '_small', 50, "thumbs"); + // Create mini thumbs for image (Ratio is near 16/9) + // Used on menu or for setup page for example + $imgThumbMini = vignette($destpath, $maxwidthmini, $maxheightmini, '_mini', 50, "thumbs"); + } + + return true; +} + +function ecommerceng_remove_obsolete_image($product, $images, &$error_message) +{ + dol_syslog(__METHOD__.': product_id=' . $product->id . ' images=' . implode(',', $images), LOG_DEBUG); + global $db, $conf; + + if ($product->type != Product::TYPE_PRODUCT && $product->type != Product::TYPE_SERVICE) { + $error_message = "Error the product is not a product or service type"; + dol_syslog(__METHOD__.': '.$error_message, LOG_ERR); + return false; + } + + $entity = isset($product->entity) ? $product->entity : $conf->entity; + + // Set upload directory + if (!empty($conf->global->PRODUCT_USE_OLD_PATH_FOR_PHOTO)) { // For backward compatiblity, we scan also old dirs + if ($product->type == Product::TYPE_PRODUCT) { + $upload_dir = $conf->product->multidir_output[$entity] . '/' . substr(substr("000" . $product->id, -2), 1, 1) . '/' . substr(substr("000" . $product->id, -2), 0, 1) . '/' . $product->id . "/photos/"; + } else { + $upload_dir = $conf->service->multidir_output[$entity] . '/' . substr(substr("000" . $product->id, -2), 1, 1) . '/' . substr(substr("000" . $product->id, -2), 0, 1) . '/' . $product->id . "/photos/"; + } + } else { + if ($product->type == Product::TYPE_PRODUCT) { + $upload_dir = $conf->product->multidir_output[$entity] . '/' . get_exdir(0, 0, 0, 0, $product, 'product') . dol_sanitizeFileName($product->ref) . '/'; + } else { + $upload_dir = $conf->service->multidir_output[$entity] . '/' . get_exdir(0, 0, 0, 0, $product, 'product') . dol_sanitizeFileName($product->ref) . '/'; + } + } + + $images_name = []; + foreach ($images as $image) { + // Define $destpath (path to file including filename) and $destfile (only filename) + $file_name = basename(parse_url($image['url'], PHP_URL_PATH)); + $destfile = $file_name; + + // lowercase extension + $info = pathinfo($destfile); + $destfile = $info['filename'] . '.' . strtolower($info['extension']); + + // Security: + // Disallow file with some extensions. We rename them. + // Because if we put the documents directory into a directory inside web root (very bad), this allows to execute on demand arbitrary code. + if (preg_match('/\.htm|\.html|\.php|\.pl|\.cgi|\.exe$/i', $destfile) && empty($conf->global->MAIN_DOCUMENT_IS_OUTSIDE_WEBROOT_SO_NOEXE_NOT_REQUIRED)) { + $destfile .= '.noexe'; + } + + $images_name[] = $destfile; + } + + dol_syslog(__METHOD__.': upload_dir=' . $upload_dir . ' images=' . implode(',',$images_name) . ' product_id=' . $product->id, LOG_DEBUG); + + $photos = $product->liste_photos($upload_dir); + foreach ($photos as $index => $photo) { + if (! in_array($photo['photo'], $images_name, true)) { + unlink($upload_dir . $photo['photo']); + } + } + + return true; +} + +function ecommerceng_add_extrafields($db, $langs, $extrafields, &$error) { + $efields = new ExtraFields($db); + foreach ($extrafields as $extrafield) { + $result = $efields->addExtraField( + $extrafield['attrname'], + $langs->trans($extrafield['label']), + $extrafield['type'], + $extrafield['pos'], + $extrafield['size'], + $extrafield['elementtype'], + $extrafield['unique'], + $extrafield['required'], + $extrafield['default_value'], + $extrafield['param'], + $extrafield['alwayseditable'], + $extrafield['perms'], + $extrafield['list'] + ); + if ($result <= 0) { + $error = $efields->error; + return -1; + } + } +} + +function ecommerceng_update_woocommerce_dict_tax_class($db, $site) +{ + global $conf, $langs; + $langs->load('woocommerce@ecommerceng'); + + $eCommerceRemoteAccessWoocommerce = new eCommerceRemoteAccessWoocommerce($db, $site); + + if (!$eCommerceRemoteAccessWoocommerce->connect()) { + setEventMessages('', $this->errors, 'errors'); + return false; + } + + $taxClasses = $eCommerceRemoteAccessWoocommerce->getAllWoocommerceTaxClass(); + if ($taxClasses === false) { + setEventMessages('', $this->errors, 'errors'); + return false; + } + + $eCommerceDict = new eCommerceDict($db, MAIN_DB_PREFIX.'c_ecommerceng_tax_class'); + + // Get all tax class in dictionary for this entity and site + $dict_tax_classes = $eCommerceDict->search(['entity'=>['value'=>$conf->entity],'site_id'=>['value'=>$site->id]]); + + // Desactive code not found in woocommerce + foreach ($dict_tax_classes as $line) { + if (!isset($taxClasses[$line['code']])) { + // Desactive code + $result = $eCommerceDict->update(['active' => ['value' => 0]], ['rowid' => ['value' => $line['rowid']]]); + if ($result == false) { + setEventMessage($langs->trans('ECommerceWoocommerceErrorDisableDictTaxClass', $line['code'], $db->error()), 'errors'); + return false; + } + } else { + $taxClasses[$line['code']]['founded'] = true; + } + } + + // Add new code from woocommerce + foreach ($taxClasses as $taxClass) { + if (!isset($taxClass['founded'])) { + // Add new tax class code + $result = $eCommerceDict->insert(['site_id','code','label','entity','active'], ['site_id'=>['value'=>$site->id],'code'=>['value'=>$taxClass['slug'],'type'=>'string'],'label'=>['value'=>$taxClass['name'],'type'=>'string'],'entity'=>['value'=>$conf->entity],'active'=>['value'=>1]]); + if ($result == false) { + setEventMessage($langs->trans('ECommerceWoocommerceErrorAddDictTaxClass', $taxClass['slug'], $taxClass['name'], $db->error()), 'errors'); + return false; + } + } + } + + return true; +} + +function get_company_by_email($db, $email, $site=0) +{ + $email = $db->escape($email); + + $sql = "SELECT s.rowid FROM " . MAIN_DB_PREFIX . "societe AS s"; + $sql .= " LEFT JOIN " . MAIN_DB_PREFIX . "socpeople AS sp ON sp.fk_soc = s.rowid"; + if ($site > 0) $sql .= " LEFT JOIN " . MAIN_DB_PREFIX . "ecommerce_societe AS es ON es.fk_societe = s.rowid"; + $sql .= " WHERE (s.email = '$email' OR sp.email = '$email')"; + if ($site > 0) $sql .= " AND es.fk_site = $site"; + $sql .= " GROUP BY s.rowid"; + + $resql = $db->query($sql); + if ($resql) { + $num = $db->num_rows($resql); + if ($num > 1) { + $result = -2; + } elseif ($num) { + $obj = $db->fetch_object($resql); + $result = $obj->rowid; + } else { + $result = 0; + } + + $db->free($resql); + } else { + $result = -1; + } + + return $result; +} \ No newline at end of file diff --git a/htdocs/ecommerceng/patchs/dolibarr/includes/OAuth/OAuth2/Service/WordPress.php b/htdocs/ecommerceng/patchs/dolibarr/includes/OAuth/OAuth2/Service/WordPress.php new file mode 100755 index 0000000..f4fc920 --- /dev/null +++ b/htdocs/ecommerceng/patchs/dolibarr/includes/OAuth/OAuth2/Service/WordPress.php @@ -0,0 +1,96 @@ +baseApiUri = new Uri('https://addresse_de_votre_site_wordpress'); + } + } +/* + // LDR CHANGE Add approval_prompt to force the prompt if value is set to 'force' so it force return of a "refresh token" in addition to "standard token" + public $approvalPrompt='auto'; + public function setApprouvalPrompt($prompt) + { + if (!in_array($prompt, array('auto', 'force'), true)) { + // @todo Maybe could we rename this exception + throw new InvalidAccessTypeException('Invalid approuvalPrompt, expected either auto or force.'); + } + $this->approvalPrompt = $prompt; + }*/ + + /** + * {@inheritdoc} + */ + public function getAuthorizationEndpoint() + { + return new Uri(sprintf('%s/oauth/authorize', $this->baseApiUri)); + } + + /** + * {@inheritdoc} + */ + public function getAccessTokenEndpoint() + { + return new Uri(sprintf('%s/oauth/token', $this->baseApiUri)); + } + + /** + * {@inheritdoc} + */ + protected function getAuthorizationMethod() + { + return static::AUTHORIZATION_METHOD_HEADER_BEARER; + } + + /** + * {@inheritdoc} + */ + protected function parseAccessTokenResponse($responseBody) + { + $data = json_decode($responseBody, true); + + if (null === $data || !is_array($data)) { + throw new TokenResponseException('Unable to parse response: "'.(isset($responseBody)?$responseBody:'NULL').'"'); + } elseif (isset($data['error'])) { + throw new TokenResponseException('Error in retrieving token: "' . $data['error'] . '" : "'.$data['error_description'].'"'); + } + + $token = new StdOAuth2Token(); + $token->setAccessToken($data['access_token']); + $token->setLifetime($data['expires_in']); + + if (isset($data['refresh_token'])) { + $token->setRefreshToken($data['refresh_token']); + unset($data['refresh_token']); + } + + unset($data['access_token']); + unset($data['expires_in']); + + $token->setExtraParams($data); + + return $token; + } +} diff --git a/htdocs/ecommerceng/site.php b/htdocs/ecommerceng/site.php index 4d439d3..19f555a 100644 --- a/htdocs/ecommerceng/site.php +++ b/htdocs/ecommerceng/site.php @@ -214,19 +214,19 @@ // Count into Magento if (! GETPOST('test_with_no_categ_count')) { - if (! $error) $nbCategoriesToUpdate = $synchro->getNbCategoriesToUpdate(true); + if (! $error && empty($conf->global->ECOMMERCENG_NO_COUNT_UPDATE)) $nbCategoriesToUpdate = $synchro->getNbCategoriesToUpdate(true); else $nbCategoriesToUpdate='?'; if ($nbCategoriesToUpdate < 0) $error++; } if (! GETPOST('test_with_no_product_count')) { - if (! $error) $nbProductToUpdate = $synchro->getNbProductToUpdate(true); + if (! $error && empty($conf->global->ECOMMERCENG_NO_COUNT_UPDATE)) $nbProductToUpdate = $synchro->getNbProductToUpdate(true); else $nbProductToUpdate='?'; if ($nbProductToUpdate < 0) $error++; } if (! GETPOST('test_with_no_thirdparty_count')) { - if (! $error) $nbSocieteToUpdate = $synchro->getNbSocieteToUpdate(true); + if (! $error && empty($conf->global->ECOMMERCENG_NO_COUNT_UPDATE)) $nbSocieteToUpdate = $synchro->getNbSocieteToUpdate(true); else $nbSocieteToUpdate='?'; if ($nbSocieteToUpdate < 0) $error++; } @@ -234,7 +234,7 @@ { if (! empty($conf->commande->enabled)) { - if (! $error) $nbCommandeToUpdate = $synchro->getNbCommandeToUpdate(true); + if (! $error && empty($conf->global->ECOMMERCENG_NO_COUNT_UPDATE)) $nbCommandeToUpdate = $synchro->getNbCommandeToUpdate(true); else $nbCommandeToUpdate='?'; if ($nbCommandeToUpdate < 0) $error++; } @@ -243,7 +243,7 @@ { if (! empty($conf->facture->enabled)) { - if (! $error) $nbFactureToUpdate = $synchro->getNbFactureToUpdate(true); + if (! $error && empty($conf->global->ECOMMERCENG_NO_COUNT_UPDATE)) $nbFactureToUpdate = $synchro->getNbFactureToUpdate(true); else $nbFactureToUpdate='?'; if ($nbFactureToUpdate < 0) $error++; } diff --git a/htdocs/ecommerceng/sql/llx_c_ecommerceng_tax_class.key.sql b/htdocs/ecommerceng/sql/llx_c_ecommerceng_tax_class.key.sql new file mode 100644 index 0000000..ecfdfa6 --- /dev/null +++ b/htdocs/ecommerceng/sql/llx_c_ecommerceng_tax_class.key.sql @@ -0,0 +1,19 @@ +-- =================================================================== +-- Copyright (C) 2011 Auguria +-- This program is free software; you can redistribute it and/or modify +-- it under the terms of the GNU General Public License as published by +-- the Free Software Foundation; either version 2 of the License, or +-- (at your option) any later version. +-- +-- This program is distributed in the hope that it will be useful, +-- but WITHOUT ANY WARRANTY; without even the implied warranty of +-- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +-- GNU General Public License for more details. +-- +-- You should have received a copy of the GNU General Public License +-- along with this program; if not, write to the Free Software +-- Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +-- +-- =================================================================== + +ALTER TABLE llx_c_ecommerceng_tax_class ADD UNIQUE INDEX uk_c_ecommerceng_tax_class_site_id_code_entity(site_id,code,entity); diff --git a/htdocs/ecommerceng/sql/llx_c_ecommerceng_tax_class.sql b/htdocs/ecommerceng/sql/llx_c_ecommerceng_tax_class.sql new file mode 100644 index 0000000..845d266 --- /dev/null +++ b/htdocs/ecommerceng/sql/llx_c_ecommerceng_tax_class.sql @@ -0,0 +1,27 @@ +-- =================================================================== +-- Copyright (C) 2011 Auguria +-- This program is free software; you can redistribute it and/or modify +-- it under the terms of the GNU General Public License as published by +-- the Free Software Foundation; either version 2 of the License, or +-- (at your option) any later version. +-- +-- This program is distributed in the hope that it will be useful, +-- but WITHOUT ANY WARRANTY; without even the implied warranty of +-- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +-- GNU General Public License for more details. +-- +-- You should have received a copy of the GNU General Public License +-- along with this program; if not, write to the Free Software +-- Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +-- +-- =================================================================== + +create table llx_c_ecommerceng_tax_class +( + rowid integer AUTO_INCREMENT PRIMARY KEY, + site_id integer NOT NULL, + code varchar(255) NOT NULL, + label varchar(255) NOT NULL, + entity integer DEFAULT 1, + active tinyint DEFAULT 1 NOT NULL +) ENGINE=InnoDB; diff --git a/htdocs/ecommerceng/sql/llx_ecommerce_site.sql b/htdocs/ecommerceng/sql/llx_ecommerce_site.sql index 6aaddfd..2b5ac7e 100644 --- a/htdocs/ecommerceng/sql/llx_ecommerce_site.sql +++ b/htdocs/ecommerceng/sql/llx_ecommerce_site.sql @@ -29,7 +29,6 @@ CREATE TABLE llx_ecommerce_site ( fk_cat_societe integer NOT NULL, fk_cat_product integer NOT NULL, fk_warehouse integer NULL, - fk_thirdparty integer NULL, -- thirdparty for non logged users stock_sync_direction varchar(24) DEFAULT 'none', last_update datetime DEFAULT NULL, timeout integer NOT NULL DEFAULT 300, diff --git a/htdocs/ecommerceng/sql/update_anonymous_thirdparty.sql b/htdocs/ecommerceng/sql/update_anonymous_thirdparty.sql new file mode 100644 index 0000000..bb34979 --- /dev/null +++ b/htdocs/ecommerceng/sql/update_anonymous_thirdparty.sql @@ -0,0 +1 @@ +ALTER TABLE llx_ecommerce_site ADD COLUMN fk_anonymous_thirdparty integer NULL; diff --git a/htdocs/ecommerceng/sql/update_oauth.sql b/htdocs/ecommerceng/sql/update_oauth.sql new file mode 100644 index 0000000..c88ab46 --- /dev/null +++ b/htdocs/ecommerceng/sql/update_oauth.sql @@ -0,0 +1,2 @@ +ALTER TABLE llx_ecommerce_site ADD COLUMN oauth_id varchar(255) NULL after user_password; +ALTER TABLE llx_ecommerce_site ADD COLUMN oauth_secret varchar(255) NULL after oauth_id; diff --git a/htdocs/ecommerceng/sql/update_pricetype.sql b/htdocs/ecommerceng/sql/update_pricetype.sql new file mode 100644 index 0000000..e297379 --- /dev/null +++ b/htdocs/ecommerceng/sql/update_pricetype.sql @@ -0,0 +1 @@ +ALTER TABLE llx_ecommerce_site CHANGE COLUMN magento_price_type ecommerce_price_type VARCHAR(3) NOT NULL DEFAULT 'HT'; diff --git a/htdocs/ecommerceng/tpl/site.tpl.php b/htdocs/ecommerceng/tpl/site.tpl.php index 2ef182c..683a62b 100644 --- a/htdocs/ecommerceng/tpl/site.tpl.php +++ b/htdocs/ecommerceng/tpl/site.tpl.php @@ -63,13 +63,13 @@ print '
'; - if (! empty($conf->global->ECOMMERCENG_USE_THIS_THIRDPARTY_FOR_NONLOGGED_CUSTOMER)) + if (! empty($site->fk_anonymous_thirdparty)) { print ''; } @@ -117,7 +117,7 @@ @@ -144,12 +144,12 @@ @@ -178,7 +178,7 @@ @@ -215,9 +215,9 @@ @@ -250,11 +250,11 @@
'; print '
'; print $langs->trans("SynchUnkownCustomersOnThirdParty").' : '; $nonloggedthirdparty=new Societe($db); - $result = $nonloggedthirdparty->fetch($conf->global->ECOMMERCENG_USE_THIS_THIRDPARTY_FOR_NONLOGGED_CUSTOMER); + $result = $nonloggedthirdparty->fetch($site->fk_anonymous_thirdparty); print $nonloggedthirdparty->getNomUrl(1); print '
- 0) { ?> + 0||!empty($conf->global->ECOMMERCENG_NO_COUNT_UPDATE)) { ?> 0 && $nbCategoriesToUpdate>0) { + if ($nbProductToUpdate>0 && $nbCategoriesToUpdate>0&&empty($conf->global->ECOMMERCENG_NO_COUNT_UPDATE)) { ?> "> 0) { ?> + elseif ($nbProductToUpdate>0||!empty($conf->global->ECOMMERCENG_NO_COUNT_UPDATE)) { ?> - 0): ?> + 0||!empty($conf->global->ECOMMERCENG_NO_COUNT_UPDATE)): ?> 0 && $nbSocieteToUpdate>0) { ?> + if ($nbCommandeToUpdate>0 && $nbSocieteToUpdate>0&&empty($conf->global->ECOMMERCENG_NO_COUNT_UPDATE)) { ?> "> - 0) { ?> + 0||!empty($conf->global->ECOMMERCENG_NO_COUNT_UPDATE)) { ?> - 0 && $nbSocieteToUpdate>0) { ?> + 0 && $nbSocieteToUpdate>0&&empty($conf->global->ECOMMERCENG_NO_COUNT_UPDATE)) { ?> "> - 0 && $nbCommandeToUpdate>0) { ?> + 0 && $nbCommandeToUpdate>0&&empty($conf->global->ECOMMERCENG_NO_COUNT_UPDATE)) { ?> "> - 0) { ?> + 0||!empty($conf->global->ECOMMERCENG_NO_COUNT_UPDATE)) { ?>