Browse Source

Grosses modifs

Mathieu Moulin 3 years ago
parent
commit
2da1ba2e74
98 changed files with 9539 additions and 453 deletions
  1. 0 1
      app/config/.gitignore
  2. 0 2
      app/config/.htaccess
  3. 0 14
      app/config/config.inc.php.dist
  4. 6 0
      gitpush.sh
  5. 4 0
      src/bootstrap.inc.php
  6. 5 2
      src/class/controller/synchro.php
  7. 47 13
      src/class/logger/base.php
  8. 596 304
      src/class/model/base.php
  9. 3 2
      src/class/table/table.php
  10. 116 0
      src/clean/product.inc.php
  11. 40 0
      src/clean/product_attribute.inc.php
  12. 79 0
      src/clean/product_lot.inc.php
  13. 28 0
      src/clean/product_pack.inc.php
  14. 379 0
      src/clean/stock.inc.php
  15. 207 0
      src/clean/supplier_price.inc.php
  16. 18 0
      src/controller/synchro_address.php
  17. 0 21
      src/controller/synchro_category.php
  18. 0 21
      src/controller/synchro_customer.php
  19. 18 0
      src/controller/synchro_order_detail.php
  20. 18 0
      src/controller/synchro_stock.php
  21. 17 0
      src/controller/synchro_supplier_price.php
  22. 31 0
      src/install/address.inc.php
  23. 14 0
      src/install/customer.inc.php
  24. 42 0
      src/install/img.inc.php
  25. 62 0
      src/install/order.inc.php
  26. 26 0
      src/install/product.inc.php
  27. 60 0
      src/install/product_lot.inc.php
  28. 12 0
      src/install/supplier.inc.php
  29. 30 0
      src/install/supplier_price.inc.php
  30. 90 0
      src/model/address.php
  31. 1 1
      src/model/category.php
  32. 1 1
      src/model/customer.php
  33. 33 21
      src/model/order.php
  34. 96 0
      src/model/order_detail.php
  35. 46 46
      src/model/product.php
  36. 1 1
      src/model/product_lot.php
  37. 95 0
      src/model/stock.php
  38. 1 1
      src/model/supplier.php
  39. 123 0
      src/model/supplier_price.php
  40. 38 0
      src/repair/customer.inc.php
  41. 28 0
      src/repair/order.inc.php
  42. 1 0
      src/repair/product.inc.php
  43. 103 0
      src/repair/product_lot.inc.php
  44. 14 0
      src/repair/shipping.inc.php
  45. 52 0
      src/repair/stock.inc.php
  46. 62 0
      src/repair/supplier.inc.php
  47. 52 0
      src/sync/address/common.inc.php
  48. 131 0
      src/sync/address/dp.inc.php
  49. 192 0
      src/sync/address/pd.inc.php
  50. 15 0
      src/sync/common.inc.php
  51. 44 0
      src/sync/customer/common.inc.php
  52. 95 0
      src/sync/customer/dp.inc.php
  53. 384 0
      src/sync/customer/pd.inc.php
  54. 474 0
      src/sync/db.inc.php
  55. 175 0
      src/sync/order/common.inc.php
  56. 246 0
      src/sync/order/dp.inc.php
  57. 558 0
      src/sync/order/pd.inc.php
  58. 66 0
      src/sync/order_detail/common.inc.php
  59. 289 0
      src/sync/order_detail/dp.inc.php
  60. 340 0
      src/sync/order_detail/pd.inc.php
  61. 48 0
      src/sync/payment/common.inc.php
  62. 74 0
      src/sync/payment/dp.inc.php
  63. 121 0
      src/sync/payment/pd.inc.php
  64. 88 0
      src/sync/product/common.inc.php
  65. 625 0
      src/sync/product/dp.inc.php
  66. 519 0
      src/sync/product/pd.inc.php
  67. 45 0
      src/sync/product_lot/common.inc.php
  68. 138 0
      src/sync/product_lot/dp.inc.php
  69. 143 0
      src/sync/product_lot/pd.inc.php
  70. 49 0
      src/sync/shipping/common.inc.php
  71. 49 0
      src/sync/shipping_detail/common.inc.php
  72. 61 0
      src/sync/stock/common.inc.php
  73. 138 0
      src/sync/stock/dp.inc.php
  74. 108 0
      src/sync/stock/pd.inc.php
  75. 43 0
      src/sync/supplier/common.inc.php
  76. 78 0
      src/sync/supplier/dp.inc.php
  77. 192 0
      src/sync/supplier/pd.inc.php
  78. 46 0
      src/sync/supplier_price/common.inc.php
  79. 97 0
      src/sync/supplier_price/dp.inc.php
  80. 174 0
      src/sync/supplier_price/pd.inc.php
  81. 687 0
      src/sync/sync.inc.php
  82. 0 0
      src/sync/user/common.inc.php
  83. 0 0
      src/sync/user/dp.inc.php
  84. 0 0
      src/sync/user/pd.inc.php
  85. 48 0
      src/update/product.inc.php
  86. 116 0
      tmp/product.csv
  87. 1 0
      web/.htaccess
  88. 14 0
      web/address.php
  89. 2 1
      web/bootstrap.inc.php
  90. 18 0
      web/clean.php
  91. 7 1
      web/index.php
  92. 18 0
      web/install.php
  93. 0 0
      web/phpinfo_lemathou.php
  94. 18 0
      web/repair.php
  95. 14 0
      web/stock.php
  96. 14 0
      web/supplier_price.php
  97. 24 0
      web/sync.php
  98. 18 0
      web/update.php

+ 0 - 1
app/config/.gitignore

@@ -1 +0,0 @@
-*

+ 0 - 2
app/config/.htaccess

@@ -1,2 +0,0 @@
-order deny,allow
-deny from all

+ 0 - 14
app/config/config.inc.php.dist

@@ -1,14 +0,0 @@
-<?php
-
-define("DB_HOST", "localhost");
-define("DB_USER", "");
-define("DB_PASS", "");
-define("DB_BASE", "");
-
-define("PATH_ROOT", realpath(dirname(__FILE__)."/.."));
-
-define("PATH_INCLUDE", PATH_ROOT."/include");
-define("PATH_TEMPLATE", PATH_ROOT."/include/template");
-
-#error_reporting(E_ERROR | E_WARNING | E_PARSE | E_NOTICE);
-

+ 6 - 0
gitpush.sh

@@ -0,0 +1,6 @@
+#!/bin/bash
+
+cd /home/siteadm/calicote/public/calicote-sync/
+
+rsync -avz --delete src/ erp-sync-calicote@calicote.com:public_html/src/
+

+ 4 - 0
src/bootstrap.inc.php

@@ -18,6 +18,10 @@ require_once CLASS_PATH.'/logger/base.php';
 $db = new \db\mysqli(DB_HOST, DB_USER, DB_PASS, DB_BASE);
 //echo 'db : '; var_dump($db);
 $db->set_charset('utf8mb4');
+$db_p = new \db\mysqli(DB_P_HOST, DB_P_USER, DB_P_PASS, DB_P_BASE);
+$db_p->set_charset('utf8mb4');
+$db_d = new \db\mysqli(DB_D_HOST, DB_D_USER, DB_D_PASS, DB_D_BASE);
+$db_d->set_charset('utf8mb4');
 
 $logger = new \logger\base();
 

+ 5 - 2
src/class/controller/synchro.php

@@ -291,7 +291,7 @@ public function oexists_exec($type, $otype, $oid, $ref=null)
 	
 	// Check OID already mapped
 	//var_dump($map['id'].", $type, $otype, $oid");
-	if ($id = $this->model::_getidbyoid($map['id'], $type, $otype, $oid)) {
+	if ($id = $this->model::_getidbyoid($type, $otype, $oid)) {
 		//var_dump($id); die();
 		return ['response'=>false, 'reason'=>'id_already', 'map'=>$map, 't'=>$t, 'r'=>$r, 'id'=>$id, 'reason_label'=>'Objet déjà Mappé...'];
 	}
@@ -324,7 +324,10 @@ public function create_exec($type, $otype, $oid, $ref=null, $data=null)
 	
 	$r = $model::_ws_oexists($type, $otype, $oid);
 	if (empty($r) || empty($r['return'])) {
-		var_dump($r); die('COUILLE oid inexistant sur serveur distant!');
+		
+		var_dump($r);
+		var_dump("$type, $otype, $oid");
+		die('COUILLE oid inexistant sur serveur distant!');
 	}
 	//var_dump($r); die();
 	// Ancienne version local...

+ 47 - 13
src/class/logger/base.php

@@ -2,6 +2,8 @@
 
 namespace logger;
 
+define('LOG_DISPLAY', true);
+
 class base
 {
 
@@ -13,39 +15,71 @@ protected $l = [
 	'critical' => [],
 ];
 
-public function notice($infos)
+public function notice($infos, $context=[])
+{
+	$this->log('notice', $infos, $context);
+}
+
+public function info($infos, $context=[])
 {
-	$this->l['notice'][] = [microtime(), $infos];
+	$this->log('info', $infos, $context);
 }
 
-public function info($infos)
+public function warning($infos, $context=[])
 {
-	$this->l['info'][] = [microtime(), $infos];
+	$this->log('warning', $infos, $context);
 }
 
-public function warning($infos)
+public function error($infos, $context=[])
 {
-	$this->l['warning'][] = [microtime(), $infos];
+	$this->log('error', $infos, $context);
 }
 
-public function error($infos)
+public function critical($infos, $context=[])
 {
-	$this->l['error'][] = [microtime(), $infos];
+	$this->log('critical', $infos, $context);
 }
 
-public function critical($infos)
+public function log($type, $infos, $context=[])
 {
-	$this->l['critical'][] = [microtime(), $infos];
+	//static::_display_one($type, $infos, $context);
+	
+	$this->l[$type][] = [microtime(), $infos, $context];
 }
 
-public function display()
+public function display_all()
 {
-	foreach($this->l as $i=>$j) {
-		echo '<b>'.$i.'</b><br />';
+	foreach($this->l as $type=>$j) {
+		echo '<b>'.$type.'</b><br />';
 		foreach($j as $k) {
 			var_dump($k);
+			// @todo use display
 		}
 	}
 }
 
+public static function _display_one($type, $infos, $context)
+{
+	echo '<p><b>'.$type.'</b></p>';
+	static::_display($infos, $context);
+}
+
+public static function _display($infos, $context)
+{
+	if (!empty($context))
+		echo '<p>context :<br />File :'.$context[0].'<br />Method :'.$context[1].'<br />Line :'.$context[2].'</p>';
+	
+	echo '<p>infos :</p>';
+	if (is_array($infos)) foreach($infos as $name=>$info) {
+		echo '<p>'.$name.'</p>';
+		print_r($info);
+	}
+	elseif (is_string($infos) || is_numeric($infos)) {
+		echo '<p>'.$infos.'</p>';
+	}
+	else {
+		var_dump($infos);
+	}
+}
+
 }

File diff suppressed because it is too large
+ 596 - 304
src/class/model/base.php


+ 3 - 2
src/class/table/table.php

@@ -404,8 +404,6 @@ public function select_sql($params)
 		WHERE ';
 	
 	$sql .= $this->sql_params($params);
-	if (DEBUG_SQL)
-		var_dump($sql);
 	return $sql;
 }
 
@@ -424,6 +422,9 @@ public function data_insert($data, $params=[])
  */
 public function data_insert_sql($data, $params=[])
 {
+	//@todo : vérif si is_numeric => pk ?
+	if (!is_array($params))
+		$params = [];
 	//echo '<p>Création</p>'; var_dump($data);
 	$f = $v = [];
 	foreach(array_merge($data, $params) as $fieldname=>$value) {

+ 116 - 0
src/clean/product.inc.php

@@ -0,0 +1,116 @@
+<?php
+
+echo '<hr />';
+echo '<h3>Dédoublonnage extrafields</h3>';
+
+$sql = 'DELETE t2.*
+	FROM `llx_product_extrafields` t1
+	INNER JOIN `llx_product_extrafields` t2 ON t2.fk_object=t1.fk_object
+	WHERE t2.rowid>t1.rowid';
+if (isset($_GET['go']))
+	DB::d_update($sql);
+else
+	echo '<p>'.$sql.'</p>';
+
+echo '<hr />';
+echo '<h3>Dédoublonnage Réf product/product</h3>';
+
+$sql = 'SELECT p.id_product, p.reference, p2.id_product id_product2
+	FROM `ps_product` p
+	LEFT JOIN ps_product p2 ON p.id_product<p2.id_product AND p2.reference=p.reference
+	WHERE p2.id_product IS NOT NULL
+	  AND p.reference!=""
+	ORDER BY p.reference';
+$q = DB::p_select($sql);
+echo '<p>NB: '.$q->num_rows.'</p>';
+while ($row=$q->fetch_assoc()) {
+	var_dump($row);
+}
+
+echo '<hr />';
+echo '<h3>Dédoublonnage EAN product/product</h3>';
+
+$sql = 'SELECT p.id_product, p.ean13, p2.id_product id_product2
+	FROM `ps_product` p
+	LEFT JOIN ps_product p2 ON p.id_product<p2.id_product AND p2.ean13=p.ean13
+	WHERE p2.id_product IS NOT NULL
+	  AND p.ean13!=""
+	ORDER BY p.ean13';
+$q = DB::p_select($sql);
+echo '<p>NB: '.$q->num_rows.'</p>';
+while ($row=$q->fetch_assoc()) {
+	var_dump($row);
+	$sql = 'UPDATE ps_product SET ean13="" WHERE id_product='.$row['id_product2'];
+	if (isset($_GET['go']))
+		DB::p_update($sql);
+	else
+		echo '<p>'.$sql.'</p>';
+}
+
+echo '<hr />';
+echo '<h3>Dédoublonnage Réf product/product_attribute</h3>';
+
+$sql = 'SELECT p.id_product, p.reference, pa.id_product id_product3, pa.id_product_attribute
+	FROM `ps_product` p
+	LEFT JOIN ps_product_attribute pa ON pa.id_product<p.id_product AND pa.reference=p.reference AND pa.id_product > 0
+	WHERE pa.id_product IS NOT NULL
+	  AND p.reference!=""
+	ORDER BY p.reference';
+$q = DB::p_select($sql);
+echo '<p>NB: '.$q->num_rows.'</p>';
+while ($row=$q->fetch_assoc()) {
+	var_dump($row);
+}
+
+echo '<hr />';
+echo '<h3>Dédoublonnage EAN product/product_attribute</h3>';
+
+$sql = 'SELECT p.id_product, p.ean13, pa.id_product id_product3, pa.id_product_attribute
+	FROM `ps_product` p
+	LEFT JOIN ps_product_attribute pa ON pa.id_product<p.id_product AND pa.ean13=p.ean13 AND pa.id_product > 0
+	WHERE pa.id_product IS NOT NULL
+	  AND p.ean13!=""
+	ORDER BY p.ean13';
+$q = DB::p_select($sql);
+echo '<p>NB: '.$q->num_rows.'</p>';
+while ($row=$q->fetch_assoc()) {
+	var_dump($row);
+	$sql = 'UPDATE ps_product_attribute SET ean13="" WHERE id_product_attribute='.$row['id_product_attribute'];
+	if (isset($_GET['go']))
+		DB::p_update($sql);
+	else
+		echo '<p>'.$sql.'</p>';
+}
+
+echo '<hr />';
+echo '<h3>Dédoublonnage Ref product_attribute/product_attribute</h3>';
+
+$sql = 'SELECT pa.reference, pa.id_product, pa.id_product_attribute, pa2.id_product id_product2, pa2.id_product_attribute id_product_attribute2
+	FROM ps_product_attribute pa
+	INNER JOIN ps_product_attribute pa2 ON pa2.id_product_attribute<pa.id_product_attribute AND pa2.reference=pa.reference
+	WHERE pa.reference!=""
+	ORDER BY pa.reference';
+$q = DB::p_select($sql);
+echo '<p>NB: '.$q->num_rows.'</p>';
+while ($row=$q->fetch_assoc()) {
+	var_dump($row);
+}
+
+echo '<hr />';
+echo '<h3>Dédoublonnage EAN product_attribute/product_attribute</h3>';
+
+$sql = 'SELECT pa.ean13, pa.id_product, pa.id_product_attribute, pa2.id_product id_product2, pa2.id_product_attribute id_product_attribute2
+	FROM ps_product_attribute pa
+	INNER JOIN ps_product_attribute pa2 ON pa2.id_product_attribute<pa.id_product_attribute AND pa2.ean13=pa.ean13
+	WHERE pa.ean13!=""
+	ORDER BY pa.ean13';
+$q = DB::p_select($sql);
+echo '<p>NB: '.$q->num_rows.'</p>';
+while ($row=$q->fetch_assoc()) {
+	var_dump($row);
+	$sql = 'UPDATE ps_product_attribute SET ean13="" WHERE id_product_attribute='.$row['id_product_attribute2'];
+	if (isset($_GET['go']))
+		DB::p_update($sql);
+	else
+		echo '<p>'.$sql.'</p>';
+}

+ 40 - 0
src/clean/product_attribute.inc.php

@@ -0,0 +1,40 @@
+<?php
+
+// price et weight à 0 dans le produit, le reste en déclinaison
+
+$sql = 'SELECT p.id_product, count(*) as nb, p.active, p.price, p.weight, p.height, p.width, p.depth
+	FROM ps_product p
+	INNER JOIN ps_product_attribute pa ON pa.id_product=p.id_product
+	WHERE (p.price > 0 OR p.weight > 0)
+	GROUP BY p.id_product';
+
+
+$q = DB::p_select($sql);
+while ($row=$q->fetch_assoc()) {
+	var_dump($row);
+	$sql = 'SELECT id_product_attribute, price, weight FROM ps_product_attribute WHERE id_product='.$row['id_product'];
+	$q2 = DB::p_select($sql);
+	while($row2=$q2->fetch_assoc()) {
+		var_dump($row2);
+		$sql = 'UPDATE ps_product_attribute SET price="'.($row['price']+$row2['price']).'", weight="'.round($row['weight']+$row2['weight'], 6).'" WHERE id_product_attribute='.$row2['id_product_attribute'];
+		if (isset($_GET['go']))
+			DB::p_update($sql);
+		else
+			echo '<p>'.$sql.'</p>';
+		$sql = 'UPDATE ps_product_attribute_shop SET price="'.($row['price']+$row2['price']).'", weight="'.round($row['weight']+$row2['weight'], 6).'" WHERE id_product_attribute='.$row2['id_product_attribute'];
+		if (isset($_GET['go']))
+			DB::p_update($sql);
+		else
+			echo '<p>'.$sql.'</p>';
+	}
+	$sql = 'UPDATE ps_product SET price=0, weight=0 WHERE id_product='.$row['id_product'];
+	if (isset($_GET['go']))
+		DB::p_update($sql);
+	else
+		echo '<p>'.$sql.'</p>';
+	$sql = 'UPDATE ps_product_shop SET price=0 WHERE id_product='.$row['id_product'];
+	if (isset($_GET['go']))
+		DB::p_update($sql);
+	else
+		echo '<p>'.$sql.'</p>';
+}

+ 79 - 0
src/clean/product_lot.inc.php

@@ -0,0 +1,79 @@
+<?php
+
+// --
+
+$sql = 'SELECT pd.id_product, pd.id_combinaison, pd.numero_lot, pl.name, p.active, COUNT(*) as nb, COUNT(DISTINCT pd.dluo) as nbdluo
+	FROM `ps_products_dlc_dluo` pd
+	INNER JOIN ps_product p ON p.id_product=pd.id_product
+	INNER JOIN ps_product_lang pl ON pl.id_product=pd.id_product AND pl.id_lang=1
+	GROUP BY pd.id_product, pd.id_combinaison, pd.numero_lot
+	HAVING nb>1';
+
+$q = DB::p_select($sql);
+while ($row=$q->fetch_assoc()) {
+	//var_dump($row);
+	echo '<p>'.$row['id_product'].'/'.$row['id_combinaison'].'/'.$row['name'].'/'.$row['numero_lot'].'</p>';
+	$sql = 'SELECT pd.*
+		FROM `ps_products_dlc_dluo` pd
+		WHERE pd.id_product='.$row['id_product'].' AND pd.id_combinaison='.$row['id_combinaison'].' AND pd.numero_lot="'.$row['numero_lot'].'"
+		ORDER BY IF(pd.is_current_stock>0, pd.is_current_stock, 50)';
+	$q2 = DB::p_select($sql);
+	$ref = $q2->fetch_assoc();
+	var_dump($ref);
+	while ($row2=$q2->fetch_assoc()) {
+		var_dump($row2);
+		//continue;
+
+		// Réassociation commandes
+		$sql = 'SELECT COUNT(*) FROM ps_products_dlc_dluo_orders WHERE id_dlc_dluo='.$row2['id'];
+		$nb = DB::p_count($sql);
+		if ($nb>0) {
+			echo '<p style="color: red;">REASSOC CMD : '.$nb.'</p>';
+			// Recherche commandes où on modifie le nombre déjà affecté
+			$sql = 'SELECT p1.id id_new, p2.* FROM ps_products_dlc_dluo_orders p1
+				INNER JOIN ps_products_dlc_dluo_orders p2 ON p2.id_order=p1.id_order AND p2.id_product=p1.id_product AND p2.id_combinaison=p1.id_combinaison AND p1.id_dlc_dluo!=p2.id_dlc_dluo
+				WHERE p1.id_dlc_dluo='.$ref['id'].'
+				  AND p2.id_dlc_dluo='.$row2['id'];
+			echo '<p>'.$sql.'</p>';
+			$q3 = DB::p_select($sql);
+			while ($row3=$q3->fetch_assoc()) {
+				// Si on a effectivement une quantité (des fois on a 0)
+				if ($row3['quantity']>0) {
+					echo '<p style="color: red;">Transfert stock pick</p>';
+					$sql = 'UPDATE ps_products_dlc_dluo_orders SET quantity=quantity+'.$row3['quantity'].' WHERE id='.$row3['id_new'];
+					echo '<p>'.$sql.'</p>';
+					if (isset($_GET['go']))
+						DB::p_update($sql);
+				}
+				echo '<p style="color: red;">Suppression ancien stock pick</p>';
+				$sql = 'DELETE FROM ps_products_dlc_dluo_orders WHERE id='.$row3['id'];
+				echo '<p>'.$sql.'</p>';
+				if (isset($_GET['go']))
+					DB::p_delete($sql);
+			}
+			// Réaffectation lot sur le reste
+					echo '<p style="color: red;">Réaffectationn de ce qui reste</p>';
+			$sql = 'UPDATE ps_products_dlc_dluo_orders SET id_dlc_dluo='.$ref['id'].' WHERE id_dlc_dluo='.$row2['id'];
+			echo '<p>'.$sql.'</p>';
+			if (isset($_GET['go']))
+				DB::p_update($sql);
+		}
+
+		// Réaffectation du stock
+		if ($row2['stock']>0) {
+			echo '<p style="color: red;">REAFFECT STOCK : '.$row2['stock'].'</p>';
+			$sql = 'UPDATE ps_products_dlc_dluo_orders SET stock=stock+'.$row2['stock'].' WHERE id_dlc_dluo='.$ref['id'];
+			echo '<p>'.$sql.'</p>';
+			if (isset($_GET['go']))
+				DB::p_update($sql);
+		}
+		
+		// Suppression doublon
+		echo '<p style="color: red;">SUPPRESSION : '.$row2['id'].'</p>';
+		$sql = 'DELETE FROM ps_products_dlc_dluo WHERE id='.$row2['id'];
+		echo '<p>'.$sql.'</p>';
+		if (isset($_GET['go']))
+			DB::p_delete($sql);
+	}
+}
+

+ 28 - 0
src/clean/product_pack.inc.php

@@ -0,0 +1,28 @@
+<?php
+
+$sql = 'SELECT pk.id_product_pack, pk.id_product_item id_product, pk.id_product_attribute_item
+	FROM `ps_pack` pk
+	LEFT JOIN ps_product p ON p.id_product=pk.id_product_item
+	WHERE pk.id_product_attribute_item=0 AND p.id_product IS NULL';
+$q = DB::p_select($sql);
+while ($row=$q->fetch_assoc()) {
+	var_dump($row);
+	
+}
+
+// Suppression résidus de déclinaisons supprimées dans Packs
+
+$sql = 'SELECT pk.id_product_pack, pk.id_product_item id_product, pk.id_product_attribute_item id_product_attribute
+	FROM `ps_pack` pk
+	LEFT JOIN ps_product_attribute pa ON pa.id_product=pk.id_product_item AND pa.id_product_attribute=pk.id_product_attribute_item
+	WHERE pk.id_product_attribute_item != 0 AND pa.id_product_attribute IS NULL';
+$q = DB::p_select($sql);
+while ($row=$q->fetch_assoc()) {
+	var_dump($row);
+	$sql = 'DELETE FROM `ps_pack`
+	  WHERE id_product_pack='.$row['id_product_pack'].' AND id_product_item='.$row['id_product'].' AND id_product_attribute_item='.$row['id_product_attribute'];
+	if (isset($_GET['go']))
+		DB::p_delete($sql);
+	else
+	echo '<p>'.$sql.'</p>';
+}

+ 379 - 0
src/clean/stock.inc.php

@@ -0,0 +1,379 @@
+<?php
+
+// Recalcul stock depuis une certains date selon les consommations
+
+die();
+
+// Nouveaux lots pas présent avant
+
+echo '<h3>NOUVEAUX DLUO</h3>';
+
+$sql = 'SELECT pd.*
+	FROM ps_products_dlc_dluo pd
+	LEFT JOIN ps_products_dlc_dluo_bkp pd2 ON pd2.id=pd.id
+	WHERE pd2.id IS NULL';
+$q = DB::p_select($sql);
+while($row=$q->fetch_assoc()) {
+	var_dump($row);
+}
+
+echo '<h3>DIFF DLUO</h3>';
+
+$sql = 'SELECT pd.*, pd2.is_current_stock is_current_stock2
+	FROM ps_products_dlc_dluo pd
+	INNER JOIN ps_products_dlc_dluo_bkp pd2 ON pd2.id=pd.id
+	WHERE pd2.is_current_stock != pd.is_current_stock';
+$q = DB::p_select($sql);
+while($row=$q->fetch_assoc()) {
+	var_dump($row); echo '<br />';
+}
+
+// Suppression stocks moisis produits inactifs
+
+echo '<h3>PRODUITS INACTIFS -> RAZ Stock</h3>';
+
+$sql = 'SELECT s.*, p.id_product id_product2, pa.id_product_attribute id_product_attribute2
+	FROM ps_stock_available s
+	LEFT JOIN ps_product p ON p.id_product=s.id_product
+	LEFT JOIN ps_product_attribute pa ON pa.id_product_attribute=s.id_product_attribute
+	WHERE (p.id_product IS NULL OR p.active=0) AND (s.quantity>0 OR s.physical_quantity>0)';
+$q = DB::p_select($sql);
+while($row=$q->fetch_assoc()) {
+	var_dump($row);
+	$o = sync::p_o_oid('stock', 'stock_available', $row['id_stock_available']);
+	var_dump($o);
+
+	$sql = 'UPDATE ps_stock_available s
+		SET s.quantity=0, s.physical_quantity=0, s.reserved_quantity=0
+		WHERE s.id_stock_available='.$row['id_stock_available'];
+	if (isset($_GET['go']))
+		DB::p_update($sql);
+	else
+		echo '<p>'.$sql.'</p>';
+	
+	// Stock Dolibarr => Stock à 0
+	if (!empty($o)) {
+		$sql = 'UPDATE llx_product_stock
+			SET reel=0
+			WHERE rowid='.$o['d_oid'];
+		if (isset($_GET['go']))
+			DB::d_update($sql);
+		else
+			echo '<p>'.$sql.'</p>';
+	}
+
+	// Produit Dolibarr => Stock à 0
+	$o = null;
+	// Update stock produit
+	if ($row['id_product_attribute']) {
+		$o = sync::p_o_oid('product', 'product_attribute', $row['id_product_attribute2']);
+	}
+	elseif ($row['id_product2']) {
+		$o = sync::p_o_oid('product', 'product', $row['id_product2']);
+	}
+	var_dump($o);
+	if (!empty($o)) {
+		$sql = 'UPDATE llx_product
+			SET stock=0
+			WHERE rowid='.$o['d_oid'];
+		if (isset($_GET['go']))
+			DB::d_update($sql);
+		else
+			echo '<p>'.$sql.'</p>';
+	}
+
+	//break;
+}
+
+// Suppression stocks dluo moisis produits inactifs
+
+echo '<h3>PRODUITS INACTIFS -> RAZ DLUO</h3>';
+
+$sql = 'SELECT sd.*, p.id_product id_product2, pa.id_product_attribute id_product_attribute2
+	FROM ps_products_dlc_dluo sd
+	LEFT JOIN ps_product p ON p.id_product=sd.id_product
+	LEFT JOIN ps_product_attribute pa ON pa.id_product_attribute=sd.id_combinaison
+	WHERE (p.id_product IS NULL OR p.active=0) AND (sd.stock>0 OR sd.is_current_stock>0)';
+$q = DB::p_select($sql);
+while($row=$q->fetch_assoc()) {
+	var_dump($row);
+	$o = sync::p_o_oid('product_lot', 'products_dlc_dluo', $row['id']);
+	var_dump($o);
+
+	$sql = 'UPDATE ps_products_dlc_dluo sd
+		SET sd.stock=0, sd.is_current_stock=0
+		WHERE sd.id='.$row['id'];
+	if (isset($_GET['go']))
+		DB::p_update($sql);
+	else
+		echo '<p>'.$sql.'</p>';
+
+	// Lot Dolibarr => Stock à 0
+	if (!empty($o)) {
+		$sql = 'SELECT sl.rowid, pl.fk_product, pl.batch, s.rowid fk_product_stock
+			FROM llx_product_lot pl
+			INNER JOIN llx_product_stock s ON s.fk_product=pl.fk_product AND s.fk_entrepot=1
+			INNER JOIN llx_product_batch sl ON sl.fk_product_stock=s.rowid AND sl.batch=pl.batch
+			WHERE pl.rowid='.$o['d_oid'];
+		$q2 = DB::d_select($sql);
+		if ($q2->num_rows != 1)
+			echo '<p style="color:red;">ACHTUNG!</p>';
+		$row2 = $q2->fetch_assoc();
+		var_dump($row2);
+
+		$sql = 'UPDATE llx_product_batch sl
+			SET sl.qty=0
+			WHERE sl.rowid='.$row2['rowid'];
+		if (isset($_GET['go']))
+			DB::d_update($sql);
+		else
+			echo '<p>'.$sql.'</p>';
+	}
+
+	//break;
+}
+
+// Ajouts produits par Sandrine depuis stock juste
+
+echo '<h3>AJOUTS MANUELS -> INCREMENT</h3>';
+
+$sql = 'SELECT s.id_stock_available, s.id_product, s.id_product_attribute, p.reference, pl.name, p.id_product id_product2, pa.id_product_attribute id_product_attribute2, sm.physical_quantity
+	FROM `ps_stock_mvt` sm
+	INNER JOIN ps_stock_available s ON s.id_stock_available=sm.id_stock
+	LEFT JOIN ps_product p ON p.id_product=s.id_product
+	LEFT JOIN ps_product_lang pl ON pl.id_product=p.id_product AND pl.id_lang=1
+	LEFT JOIN ps_product_attribute pa ON pa.id_product_attribute=s.id_product_attribute
+	WHERE sm.`sign` = 1 AND sm.date_add>="2022-01-08 00:00:00"';
+$q = DB::p_select($sql);
+while($row=$q->fetch_assoc()) {
+	var_dump($row);
+	$o = sync::p_o_oid('stock', 'stock_available', $row['id_stock_available']);
+	var_dump($o);
+}
+
+// Différentiel produits commandés depuis stock juste correspondats à après la commande XXX, avant la ZZZ, sauf pour les commandes YYY
+
+echo '<h3>COMMANDES -> DECREMENT</h3>';
+
+$pl = $pl2 = $s = $l = [];
+
+//$id_order = 1055;
+// A partir de la date où le stock est bon
+$id_order = 1135;
+
+$sql = 'SELECT cd.product_id, cd.product_attribute_id, cd.id_order, pl.id, cd.product_quantity qte, opl.quantity l_qte
+	FROM ps_order_detail cd
+	INNER JOIN ps_orders c ON c.id_order=cd.id_order
+	LEFT JOIN ps_products_dlc_dluo_orders opl ON opl.id_order=cd.id_order AND opl.id_product=cd.product_id AND opl.id_combinaison=cd.product_attribute_id
+	LEFT JOIN ps_products_dlc_dluo_bkp pl ON pl.id=opl.id_dlc_dluo
+	WHERE cd.id_order >= '.$id_order.' AND cd.id_order NOT IN (1137, 1142)';
+$q = DB::p_select($sql);
+while($row=$q->fetch_assoc()) {
+	var_dump($row);
+
+	// KEY order-product
+	$ck = $row['id_order'].'-'.$row['product_id'].'-'.$row['product_attribute_id'];
+	$pk = $row['product_id'].'-'.$row['product_attribute_id'];
+
+	// command qte Increment
+	if (empty($s[$ck])) {
+		if ($row['l_qte']>$row['qte'])
+			$row['l_qte'] = $row['qte'];
+		$s[$ck] = [$row['qte'], $row['l_qte']];
+	}
+	else{
+		if ($row['l_qte']>$s[$ck][0]-$s[$ck][1])
+			$row['l_qte'] = $s[$ck][0]-$s[$ck][1];
+		$s[$ck][1] += $row['l_qte'];
+	}
+
+	// Lot update
+	if ($row['id']) {
+		if (!isset($pl[$row['id']]))
+			$pl[$row['id']] = ($row['l_qte']>0 ?$row['l_qte'] :0);
+		else
+			$pl[$row['id']] += ($row['l_qte']>0 ?$row['l_qte'] :0);
+	}
+}
+
+var_dump($pl);
+
+// Ce qu'il reste à assigner à des lots
+foreach($s as $sk=>$qte) {
+	if ($qte[0] != $qte[1]) {
+		list($id_order, $id_product, $id_product_attribute) = explode('-', $sk);
+
+		$pk = $id_product.'-'.$id_product_attribute;
+		if (!isset($pl2[$pk]))
+			$pl2[$pk] = $qte[0] - $qte[1];
+		else
+			$pl2[$pk] += $qte[0] - $qte[1];
+	}
+}
+
+$pl0 = [];
+
+// Impact lots en cours
+foreach ($pl2 as $pk=>$qte) {
+	list($id_product, $id_product_attribute) = explode('-', $pk);
+
+	$sql = 'SELECT id, stock
+		FROM ps_products_dlc_dluo_bkp
+		WHERE id_product='.$id_product.' AND id_combinaison='.$id_product_attribute.' AND is_current_stock>0 AND stock>0
+		ORDER BY is_current_stock
+		LIMIT 1';
+	$q = DB::p_select($sql);
+	if ($row = $q->fetch_assoc()) {
+		if (!isset($pl0[$row['id']]))
+			$pl0[$row['id']] = $row['stock'];
+		if (!isset($pl[$row['id']]))
+			$pl[$row['id']] = $qte;
+		else
+			$pl[$row['id']] += $qte;
+		unset($pl2[$pk]);
+	}
+}
+
+foreach ($pl as $pk=>&$qte) {
+	$sql = 'SELECT id, stock, is_current_stock, id_product, id_combinaison
+		FROM ps_products_dlc_dluo_bkp
+		WHERE id='.$pk;
+	$q = DB::p_select($sql);
+	$row = $q->fetch_assoc();
+	$pk2 = $row['id_product'].'-'.$row['id_combinaison'];
+
+	if (!isset($pl0[$row['id']]))
+		$pl0[$row['id']] = $row['stock'];
+
+	// Si qte trop élevée
+	if ($row['stock']<$qte) {
+		$qte2 = $qte - $row['stock'];
+		$pl[$pk] = $row['stock'];
+		echo '<p style="color:red;">PAS ASSEZ!</p>';
+
+		$sql = 'SELECT id, stock
+			FROM ps_products_dlc_dluo_bkp
+			WHERE id_product='.$row['id_product'].' AND id_combinaison='.$row['id_combinaison'].' AND is_current_stock>'.$row['id_combinaison'].'
+			LIMIT 1';
+		$q2 = DB::p_select($sql);
+		if ($row2 = $q2->fetch_assoc()) {
+			if (!isset($pl0[$row2['id']]))
+				$pl0[$row2['id']] = $row2['stock'];
+			// New lot
+			if (!isset($pl[$row2['id']])) {
+				if ($row2['stock']>=$qte2) {
+					$pl[$row2['id']] = $qte2;
+				}
+				else {
+					$pl[$row2['id']] = $row2['stock'];
+					$qte3 = $qte2-$row2['stock'];
+					if (!isset($pl2[$pk2]))
+						$pl2[$pk2] = $qte3;
+					else
+						$pl2[$pk2] += $qte3;
+				}
+			}
+			// Modif lot
+			else {
+				if ($row2['stock']>=$pl[$row2['id']]+$qte2) {
+					$pl[$row2['id']] += $qte2;
+				}
+				else {
+					$qte3 = $qte2+$pl[$row2['id']]-$row2['stock'];
+					$pl[$row2['id']] = $row2['stock'];
+					if (!isset($pl2[$pk2]))
+						$pl2[$pk2] = $qte3;
+					else
+						$pl2[$pk2] += $qte3;
+				}
+			}
+		}
+	}
+}
+
+foreach($pl2 as $pk=>$qte) {
+	list($id_product, $id_product_attribute) = explode('-', $pk);
+	echo '<p>Ajouter stock : '.$id_product.' / '.$id_product_attribute.' => '.$qte.'</p>';
+	$sql = 'SELECT id, stock, is_current_stock, id_product, id_combinaison
+		FROM ps_products_dlc_dluo';
+}
+
+foreach($pl as $id_dlc=>$qte) {
+	$sql = 'SELECT id, stock, is_current_stock, id_product, id_combinaison
+		FROM ps_products_dlc_dluo
+		WHERE id='.$id_dlc;
+	$q = DB::p_select($sql);
+	$row = $q->fetch_assoc();
+	$qte0 = $row['stock'];
+	$qte1 = $pl0[$id_dlc] - $qte;
+	if ($qte0==$qte1)
+		echo '<p style="color:red;">DEJA OK</p>';
+	else {
+		echo '<p style="color:red;">DIFF '.$qte0.' => '.$qte1.'</p>';
+		$sql = 'UPDATE ps_products_dlc_dluo
+			SET stock='.$qte1.'
+			WHERE id='.$id_dlc;
+		if (isset($_GET['go'])) {
+			DB::p_update($sql);
+		}
+		else
+			echo '<p>'.$sql.'</p>';
+		
+	}
+	//$o = sync::p_o_oid('product_lot', 'products_dlc_dluo', $id_dlc);
+	//var_dump($o);
+}
+
+// UPDATE STOCK USING DLUO
+
+$sql = 'SELECT s.id_stock_available, s.id_product, s.id_product_attribute, p.active, l.name, s.quantity, s.physical_quantity, s.reserved_quantity, SUM(pl.stock) stock
+	FROM ps_stock_available s
+	INNER JOIN ps_product p ON p.id_product=s.id_product
+	INNER JOIN ps_product_lang l ON l.id_product=s.id_product AND l.id_lang=1
+	INNER JOIN ps_products_dlc_dluo pl ON pl.id_product=s.id_product AND pl.id_combinaison=s.id_product_attribute
+	GROUP BY s.id_product,s.id_product_attribute
+	HAVING SUM(pl.stock) != s.quantity';
+$q = DB::p_select($sql);
+while ($row = $q->fetch_assoc()) {
+	$sql = 'UPDATE ps_stock_available s
+		SET s.quantity='.$row['stock'].', s.physical_quantity='.$row['stock'].', s.reserved_quantity=0
+		WHERE s.id_stock_available='.$row['id_stock_available'];
+	if (isset($_GET['go'])) {
+		DB::p_update($sql);
+	}
+	else
+		echo '<p>'.$sql.'</p>';
+}
+
+// SYNC DLUO
+
+$sql = 'SELECT *
+	FROM ps_products_dlc_dluo';
+$q = DB::p_select($sql);
+while ($row = $q->fetch_assoc()) {
+	if (isset($_GET['go'])) {
+		$o = sync::_action('product_lot', 'pd', 'osync', 'products_dlc_dluo', $row['id']);
+		var_dump($o); echo '<br />';
+	}
+}
+
+// SYNC STOCK
+
+$sql = 'SELECT *
+	FROM ps_stock_available s';
+$q = DB::p_select($sql);
+while ($row = $q->fetch_assoc()) {
+	if (!empty($row['id_product_attribute']))
+		$o = sync::p_o_oid('product', 'product_attribute', $row['id_product_attribute']);
+	else
+		$o = sync::p_o_oid('product', 'product', $row['id_product']);
+	var_dump($o);
+	
+	if (empty($o))
+		continue;
+	
+	if (isset($_GET['go'])) {
+		$o = sync::_action('stock', 'pd', 'osync', 'stock_available', $row['id_stock_available']);
+		var_dump($o); echo '<br />';
+	}
+}

+ 207 - 0
src/clean/supplier_price.inc.php

@@ -0,0 +1,207 @@
+<?php
+
+echo '<hr />';
+echo '<h3>Référence fournisseur obsolètes</h3>';
+
+$sql = 'SELECT DISTINCT sp.id_product_supplier, p.id_product, pl.name, IF (p.id_product IS NULL, 1, 0) product_null, IF(sp.id_product_attribute>0 AND pa.id_product_attribute IS NULL, 1, 0) pa_null, IF(sp.id_product_attribute=0 AND pa2.id_product IS NOT NULL, 1, 0) pa_new
+	FROM `ps_product_supplier` sp
+	LEFT JOIN `ps_product` p ON p.id_product=sp.id_product
+	LEFT JOIN `ps_product_lang` pl ON pl.id_product=sp.id_product AND pl.id_lang=1
+	LEFT JOIN `ps_product_attribute` pa ON pa.id_product=sp.id_product AND pa.id_product_attribute=sp.id_product_attribute
+	LEFT JOIN `ps_product_attribute` pa2 ON pa2.id_product=sp.id_product
+	WHERE p.id_product IS NULL
+		OR (sp.id_product_attribute>0 AND pa.id_product_attribute IS NULL)
+		OR (sp.id_product_attribute=0 AND pa2.id_product IS NOT NULL)';
+
+$q = DB::p_select($sql);
+echo '<p>NB: '.$q->num_rows.'</p>';
+if (true) while ($row=$q->fetch_assoc()) {
+	var_dump($row);
+	$sql = 'DELETE FROM ps_product_supplier WHERE id_product_supplier='.$row['id_product_supplier'];
+	if (isset($_GET['go']))
+		DB::p_update($sql);
+	else
+		echo '<p>'.$sql.'</p>';
+}
+
+//die();
+
+echo '<hr />';
+echo '<h3>Référence fournisseur en doublon</h3>';
+
+$sql = 'SELECT sp.*, sp2.id_product_supplier id_product_supplier2, sp2.id_product id_product2, sp2.id_product_attribute id_product_attribute2
+	FROM `ps_product_supplier` sp
+	INNER JOIN `ps_product_supplier` sp2 ON sp.id_product_supplier<sp2.id_product_supplier AND sp.id_supplier=sp2.id_supplier AND sp.product_supplier_reference=sp2.product_supplier_reference
+	WHERE sp.product_supplier_reference!=""';
+
+$q = DB::p_select($sql);
+echo '<p>NB: '.$q->num_rows.'</p>';
+if (true) while ($row=$q->fetch_assoc()) {
+	var_dump($row);
+	$sql = 'UPDATE ps_product_supplier
+		SET product_supplier_reference="" WHERE id_product_supplier='.$row['id_product_supplier2'];
+	if (isset($_GET['go']))
+		DB::p_update($sql);
+	else
+		echo '<p>'.$sql.'</p>';
+}
+
+echo '<hr />';
+echo '<h3>Différences de prix fournisseur</h3>';
+
+$sql = '(SELECT sp.*, p.wholesale_price, pl.name, p.active, p.id_category_default
+	FROM `ps_product_supplier` sp
+	INNER JOIN `ps_product` p ON p.id_product=sp.id_product
+	INNER JOIN `ps_product_lang` pl ON pl.id_product=sp.id_product AND pl.id_lang=1
+	LEFT JOIN `ps_product_attribute` pa ON pa.id_product=sp.id_product
+	WHERE sp.id_product_attribute=0 AND pa.id_product_attribute IS NULL
+		AND sp.product_supplier_price_te != p.wholesale_price)
+	UNION
+	(SELECT sp.*, pa.wholesale_price, pl.name, p.active, p.id_category_default
+	FROM `ps_product_supplier` sp
+	INNER JOIN `ps_product` p ON p.id_product=sp.id_product
+	INNER JOIN `ps_product_lang` pl ON pl.id_product=sp.id_product AND pl.id_lang=1
+	INNER JOIN `ps_product_attribute` pa ON pa.id_product=sp.id_product AND pa.id_product_attribute=sp.id_product_attribute
+	WHERE sp.product_supplier_price_te != pa.wholesale_price)';
+
+$q = DB::p_select($sql);
+echo '<p>NB: '.$q->num_rows.'</p>';
+if (false) while ($row=$q->fetch_assoc()) {
+	var_dump($row);
+}
+
+echo '<hr />';
+echo '<h3>Produits non déclinés avec prix fournisseur non saisi</h3>';
+
+$sql = 'SELECT sp.*, p.wholesale_price
+	FROM `ps_product_supplier` sp
+	INNER JOIN `ps_product` p ON p.id_product=sp.id_product
+	LEFT JOIN `ps_product_attribute` pa ON pa.id_product=sp.id_product
+	WHERE sp.id_product_attribute=0 AND pa.id_product_attribute IS NULL
+		AND sp.product_supplier_price_te=0 AND p.wholesale_price>0';
+
+$q = DB::p_select($sql);
+echo '<p>NB: '.$q->num_rows.'</p>';
+if (true) while ($row=$q->fetch_assoc()) {
+	var_dump($row);
+	$sql = 'UPDATE ps_product_supplier sp
+		SET product_supplier_price_te="'.$row['wholesale_price'].'" WHERE id_product_supplier='.$row['id_product_supplier'];
+	if (isset($_GET['go']))
+		DB::p_update($sql);
+	else
+		echo '<p>'.$sql.'</p>';
+}
+
+echo '<hr />';
+echo '<h3>Produits non déclinés avec prix de revient non saisi</h3>';
+
+$sql = 'SELECT sp.*, p.wholesale_price
+	FROM `ps_product_supplier` sp
+	INNER JOIN `ps_product` p ON p.id_product=sp.id_product
+	LEFT JOIN `ps_product_attribute` pa ON pa.id_product=sp.id_product
+	WHERE sp.id_product_attribute=0 AND pa.id_product_attribute IS NULL
+		AND sp.product_supplier_price_te>0 AND p.wholesale_price=0';
+
+$q = DB::p_select($sql);
+echo '<p>NB: '.$q->num_rows.'</p>';
+if (true) while ($row=$q->fetch_assoc()) {
+	var_dump($row);
+	$sql = 'UPDATE ps_product p
+		SET p.wholesale_price="'.$row['product_supplier_price_te'].'" WHERE p.id_product='.$row['id_product'];
+	if (isset($_GET['go']))
+		DB::p_update($sql);
+	else
+		echo '<p>'.$sql.'</p>';
+}
+
+echo '<hr />';
+echo '<h3>Produits non déclinés avec prix fournisseur différent</h3>';
+
+$sql = 'SELECT sp.*, p.wholesale_price
+	FROM `ps_product_supplier` sp
+	INNER JOIN `ps_product` p ON p.id_product=sp.id_product
+	LEFT JOIN `ps_product_attribute` pa ON pa.id_product=sp.id_product
+	WHERE sp.id_product_attribute=0 AND pa.id_product_attribute IS NULL
+		AND sp.product_supplier_price_te != p.wholesale_price';
+
+$q = DB::p_select($sql);
+echo '<p>NB: '.$q->num_rows.'</p>';
+if (true) while ($row=$q->fetch_assoc()) {
+	var_dump($row);
+	// Pb d'arrondi
+	if ((float)$row['wholesale_price'] == floor((float)$row['product_supplier_price_te'])) {
+	$sql = 'UPDATE ps_product p
+		SET p.wholesale_price="'.$row['product_supplier_price_te'].'" WHERE p.id_product='.$row['id_product'];
+	if (isset($_GET['go']))
+		if (isset($_GET['go']))
+			DB::p_update($sql);
+		else
+			echo '<p>'.$sql.'</p>';
+	}
+}
+
+echo '<hr />';
+echo '<h3>Produits déclinés avec prix fournisseur non saisi</h3>';
+
+$sql = 'SELECT sp.*, p.wholesale_price wholesale_price_product, pa.wholesale_price
+	FROM `ps_product_supplier` sp
+	INNER JOIN `ps_product` p ON p.id_product=sp.id_product
+	INNER JOIN `ps_product_attribute` pa ON pa.id_product=sp.id_product AND pa.id_product_attribute=sp.id_product_attribute
+	WHERE sp.product_supplier_price_te=0 AND pa.wholesale_price>0';
+
+$q = DB::p_select($sql);
+echo '<p>NB: '.$q->num_rows.'</p>';
+if (true) while ($row=$q->fetch_assoc()) {
+	var_dump($row);
+	$sql = 'UPDATE ps_product_supplier sp
+		SET product_supplier_price_te="'.$row['wholesale_price'].'" WHERE id_product_supplier='.$row['id_product_supplier'];
+	if (isset($_GET['go']))
+		DB::p_update($sql);
+	else
+		echo '<p>'.$sql.'</p>';
+}
+
+echo '<hr />';
+echo '<h3>Produits déclinés avec prix de revient non saisi</h3>';
+
+$sql = 'SELECT sp.*, p.wholesale_price wholesale_price_product, pa.wholesale_price
+	FROM `ps_product_supplier` sp
+	INNER JOIN `ps_product` p ON p.id_product=sp.id_product
+	INNER JOIN `ps_product_attribute` pa ON pa.id_product=sp.id_product AND pa.id_product_attribute=sp.id_product_attribute
+	WHERE sp.product_supplier_price_te>0 AND (pa.wholesale_price=0 OR pa.wholesale_price IS NULL)';
+
+$q = DB::p_select($sql);
+echo '<p>NB: '.$q->num_rows.'</p>';
+if (true) while ($row=$q->fetch_assoc()) {
+	var_dump($row);
+	$sql = 'UPDATE ps_product_attribute pa
+		SET pa.wholesale_price="'.$row['product_supplier_price_te'].'" WHERE pa.id_product_attribute='.$row['id_product_attribute'];
+	if (isset($_GET['go']))
+		DB::p_update($sql);
+	else
+		echo '<p>'.$sql.'</p>';
+}
+
+echo '<hr />';
+echo '<h3>Produits déclinés avec prix de revient différent</h3>';
+
+$sql = 'SELECT sp.*, p.wholesale_price wholesale_price_product, pa.wholesale_price
+	FROM `ps_product_supplier` sp
+	INNER JOIN `ps_product` p ON p.id_product=sp.id_product
+	INNER JOIN `ps_product_attribute` pa ON pa.id_product=sp.id_product AND pa.id_product_attribute=sp.id_product_attribute
+	WHERE sp.product_supplier_price_te != pa.wholesale_price';
+
+$q = DB::p_select($sql);
+echo '<p>NB: '.$q->num_rows.'</p>';
+if (true) while ($row=$q->fetch_assoc()) {
+	var_dump($row);
+	// Pb d'arrondi
+	if ((float)$row['wholesale_price'] == floor((float)$row['product_supplier_price_te'])) {
+	$sql = 'UPDATE ps_product_attribute pa
+		SET pa.wholesale_price="'.$row['product_supplier_price_te'].'" WHERE pa.id_product_attribute='.$row['id_product_attribute'];
+		if (isset($_GET['go']))
+			DB::p_update($sql);
+		else
+			echo '<p>'.$sql.'</p>';
+	}
+}

+ 18 - 0
src/controller/synchro_address.php

@@ -0,0 +1,18 @@
+<?php
+
+namespace Controller;
+
+require_once CLASS_PATH.'/controller/synchro.php';
+
+class synchro_address extends Synchro
+{
+
+protected $model_name = 'address';
+protected $viewmodel_name = 'sync/sync';
+
+protected $params = [];
+
+}
+
+synchro_address::__init();
+

+ 0 - 21
src/controller/synchro_category.php

@@ -12,27 +12,6 @@ protected $viewmodel_name = 'sync/sync';
 
 protected $params = [];
 
-/**
- * Associe des objets
- * Ensuite on pourra les synchroniser
- * Ordre d'association : dp = Doli => Presta
- */
-protected function assoc_exec($order='dp', $r)
-{
-	static::__log(__METHOD__);
-	
-	if(is_string($r))
-		$r = explode('-', $r);
-	var_dump($r);
-	
-	$id = $r[0];
-	$d_product_id = $r[1];
-	$p_product_id = $r[2];
-	$p_product_attribute_id = $r[3];
-	$ref = $r[4];
-	
-}
-
 }
 
 synchro_category::__init();

+ 0 - 21
src/controller/synchro_customer.php

@@ -12,27 +12,6 @@ protected $viewmodel_name = 'sync/sync';
 
 protected $params = [];
 
-/**
- * Associe des objets
- * Ensuite on pourra les synchroniser
- * Ordre d'association : dp = Doli => Presta
- */
-protected function assoc_exec($order='dp', $r)
-{
-	static::__log(__METHOD__);
-	
-	if(is_string($r))
-		$r = explode('-', $r);
-	var_dump($r);
-	
-	$id = $r[0];
-	$d_product_id = $r[1];
-	$p_product_id = $r[2];
-	$p_product_attribute_id = $r[3];
-	$ref = $r[4];
-	
-}
-
 }
 
 synchro_customer::__init();

+ 18 - 0
src/controller/synchro_order_detail.php

@@ -0,0 +1,18 @@
+<?php
+
+namespace Controller;
+
+require_once CLASS_PATH.'/controller/synchro.php';
+
+class synchro_order_detail extends Synchro
+{
+
+protected $model_name = 'order_detail';
+protected $viewmodel_name = 'sync/sync';
+
+protected $params = [];
+
+}
+
+synchro_order_detail::__init();
+

+ 18 - 0
src/controller/synchro_stock.php

@@ -0,0 +1,18 @@
+<?php
+
+namespace Controller;
+
+require_once CLASS_PATH.'/controller/synchro.php';
+
+class synchro_stock extends Synchro
+{
+
+protected $model_name = 'stock';
+protected $viewmodel_name = 'sync/sync';
+
+protected $params = [];
+
+}
+
+synchro_stock::__init();
+

+ 17 - 0
src/controller/synchro_supplier_price.php

@@ -0,0 +1,17 @@
+<?php
+
+namespace Controller;
+
+require_once CLASS_PATH.'/controller/synchro.php';
+
+class synchro_supplier_price extends Synchro
+{
+
+protected $model_name = 'supplier_price';
+protected $viewmodel_name = 'sync/sync';
+
+protected $params = [];
+
+}
+
+synchro_supplier_price::__init();

+ 31 - 0
src/install/address.inc.php

@@ -0,0 +1,31 @@
+<?php
+
+echo '<h3>Synchronisation client</h3>';
+
+$sql = 'SELECT DISTINCT c.id_customer
+	FROM ps_customer c';
+	
+//$sql .= ' WHERE c.id_customer=49';
+//$sql .= ' ORDER BY RAND()';
+
+$q = DB::p_select($sql);
+while ($row=$q->fetch_assoc()) {
+	var_dump($row);
+	if (isset($_GET['go']))
+		sync::_action('customer', 'pd', 'osync', 'customer', $row['id_customer']);
+	//break;
+}
+
+echo '<h3>Synchronisation adresse client</h3>';
+
+$sql = 'SELECT DISTINCT a.id_address, a.id_customer
+	FROM ps_address a
+	INNER JOIN ps_customer c ON c.id_customer=a.id_customer';
+
+$q = DB::p_select($sql);
+while ($row=$q->fetch_assoc()) {
+	var_dump($row);
+	if (isset($_GET['go']))
+		sync::_action('address', 'pd', 'osync', 'address', $row['id_address']);
+	//break;
+}

+ 14 - 0
src/install/customer.inc.php

@@ -0,0 +1,14 @@
+<?php
+
+echo '<h3>Synchro Customers</h3>';
+
+$sql = 'SELECT c.id_customer
+	FROM ps_customer c';
+
+$q = DB::p_select($sql);
+while ($row=$q->fetch_assoc()) {
+	var_dump($row);
+	if (isset($_GET['go']))
+		sync::_action('customer', 'pd', 'osync', 'customer', $row['id_customer']);
+	//break;
+}

+ 42 - 0
src/install/img.inc.php

@@ -0,0 +1,42 @@
+<?php
+
+// Image
+$sql = '(SELECT p.id_product, pa.id_product_attribute, p.reference, i.id_image
+	FROM ps_image i
+	INNER JOIN ps_product p ON p.id_product=i.id_product
+	LEFT JOIN ps_product_attribute pa ON pa.id_product=p.id_product
+	WHERE i.cover=1 AND pa.id_product IS NULL)
+	UNION
+	(SELECT p.id_product, pa.id_product_attribute, pa.reference,  i.id_image
+	FROM ps_image i
+	INNER JOIN ps_product p ON p.id_product=i.id_product
+	INNER JOIN ps_product_attribute pa ON pa.id_product=p.id_product
+	WHERE i.cover=1)';
+$q = DB::p_select($sql);
+
+foreach($q as $row) {
+	$p_id = $row['id_product'];
+	$p_ref = $row['reference'];
+	$i_id = $row['id_image'];
+	$p_filename = $i_id.'.jpg';
+	$p_folder = P_FOLDER_ROOT.'/img/p';
+	$n = strlen($i_id);
+	for($i=0; $i<$n; $i++)
+		$p_folder .= '/'.substr($i_id, $i, 1);
+	var_dump($p_f=$p_folder.'/'.$p_filename);
+	if (file_exists($p_f=$p_folder.'/'.$p_filename)) {
+		//var_dump($p_f);
+		$ref = $p_ref;
+		$d_filename = $ref.'-'.$i_id.'.jpg';
+		$d_folder = D_FOLDER_ROOT.'/documents/produit/'.$ref;
+		if (!file_exists($d_folder))
+			mkdir($d_folder);
+		$d_f = $d_folder.'/'.$d_filename;
+		//var_dump($d_f);
+		$cmd = "rsync -a \"$p_f\" \"$d_f\""; // --stats
+		var_dump($cmd);
+		exec($cmd, $output);
+		var_dump($output);
+		//break;
+	}
+}

+ 62 - 0
src/install/order.inc.php

@@ -0,0 +1,62 @@
+<?php
+
+echo '<h3>Commandes basculées/envoyées 2021 et avant => statut livré/expédié</h3>';
+
+// Initialisation de la classe
+sync::_class('order', 'pd');
+$_p_order_state_map = sync_order::_p_order_state_map();
+
+$sql = 'SELECT DISTINCT o.id_order, MAX(h.id_order_state) id_order_state, o.current_state
+	FROM ps_orders o
+	INNER JOIN ps_order_history h ON h.id_order=o.id_order
+	WHERE YEAR(h.date_add) <= "2021"
+	GROUP BY o.id_order';
+
+$q = DB::p_select($sql);
+if (isset($_GET['clean'])) while ($row=$q->fetch_assoc()) {
+	var_dump($row);
+	
+	// Statut finalisé avant 2021
+	if ($row['current_state'] != $row['id_order_state'])
+		continue;
+	
+	$o = sync::p_o_oid('order', 'orders', $row['id_order']);
+	if (empty($o))
+		continue;
+	var_dump($o);
+
+	if (isset($_p_order_state_map[$row['id_order_state']])) {
+		$id_order_state = $_p_order_state_map[$row['id_order_state']]['statut'];
+	}
+	else {
+		$id_order_state = NULL;
+	}
+	
+	
+	// GO
+	if ($id_order_state) {
+		$sql = 'UPDATE llx_commande c
+			SET fk_statut='.$id_order_state.'
+			WHERE rowid='.$o['d_oid'];
+		if (isset($_GET['go'])) 
+			DB::d_update($sql);
+		else
+			echo '<p>'.$sql.'</p>';
+	}
+}
+
+if(empty($_GET['n']) || !is_numeric($n=$_GET['n']) || (int)$n != $n || $n<0)
+	$n = 0;
+
+echo '<h3>Synchronisation commande</h3>';
+
+$sql = 'SELECT o.id_order
+	FROM ps_orders o
+	LIMIT '.$n.', 100';
+
+$q = DB::p_select($sql);
+if (isset($_GET['sync'])) while ($row=$q->fetch_assoc()) {
+	var_dump($row);
+	if (isset($_GET['go']))
+		sync::_action('order', 'pd', 'osync', 'orders', $row['id_order']);
+}

+ 26 - 0
src/install/product.inc.php

@@ -0,0 +1,26 @@
+<?php
+
+$sql = 'SELECT p.id_product
+	FROM ps_product p
+	LEFT JOIN ps_product_attribute pa ON pa.id_product=p.id_product
+	WHERE pa.id_product IS NULL';
+
+$q = DB::p_select($sql);
+if (true) while ($row=$q->fetch_assoc()) {
+	var_dump($row);
+	if (isset($_GET['go']))
+		sync::_action('product', 'pd', 'osync', 'product', $row['id_product']);
+	//break;
+}
+
+$sql = 'SELECT pa.id_product_attribute
+	FROM ps_product p
+	INNER JOIN ps_product_attribute pa ON pa.id_product=p.id_product';
+
+$q = DB::p_select($sql);
+if (true) while ($row=$q->fetch_assoc()) {
+	var_dump($row);
+	if (isset($_GET['go']))
+		sync::_action('product', 'pd', 'osync', 'product_attribute', $row['id_product_attribute']);
+	//break;
+}

+ 60 - 0
src/install/product_lot.inc.php

@@ -0,0 +1,60 @@
+<?php
+
+// Lots P=>D
+
+$sql = 'SELECT pl.*
+	FROM ps_products_dlc_dluo pl
+	INNER JOIN ps_product p ON p.id_product=pl.id_product
+	LEFT JOIN ps_product_attribute pa ON pa.id_product=pl.id_product
+	WHERE pa.id_product_attribute IS NULL';
+
+$q = DB::p_select($sql);
+if (false) while ($row=$q->fetch_assoc()) {
+	var_dump($row);
+	if (isset($_GET['go']))
+		sync::_action('product_lot', 'pd', 'osync', 'products_dlc_dluo', $row['id']);
+	//break;
+}
+
+$sql = 'SELECT pl.*
+	FROM ps_products_dlc_dluo pl
+	INNER JOIN ps_product p ON p.id_product=pl.id_product
+	INNER JOIN ps_product_attribute pa ON pa.id_product=pl.id_product AND pa.id_product_attribute=pl.id_combinaison
+	WHERE 1';
+
+$q = DB::p_select($sql);
+if (false) while ($row=$q->fetch_assoc()) {
+	var_dump($row);
+	if (isset($_GET['go']))
+		sync::_action('product_lot', 'pd', 'osync', 'products_dlc_dluo', $row['id']);
+	//break;
+}
+
+// Stock
+
+$sql = 'SELECT s.*
+	FROM ps_stock_available s
+	INNER JOIN ps_product p ON p.id_product=s.id_product
+	LEFT JOIN ps_product_attribute pa ON pa.id_product=s.id_product
+	WHERE pa.id_product_attribute IS NULL';
+
+$q = DB::p_select($sql);
+while ($row=$q->fetch_assoc()) {
+	var_dump($row);
+	if (isset($_GET['go']))
+		sync::_action('stock', 'pd', 'osync', 'stock_available', $row['id_stock_available']);
+}
+
+$sql = 'SELECT s.*
+	FROM ps_stock_available s
+	INNER JOIN ps_product p ON p.id_product=s.id_product
+	INNER JOIN ps_product_attribute pa ON pa.id_product=s.id_product AND pa.id_product_attribute=s.id_product_attribute
+	WHERE 1';
+
+$q = DB::p_select($sql);
+while ($row=$q->fetch_assoc()) {
+	var_dump($row);
+	if (isset($_GET['go']))
+		sync::_action('stock', 'pd', 'osync', 'stock_available', $row['id_stock_available']);
+	//break;
+}

+ 12 - 0
src/install/supplier.inc.php

@@ -0,0 +1,12 @@
+<?php
+
+// Fournisseur
+$sql = 'SELECT s.*
+	FROM ps_supplier s';
+$q = DB::p_select($sql);
+while ($row=$q->fetch_assoc()) {
+	var_dump($row);
+	if (isset($_GET['go']))
+		sync::_action('supplier', 'pd', 'osync', 'supplier', $row['id_supplier']);
+	break;
+}

+ 30 - 0
src/install/supplier_price.inc.php

@@ -0,0 +1,30 @@
+<?php
+
+// Produits non déclinés
+$sql = 'SELECT sp.*
+	FROM `ps_product_supplier` sp
+	INNER JOIN `ps_product` p ON p.id_product=sp.id_product AND sp.id_product_attribute=0
+	LEFT JOIN `ps_product_attribute` pa ON pa.id_product=sp.id_product
+	WHERE pa.id_product_attribute IS NULL';
+
+$q = DB::p_select($sql);
+while ($row=$q->fetch_assoc()) {
+	var_dump($row);
+	if (isset($_GET['go']))
+		sync::_action('supplier_price', 'pd', 'osync', 'product_supplier', $row['id_product_supplier']);
+}
+
+
+// Produits déclinés avec prix fournisseur non saisi
+$sql = 'SELECT sp.*
+	FROM `ps_product_supplier` sp
+	INNER JOIN `ps_product` p ON p.id_product=sp.id_product
+	INNER JOIN `ps_product_attribute` pa ON pa.id_product=sp.id_product AND pa.id_product_attribute=sp.id_product_attribute
+	WHERE 1';
+
+$q = DB::p_select($sql);
+while ($row=$q->fetch_assoc()) {
+	var_dump($row);
+	if (isset($_GET['go']))
+		sync::_action('supplier_price', 'pd', 'osync', 'product_supplier', $row['id_product_supplier']);
+}

+ 90 - 0
src/model/address.php

@@ -0,0 +1,90 @@
+<?php
+
+namespace Model;
+
+require_once CLASS_PATH.'/model/base.php';
+
+class address extends base
+{
+
+protected static $_cache = [];
+
+protected static $_id;
+protected static $_name;
+protected static $_label;
+
+protected static $_pt = [];
+protected static $_pt_alias = [];
+protected static $_dt = [];
+protected static $_dt_alias = [];
+
+protected static $_maps = [];
+
+protected static $_map_values = [];
+protected static $_map_fct = [
+	'd' => [
+		'fk_societe' => '',
+	],
+	'p' => [
+		'fk_customer' => '',
+		'fk_supplier' => '',
+	],
+];
+
+public static function _d_map_fk_societe($orig_data, $dest_tree=[])
+{
+	$orig = 'p';
+	$dest = 'd';
+	if (!empty($orig_data['address']['id_supplier'])) {
+		$oid = $orig_data['address']['id_supplier'];
+		//
+		$modelname = 'supplier';
+		$tablename = 'supplier';
+	}
+	elseif (!empty($orig_data['address']['id_customer'])) {
+		$oid = $orig_data['address']['id_customer'];
+		//
+		$modelname = 'customer';
+		$tablename = 'customer';
+	}
+	
+	if (DEBUG_SYNCHRO) {
+		var_dump($orig_data);
+		var_dump($dest_tree);
+	}
+	
+	$o = static::_map_fk($orig, $modelname, $tablename, $oid);
+	return $o[$dest]['oid'];
+}
+
+public static function _p_map_fk_supplier($orig_data, $dest_tree=[])
+{
+	$orig = 'd';
+	$dest = 'p';
+	//var_dump($orig_data); var_dump($dest_tree);
+	$oid = $orig_data['product_fournisseur_price']['fk_soc'];
+	//
+	$modelname = 'supplier';
+	$tablename = 'societe';
+	
+	$o = static::_map_fk($orig, $modelname, $tablename, $oid);
+	return $o[$dest]['oid'];
+}
+
+public static function _p_map_fk_customer($orig_data, $dest_tree=[])
+{
+	$orig = 'd';
+	$dest = 'p';
+	//var_dump($orig_data); var_dump($dest_tree);
+	$oid = $orig_data['product_fournisseur_price']['fk_soc'];
+	//
+	$modelname = 'customer';
+	$tablename = 'societe';
+	
+	$o = static::_map_fk($orig, $modelname, $tablename, $oid);
+	return $o[$dest]['oid'];
+}
+
+}
+
+address::__init();

+ 1 - 1
src/model/category.php

@@ -10,7 +10,7 @@ class category extends base
 protected static $_cache = [];
 
 protected static $_id;
-protected static $_name = 'supplier';
+protected static $_name;
 protected static $_label;
 
 protected static $_pt = [];

+ 1 - 1
src/model/customer.php

@@ -10,7 +10,7 @@ class customer extends base
 protected static $_cache = [];
 
 protected static $_id;
-protected static $_name = 'customer';
+protected static $_name;
 protected static $_label;
 
 protected static $_pt = [];

+ 33 - 21
src/model/order.php

@@ -10,7 +10,7 @@ class order extends base
 protected static $_cache = [];
 
 protected static $_id;
-protected static $_name = 'order';
+protected static $_name;
 protected static $_label;
 
 protected static $_pt = [];
@@ -29,46 +29,58 @@ protected static $_map_fct = [
 	],
 	'p' => [
 		//'id_tax_rules_group' => '',
+		'fk_product'=>'',
 	],
 ];
 
-public static function _d_map_fk_product($p_data, $d_tree=[])
+public static function _d_map_fk_product($orig_data, $dest_tree=[])
 {
 	$orig = 'p';
-	//var_dump($p_data); var_dump($d_tree);
-	$id_product = $p_data['order_detail']['product_id'];
-	$id_product_attribute = $p_data['order_detail']['product_attribute_id'];
-	
+	$dest = 'd';
 	$modelname = 'product';
+	//var_dump($orig_data); var_dump($dest_tree);
 	
-	if (empty($id_product_attribute)) {
-		$map_id = 1;
-		$tablename = 'product';
-		$oid = $id_product;
+	if (!empty($orig_data['order_detail']['product_attribute_id'])) {
+		$tablename = 'product_attribute';
+		$oid = $orig_data['order_detail']['product_attribute_id'];
 	}
 	else {
-		$map_id = 2;
-		$tablename = 'product_attribute';
-		$oid = $id_product_attribute;
+		$tablename = 'product';
+		$oid = $orig_data['order_detail']['product_id'];
 	}
 	
-	return static::_map_fk($orig, $modelname, $map_id, $tablename, $oid);
+	$o = static::_map_fk($orig, $modelname, $tablename, $oid);
+	return $o[$dest]['oid'];
+}
+
+public static function _p_map_fk_product($orig_data, $dest_tree=[])
+{
+	$orig = 'd';
+	$dest = 'p';
+	$modelname = 'product';
+	//var_dump($orig_data); var_dump($dest_tree);
+	
+	$tablename = 'product';
+	$oid = $orig_data['commandedet']['fk_product'];
+	
+	$o = static::_map_fk($orig, $modelname, $tablename, $oid);
+	return $o[$dest]['oid'];
 }
 
-public static function _d_map_fk_customer($p_data, $d_tree=[])
+public static function _d_map_fk_customer($orig_data, $dest_tree=[])
 {
 	$orig = 'p';
-	//var_dump($p_data); var_dump($d_tree);
-	$oid = $p_data['orders']['id_customer'];
+	$dest = 'd';
+	$modelname = 'product';
+	//var_dump($orig_data); var_dump($dest_tree);
+	$oid = $orig_data['orders']['id_customer'];
 	//
-	$modelname = 'customer';
-	$map_id = 4;
 	$tablename = 'customer';
 	
-	return static::_map_fk($orig, $modelname, $map_id, $tablename, $oid);
+	$o = static::_map_fk($orig, $modelname, $tablename, $oid);
+	return $o[$dest]['oid'];
 }
 
-
 }
 
 order::__init();

+ 96 - 0
src/model/order_detail.php

@@ -0,0 +1,96 @@
+<?php
+
+namespace Model;
+
+require_once CLASS_PATH.'/model/base.php';
+
+class order_detail extends base
+{
+
+protected static $_cache = [];
+
+protected static $_id;
+protected static $_name;
+protected static $_label;
+
+protected static $_pt = [];
+protected static $_pt_alias = [];
+protected static $_dt = [];
+protected static $_dt_alias = [];
+
+protected static $_maps = [];
+
+protected static $_map_values = [];
+protected static $_map_fct = [
+	'd' => [
+		//'tva_tx' => '',
+		'fk_product'=>'',
+	],
+	'p' => [
+		//'id_tax_rules_group' => '',
+	],
+];
+
+public static function _d_map_fk_product($orig_data, $dest_tree=[])
+{
+	$orig = 'p';
+	$dest = 'd';
+	$modelname = 'product';
+	//var_dump($orig_data); var_dump($dest_tree);
+	
+	if (!empty($orig_data['order_detail']['product_attribute_id'])) {
+		$tablename = 'product_attribute';
+		$oid = $orig_data['order_detail']['product_attribute_id'];
+	}
+	else {
+		$tablename = 'product';
+		$oid = $orig_data['order_detail']['product_id'];
+	}
+	
+	$o = static::_map_fk($orig, $modelname, $tablename, $oid);
+	return $o[$dest]['oid'];
+}
+
+public static function _p_map_fk_product($orig_data, $dest_tree=[])
+{
+	$orig = 'd';
+	$dest = 'p';
+	$modelname = 'product';
+	//var_dump($orig_data); var_dump($dest_tree);
+	
+	$tablename = 'product';
+	$oid = $orig_data['commandedet']['fk_product'];
+	
+	$o = static::_map_fk($orig, $modelname, $tablename, $oid);
+	if ($o['map_id'] == 2) {
+		$row = static::_ws_odata($dest, 'product_attribute', $oid)
+		return $row['id_product'];
+	}
+	else {
+		return $o[$dest]['oid'];
+	}
+}
+
+public static function _p_map_fk_product_attribute($orig_data, $dest_tree=[])
+{
+	$orig = 'd';
+	$dest = 'p';
+	$modelname = 'product';
+	//var_dump($orig_data); var_dump($dest_tree);
+	
+	$tablename = 'product';
+	$oid = $orig_data['commandedet']['fk_product'];
+	
+	$o = static::_map_fk($orig, $modelname, $tablename, $oid);
+	if ($o['map_id'] == 2) {
+		return $o[$dest]['oid'];
+	}
+	else {
+		return 0;
+	}
+}
+
+}
+
+order_detail::__init();
+

+ 46 - 46
src/model/product.php

@@ -10,7 +10,7 @@ class product extends base
 protected static $_cache = [];
 
 protected static $_id;
-protected static $_name = 'product';
+protected static $_name;
 protected static $_label;
 
 protected static $_pt = [];
@@ -36,70 +36,66 @@ protected static $_map_fct = [
 		'isbn' => '',
 		'upc' => '',
 		'fk_supplier' => '',
+		'link_rewrite' => '',
 	],
 ];
 
-protected static $_p_product_tname = '';
 protected static $_p_product_tid = 1;
-protected static $_p_product_attribute_tname = '';
 protected static $_p_product_attribute_tid = 5;
-protected static $_d_product_tname = '';
 protected static $_d_product_tid = 1;
 
-public static function _d_map_fk_supplier($p_data, $d_tree=[])
+public static function _d_map_fk_supplier($orig_data, $dest_tree=[])
 {
 	$orig = 'p';
-	//var_dump($p_data); var_dump($d_tree);
-	$oid = $p_data['product_supplier']['id_supplier'];
+	//var_dump($orig_data); var_dump($dest_tree);
+	$oid = $orig_data['product_supplier']['id_supplier'];
 	//
 	$modelname = 'supplier';
-	$map_id = 3;
 	$tablename = 'supplier';
 	
-	return static::_map_fk($orig, $modelname, $map_id, $tablename, $oid);
+	return static::_map_fk($orig, $modelname, $tablename, $oid);
 }
-public static function _p_map_fk_supplier($d_data, $d_tree=[])
+public static function _p_map_fk_supplier($orig_data, $dest_tree=[])
 {
 	$orig = 'd';
-	//var_dump($p_data); var_dump($d_tree);
-	$oid = $d_data['product_fournisseur_price']['fk_soc'];
+	//var_dump($orig_data); var_dump($dest_tree);
+	$oid = $orig_data['product_fournisseur_price']['fk_soc'];
 	//
 	$modelname = 'supplier';
-	$map_id = 3;
 	$tablename = 'societe';
 	
-	return static::_map_fk($orig, $modelname, $map_id, $tablename, $oid);
+	return static::_map_fk($orig, $modelname, $tablename, $oid);
 }
 
-public static function _d_map_product_url($p_data, $d_tree=[])
+public static function _d_map_product_url($orig_data, $dest_tree=[])
 {
-	return static::$__p_params['url'].'/'.static::$__p_params['lang'].'/'.$p_data['product']['id_product'].'-'.$p_data['product_lang']['link_rewrite'];
+	return static::$__p_params['url'].'/'.static::$__p_params['lang'].'/'.$orig_data['product']['id_product'].'-'.$orig_data['product_lang']['link_rewrite'];
 }
-public static function _d_map_barcode($p_data, $d_tree=[])
+public static function _d_map_barcode($orig_data, $dest_tree=[])
 {
-	if (!empty($p_data['product']['ean13']))
-		return $p_data['product']['ean13'];
-	elseif (!empty($p_data['product']['upc']))
-		return $p_data['product']['upc'];
-	elseif (!empty($p_data['product']['isbn']))
-		return $p_data['product']['isbn'];
+	if (!empty($orig_data['product']['ean13']))
+		return $orig_data['product']['ean13'];
+	elseif (!empty($orig_data['product']['upc']))
+		return $orig_data['product']['upc'];
+	elseif (!empty($orig_data['product']['isbn']))
+		return $orig_data['product']['isbn'];
 }
-public static function _d_map_barcode_type($p_data, $d_tree=[])
+public static function _d_map_barcode_type($orig_data, $dest_tree=[])
 {
-	if (!empty($p_data['product']['ean13']))
+	if (!empty($orig_data['product']['ean13']))
 		return'2';
-	elseif (!empty($p_data['product']['upc']))
+	elseif (!empty($orig_data['product']['upc']))
 		return '3';
-	elseif (!empty($p_data['product']['isbn']))
+	elseif (!empty($orig_data['product']['isbn']))
 		return '4';
 }
-public static function _d_map_is_batch($p_data, $d_tree=[])
+public static function _d_map_is_batch($orig_data, $dest_tree=[])
 {
 }
-public static function _d_map_tva_tx($p_data, $d_tree=[])
+public static function _d_map_tva_tx($orig_data, $dest_tree=[])
 {
 	// @todo mapper les taxes proprement !!
-	switch($p_data['product']['id_tax_rules_group']) {
+	switch($orig_data['product']['id_tax_rules_group']) {
 		case '1':
 			return 20;
 		case '2':
@@ -112,10 +108,10 @@ public static function _d_map_tva_tx($p_data, $d_tree=[])
 			return 0;
 	}
 }
-public static function _p_map_id_tax_rules_group($d_data, $p_tree=[])
+public static function _p_map_id_tax_rules_group($orig_data, $dest_tree=[])
 {
 	// @todo mapper les taxes proprement !!
-	switch($d_data['product']['tva_tx']) {
+	switch($orig_data['product']['tva_tx']) {
 		case '20':
 			return 1;
 		case '10':
@@ -128,17 +124,21 @@ public static function _p_map_id_tax_rules_group($d_data, $p_tree=[])
 			return 5;
 	}
 }
-public static function _p_map_ean($d_data, $p_tree=[])
+public static function _p_map_ean($orig_data, $dest_tree=[])
 {
-	return ($d_data['product']['fk_barcode_type']==2) ?$d_data['product']['barcode'] :NULL;
+	return ($orig_data['product']['fk_barcode_type']==2) ?$orig_data['product']['barcode'] :NULL;
 }
-public static function _p_map_isbn($d_data, $p_tree=[])
+public static function _p_map_isbn($orig_data, $dest_tree=[])
 {
-	return ($d_data['product']['fk_barcode_type']==4) ?$d_data['product']['barcode'] :NULL;
+	return ($orig_data['product']['fk_barcode_type']==4) ?$orig_data['product']['barcode'] :NULL;
 }
-public static function _p_map_upc($d_data, $p_tree=[])
+public static function _p_map_upc($orig_data, $dest_tree=[])
 {
-	return ($d_data['product']['fk_barcode_type']==3) ?$d_data['product']['barcode'] :NULL;
+	return ($orig_data['product']['fk_barcode_type']==3) ?$orig_data['product']['barcode'] :NULL;
+}
+public static function _p_map_link_rewrite($orig_data, $dest_tree=[])
+{
+	return static::_slugify($orig_data['product']['label']);
 }
 
 /**
@@ -187,10 +187,10 @@ public static function _p_list($params=null)
  */
 public static function _p_sql_1($params=null)
 {
-	$_sql_p = 'FROM '.PS_DB_PREFIX.'product AS p
-		LEFT JOIN '.PS_DB_PREFIX.'product_attribute AS pa
+	$_sql_p = 'FROM '.DB_P_PREFIX.'product AS p
+		LEFT JOIN '.DB_P_PREFIX.'product_attribute AS pa
 			ON pa.id_product=p.id_product
-		LEFT JOIN '.PS_DB_PREFIX.'product_lang AS pl
+		LEFT JOIN '.DB_P_PREFIX.'product_lang AS pl
 			ON pl.id_product=p.id_product AND pl.id_lang='.static::$__p_params['id_lang'];
 
 	$sql = 'SELECT
@@ -221,10 +221,10 @@ public static function _p_sql_1($params=null)
  */
 public static function _p_sql_2($params=null)
 {
-	$_sql_p = 'FROM '.PS_DB_PREFIX.'product AS p
-		LEFT JOIN '.PS_DB_PREFIX.'product_attribute AS pa
+	$_sql_p = 'FROM '.DB_P_PREFIX.'product AS p
+		LEFT JOIN '.DB_P_PREFIX.'product_attribute AS pa
 			ON pa.id_product=p.id_product
-		LEFT JOIN '.PS_DB_PREFIX.'product_lang AS pl
+		LEFT JOIN '.DB_P_PREFIX.'product_lang AS pl
 			ON pl.id_product=p.id_product AND pl.id_lang='.static::$__p_params['id_lang'];
 
 	$sql = 'SELECT
@@ -252,7 +252,7 @@ public static function _p_sql_2($params=null)
 
 public static function _d_sql($params=null)
 {
-	$_sql_d = 'FROM '.DOLI_DB_PREFIX.'product AS p';
+	$_sql_d = 'FROM '.DB_D_PREFIX.'product AS p';
 	
 	$sql = 'SELECT
 			p.rowid,
@@ -307,7 +307,7 @@ public static function _map_pd(&$o)
 		$tree[$tablename]['row'] = static::_map_table_row($sens, $table, $map, $o['d_tree_web'], $o['p_tree_web']);
 	}
 	
-	var_dump($tree); die();
+	//var_dump($tree); die();
 }
 
 }

+ 1 - 1
src/model/product_lot.php

@@ -10,7 +10,7 @@ class product_lot extends base
 protected static $_cache = [];
 
 protected static $_id;
-protected static $_name = 'product_lot';
+protected static $_name;
 protected static $_label;
 
 protected static $_pt = [];

+ 95 - 0
src/model/stock.php

@@ -0,0 +1,95 @@
+<?php
+
+namespace Model;
+
+require_once CLASS_PATH.'/model/base.php';
+
+class stock extends base
+{
+
+protected static $_cache = [];
+
+protected static $_id;
+protected static $_name;
+protected static $_label;
+
+protected static $_pt = [];
+protected static $_pt_alias = [];
+protected static $_dt = [];
+protected static $_dt_alias = [];
+
+protected static $_maps = [];
+
+protected static $_map_values = [];
+protected static $_map_fct = [
+	'd' => [
+		'fk_product'=>'',
+	],
+	'p' => [
+		'fk_product'=>'',
+		'fk_product_attribute'=>'',
+	],
+];
+
+public static function _d_map_fk_product($orig_data, $dest_tree=[])
+{
+	$orig = 'p';
+	$dest = 'd';
+	$modelname = 'product';
+	//var_dump($orig_data); var_dump($dest_tree);
+	
+	if (!empty($orig_data['stock_available']['product_attribute_id'])) {
+		$tablename = 'product_attribute';
+		$oid = $orig_data['stock_available']['product_attribute_id'];
+	}
+	else {
+		$tablename = 'product';
+		$oid = $orig_data['stock_available']['product_id'];
+	}
+	
+	$o = static::_map_fk($orig, $modelname, $tablename, $oid);
+	return $o[$dest]['oid'];
+}
+
+public static function _p_map_fk_product($orig_data, $dest_tree=[])
+{
+	$orig = 'd';
+	$dest = 'p';
+	$modelname = 'product';
+	//var_dump($orig_data); var_dump($dest_tree);
+	
+	$tablename = 'product';
+	$oid = $orig_data['product_stock']['fk_product'];
+	
+	$o = static::_map_fk($orig, $modelname, $tablename, $oid);
+	if ($o['map_id'] == 2) {
+		$row = static::_ws_odata($dest, 'product_attribute', $oid);
+		return $row['id_product'];
+	}
+	else {
+		return $o[$dest]['oid'];
+	}
+}
+
+public static function _p_map_fk_product_attribute($orig_data, $dest_tree=[])
+{
+	$orig = 'd';
+	$dest = 'p';
+	$modelname = 'product';
+	//var_dump($orig_data); var_dump($dest_tree);
+	
+	$tablename = 'product';
+	$oid = $orig_data['product_stock']['fk_product'];
+	
+	$o = static::_map_fk($orig, $modelname, $tablename, $oid);
+	if ($o['map_id'] == 2) {
+		return $o[$dest]['oid'];
+	}
+	else {
+		return 0;
+	}
+}
+
+}
+
+stock::__init();

+ 1 - 1
src/model/supplier.php

@@ -10,7 +10,7 @@ class supplier extends base
 protected static $_cache = [];
 
 protected static $_id;
-protected static $_name = 'supplier';
+protected static $_name;
 protected static $_label;
 
 protected static $_pt = [];

+ 123 - 0
src/model/supplier_price.php

@@ -0,0 +1,123 @@
+<?php
+
+namespace Model;
+
+require_once CLASS_PATH.'/model/base.php';
+
+class supplier_price extends base
+{
+
+protected static $_cache = [];
+
+protected static $_id;
+protected static $_name;
+protected static $_label;
+
+protected static $_pt = [];
+protected static $_pt_alias = [];
+protected static $_dt = [];
+protected static $_dt_alias = [];
+
+protected static $_maps = [];
+
+protected static $_map_values = [];
+protected static $_map_fct = [
+	'd' => [
+		'fk_supplier' => '',
+		'fk_product' => '',
+	],
+	'p' => [
+		'fk_supplier' => '',
+		'fk_product' => '',
+	],
+];
+
+public static function _d_map_fk_supplier($orig_data, $dest_tree=[])
+{
+	$orig = 'p';
+	$dest = 'd';
+	$modelname = 'supplier';
+	//var_dump($orig_data); var_dump($dest_tree);
+	$oid = $orig_data['product_supplier']['id_supplier'];
+	//
+	$tablename = 'supplier';
+	
+	$o = static::_map_fk($orig, $modelname, $tablename, $oid);
+	return $o[$dest]['oid'];
+}
+public static function _p_map_fk_supplier($orig_data, $dest_tree=[])
+{
+	$orig = 'd';
+	$dest = 'p';
+	$modelname = 'customer';
+	//var_dump($orig_data); var_dump($dest_tree);
+	$oid = $orig_data['product_fournisseur_price']['fk_soc'];
+	//
+	$tablename = 'societe';
+	
+	$o = static::_map_fk($orig, $modelname, $tablename, $oid);
+	return $o[$dest]['oid'];
+
+}
+
+public static function _d_map_fk_product($orig_data, $dest_tree=[])
+{
+	$orig = 'p';
+	$dest = 'd';
+	$modelname = 'product';
+	//var_dump($orig_data); var_dump($dest_tree);
+	
+	if (!empty($orig_data['product_supplier']['product_attribute_id'])) {
+		$tablename = 'product_attribute';
+		$oid = $orig_data['product_supplier']['product_attribute_id'];
+	}
+	else {
+		$tablename = 'product';
+		$oid = $orig_data['product_supplier']['product_id'];
+	}
+	
+	$o = static::_map_fk($orig, $modelname, $tablename, $oid);
+	return $o[$dest]['oid'];
+}
+
+public static function _p_map_fk_product($orig_data, $dest_tree=[])
+{
+	$orig = 'd';
+	$dest = 'p';
+	$modelname = 'product';
+	//var_dump($orig_data); var_dump($dest_tree);
+	
+	$tablename = 'product';
+	$oid = $orig_data['product_fournisseur_price']['fk_product'];
+	
+	$o = static::_map_fk($orig, $modelname, $tablename, $oid);
+	if ($o['map_id'] == 2) {
+		$row = static::_ws_odata($dest, 'product_attribute', $oid);
+		return $row['id_product'];
+	}
+	else {
+		return $o[$dest]['oid'];
+	}
+}
+public static function _p_map_fk_product_attribute($orig_data, $dest_tree=[])
+{
+	$orig = 'd';
+	$dest = 'p';
+	$modelname = 'product';
+	//var_dump($orig_data); var_dump($dest_tree);
+	
+	$tablename = 'product';
+	$oid = $orig_data['product_fournisseur_price']['fk_product'];
+	
+	$o = static::_map_fk($orig, $modelname, $tablename, $oid);
+	if ($o['map_id'] == 2) {
+		return $o[$dest]['oid'];
+	}
+	else {
+		return 0;
+	}
+}
+
+}
+
+supplier_price::__init();

+ 38 - 0
src/repair/customer.inc.php

@@ -0,0 +1,38 @@
+<?php
+
+echo '<h3>Création code client</h3>';
+
+$nb = sync::_class('customer', 'pd')::d_ref_nb();
+
+$sql = 'SELECT DISTINCT c.rowid, c.datec
+	FROM llx_societe c
+	INNER JOIN llx_commande o ON o.fk_soc=c.rowid
+	WHERE (c.code_client IS NULL OR c.code_client NOT REGEXP "CU[0-9]{4}-[0-9]{6}")
+	ORDER BY c.datec, c.rowid';
+$q = DB::d_select($sql);
+if (true) while(list($id, $date)=$q->fetch_row()) {
+	var_dump($id); var_dump($date);
+	$nb++;
+	for ($i=strlen($nb); $i<6; $i++)
+		$nb = '0'.$nb;
+	$ref = 'CU'.substr($date, 2, 2).substr($date, 5, 2).'-'.$nb;
+	$sql = 'UPDATE llx_societe
+		SET code_client="'.$ref.'"
+		WHERE rowid='.$id;
+	if (isset($_GET['go']))
+		DB::d_update($sql);
+	else
+		echo '<p>'.$sql.'</p>';
+}
+
+die();
+
+$sql = 'UPDATE `llx_societe`
+	SET code_client = CONCAT(SUBSTRING(code_client, 1, 7), "0", SUBSTRING(code_client, 8, 5))
+	WHERE code_client IS NOT NULL AND length(code_client)=11';
+
+$sql = 'SELECT SUBSTRING(MAX(code_client), 8, 6)
+	FROM `llx_societe`';
+$q = DB::d_select($sql);
+list($nb) = $q->fetch_row();
+var_dump($nb);

+ 28 - 0
src/repair/order.inc.php

@@ -0,0 +1,28 @@
+<?php
+
+$nb = sync::_class('order', 'pd')::d_ref_nb();
+
+//$nb = 0;
+var_dump($nb);
+
+//die();
+
+$sql = 'SELECT rowid, date_commande
+	FROM llx_commande
+	WHERE ref NOT REGEXP "CO[0-9]{4}-[0-9]{6}"
+	ORDER BY date_commande, rowid';
+$q = DB::d_select($sql);
+if (true) while(list($id, $date)=$q->fetch_row()) {
+	var_dump($id); var_dump($date);
+	$nb++;
+	for ($i=strlen($nb); $i<6; $i++)
+		$nb = '0'.$nb;
+	$ref = 'CO'.substr($date, 2, 2).substr($date, 5, 2).'-'.$nb;
+	$sql = 'UPDATE llx_commande
+		SET ref="'.$ref.'"
+		WHERE rowid='.$id;
+	if (isset($_GET['go']))
+		DB::d_update($sql);
+	else
+		echo '<p>'.$sql.'</p>';
+}

+ 1 - 0
src/repair/product.inc.php

@@ -0,0 +1 @@
+<?php

+ 103 - 0
src/repair/product_lot.inc.php

@@ -0,0 +1,103 @@
+<?php
+
+// Prestashop
+
+// Stock 0 => Archivage
+$sql = 'UPDATE ps_products_dlc_dluo
+	SET is_current_stock=0
+	WHERE is_current_stock>0 AND stock=0';
+if (isset($_GET['go']))
+	$q = DB::p_update($sql);
+else
+	echo '<p>'.$sql.'</p>';
+
+// Pas de courant => on décrémente tout
+$sql = 'UPDATE ps_products_dlc_dluo l1
+	LEFT JOIN ps_products_dlc_dluo l2 ON l2.id_product=l1.id_product AND l2.id_combinaison=l1.id_combinaison AND l2.is_current_stock=1
+	SET l1.is_current_stock=l1.is_current_stock-1
+	WHERE l1.is_current_stock>1 AND l2.id IS NULL';
+if (isset($_GET['go']))
+	$q = DB::p_update($sql);
+else
+	echo '<p>'.$sql.'</p>';
+
+// Recalcul stock Doli Si lots
+
+$sql = 'SELECT p.rowid k_product, s.rowid k_product_stock, p.label, p.stock, s.reel, SUM(IF(sl.qty IS NOT NULL, sl.qty, 0)) qty, p.stock-SUM(IF(sl.qty IS NOT NULL, sl.qty, 0)) diff
+	FROM `llx_product` p
+	INNER JOIN llx_product_stock s ON s.fk_product=p.rowid
+	LEFT JOIN llx_product_batch sl ON sl.fk_product_stock=s.rowid
+	WHERE p.tobatch=1
+	GROUP BY p.rowid
+	HAVING p.stock != s.reel OR p.stock != SUM(IF(sl.qty IS NOT NULL, sl.qty, 0))  
+	ORDER BY `diff` DESC';
+$q = DB::d_select($sql);
+if (false) while($row=$q->fetch_assoc()) {
+	$sql = 'UPDATE llx_product
+		SET stock='.$row['qty'].'
+		WHERE rowid='.$row['k_product'];
+	if (isset($_GET['go']))
+		DB::d_update($sql);
+	else
+		echo '<p>'.$sql.'</p>';
+	$sql = 'UPDATE llx_product_stock
+		SET reel='.$row['qty'].'
+		WHERE rowid='.$row['k_product_stock'];
+	if (isset($_GET['go']))
+		DB::d_update($sql);
+	else
+		echo '<p>'.$sql.'</p>';
+}
+
+// Lots supprimés P=>D
+
+$sql = 'SELECT l.rowid, l.fk_product, l.batch, l.eatby, l.sellby, s.rowid s_rowid, sl.rowid sl_rowid
+	FROM llx_product_lot l
+	LEFT JOIN llx_product_stock s ON s.fk_product=l.fk_product AND s.fk_entrepot=1
+	LEFT JOIN llx_product_batch sl ON sl.fk_product_stock=s.rowid AND sl.batch=l.batch
+	WHERE s.rowid IS NULL OR sl.rowid IS NULL';
+$q = DB::d_select($sql);
+if (false) while ($row=$q->fetch_assoc()) {
+	var_dump($row);
+	echo '<br />';
+
+	$o = sync::d_o_oid('product_lot', 'product_lot', $row['rowid']);
+	var_dump($o);
+	echo '<br />';
+	if (!empty($o)) {
+		$sql = 'SELECT * FROM ps_products_dlc_dluo WHERE id='.$o['p_oid'];
+		$q2 = DB::p_select($sql);
+		while ($row2=$q2->fetch_assoc()) {
+			var_dump($row2);
+		}
+		$sql = 'INSERT INTO llx_product_batch
+			(fk_product_stock, eatby, sellby, batch, qty, import_key)
+			VALUES
+			("'.$row['s_rowid'].'", '.(!is_null($row['eatby']) ?'"'.$row['eatby'].'"' :'NULL').', '.(!is_null($row['sellby']) ?'"'.$row['sellby'].'"' :'NULL').', "'.$row['batch'].'", 0, NULL)';
+		if (isset($_GET['go']))
+			DB::d_insert($sql);
+		else
+			echo '<p>'.$sql.'</p>';
+	}
+	else {
+		echo '<p>ERREUR MANQUE CORRESPONDANCE LOT</p>';
+	}
+}
+
+// Lots mis à jour depuis $date, stock produit mis à jour depuis $date, qte lot stockée mise à jour depuis $date
+
+if (!empty($_GET['date']))
+	$date = $_GET['date'];
+else
+	$date = date('Y-m-d', time()-86400*7); // Il y a 7 jours par défaut
+$sql = 'SELECT l.rowid
+	FROM llx_product_lot l
+	LEFT JOIN llx_product_stock s ON s.fk_product=l.fk_product AND s.fk_entrepot=1
+	LEFT JOIN llx_product_batch sl ON sl.fk_product_stock=s.rowid AND sl.batch=l.batch
+	WHERE (l.tms >= "'.$date.' 00:00:00" OR sl.tms >= "'.$date.' 00:00:00")';
+$q = DB::d_select($sql);
+if (true) while ($row=$q->fetch_assoc()) {
+	var_dump($row);
+	if (isset($_GET['go']))
+		sync::_action('product_lot', 'dp', 'osync', 'product_lot', $row['rowid']);
+}

+ 14 - 0
src/repair/shipping.inc.php

@@ -0,0 +1,14 @@
+<?php
+
+$sql = 'UPDATE `llx_expedition`
+	SET ref = CONCAT(SUBSTRING(ref, 1, 7), "00", SUBSTRING(ref, 8, 4))
+	WHERE ref IS NOT NULL AND LENGTH(ref)=11';
+
+$sql = 'SELECT SUBSTRING(MAX(ref), 8, 6)
+	FROM `llx_expedition`
+	WHERE length(ref)=11';
+$q = DB::d_select($sql);
+list($nb) = $q->fetch_row();
+var_dump($nb);
+
+// --

+ 52 - 0
src/repair/stock.inc.php

@@ -0,0 +1,52 @@
+<?php
+
+// Stock supprimé P=>D
+
+$sql = 'SELECT p.rowid k_product
+	FROM llx_product p
+	LEFT JOIN llx_product_stock s ON s.fk_product=p.rowid AND s.fk_entrepot=1
+	WHERE s.rowid IS NULL';
+$q = DB::d_select($sql);
+if (true) while ($row=$q->fetch_assoc()) {
+	var_dump($row);
+	echo '<br />';
+
+	$o = sync::d_o_oid('product', 'product', $row['k_product']);
+	var_dump($o);
+	echo '<br />';
+	if (!empty($o)) {
+		if ($o['p_tref']=='product')
+			$sql = 'SELECT *
+			FROM ps_stock_available
+			WHERE id_product='.$o['p_oid'].' AND id_product_attribute=0';
+		elseif ($o['p_tref']=='product_attribute')
+			$sql = 'SELECT *
+			FROM ps_stock_available s
+			INNER JOIN ps_product_attribute pa ON pa.id_product_attribute=s.id_product_attribute AND pa.id_product=s.id_product
+			WHERE pa.id_product_attribute='.$o['p_oid'];
+		else
+			continue;
+
+		$q2 = DB::p_select($sql);
+		// Dans la pratique, un seul enregistrement
+		while ($row2=$q2->fetch_assoc()) {
+			var_dump($row2);
+			$o = sync::p_o_oid('stock', 'stock_available', $row2['id_stock_available']);
+			if (!empty($o)) {
+				$sql = 'INSERT INTO llx_product_stock
+					(rowid, tms, fk_product, fk_entrepot, reel, import_key)
+					VALUES
+					('.$o['d_oid'].', NOW(), '.$row['k_product'].', 1, 0, NULL)';
+				if (isset($_GET['go']))
+					DB::d_insert($sql);
+				else
+					echo '<p>'.$sql.'</p>';
+			}
+		}
+	}
+	else {
+		echo '<p>ERREUR MANQUE CORRESPONDANCE LOT</p>';
+	}
+}
+
+die();

+ 62 - 0
src/repair/supplier.inc.php

@@ -0,0 +1,62 @@
+<?php
+
+$nb = sync::_class('supplier', 'pd')::d_ref_nb();
+
+$sql = 'SELECT DISTINCT c.rowid, c.datec
+	FROM llx_societe c
+	WHERE c.fournisseur=1 AND (c.code_fournisseur IS NULL OR c.code_fournisseur NOT REGEXP "SU[0-9]{4}-[0-9]{6}")
+	ORDER BY c.datec, c.rowid';
+$q = DB::d_select($sql);
+if (true) while(list($id, $date)=$q->fetch_row()) {
+	var_dump($id); var_dump($date);
+	$nb++;
+	for ($i=strlen($nb); $i<6; $i++)
+		$nb = '0'.$nb;
+	$ref = 'SU'.substr($date, 2, 2).substr($date, 5, 2).'-'.$nb;
+	$sql = 'UPDATE llx_societe
+		SET code_fournisseur="'.$ref.'"
+		WHERE rowid='.$id;
+	if (isset($_GET['go']))
+		DB::d_update($sql);
+	else
+		echo '<p>'.$sql.'</p>';
+}
+
+die();
+
+// --
+
+$sql = 'SELECT SUBSTRING(MAX(code_fournisseur), 8, 6)
+	FROM `llx_societe`';
+$q = DB::d_select($sql);
+if ($q->num_rows)
+	list($nb) = $q->fetch_row();
+else
+	$nb = 0;
+
+var_dump($nb);
+
+$sql = 'SELECT DISTINCT DISTINCT c.rowid
+	FROM `llx_societe` c
+	WHERE c.code_fournisseur IS NULL AND fournisseur=1';
+$q = DB::d_select($sql);
+if (false) while(list($id)=$q->fetch_row()) {
+	var_dump($id);
+	$nb++;
+	if (($n=strlen($nb))<5)
+		for ($i=1;$i<=5-$n;$i++)
+			$nb = '0'.$nb;
+	$sql = 'UPDATE llx_societe
+		SET code_fournisseur="'.('SU'.date('ym').'-'.$nb).'"
+		WHERE rowid='.$id;
+	if (isset($_GET['go']))
+		DB::d_update($sql);
+	else
+		var_dump($sql);
+}
+
+die();
+
+$sql = 'UPDATE `llx_societe`
+	SET code_fournisseur = CONCAT(SUBSTRING(code_fournisseur, 1, 7), "0", SUBSTRING(code_fournisseur, 8, 5))
+	WHERE code_fournisseur IS NOT NULL';

+ 52 - 0
src/sync/address/common.inc.php

@@ -0,0 +1,52 @@
+<?php
+
+// Order
+
+class sync_address extends sync
+{
+
+const MODEL_NAME = 'address';
+const MODEL_ID = 21;
+const MAP_ID = 10;
+const D_TABLE_MAIN = 'socpeople';
+const P_TABLE_MAIN = 'address';
+const P_TABLE_MAIN_ID = 'id_address';
+
+function d_data($otype, $oid)
+{
+	$data = [];
+	$data['socpeople'] = DB::d_get_row('socpeople', ['rowid'=>$oid]);
+	$data['socpeople_extrafields'] = DB::d_get_row('socpeople_extrafields', ['fk_object'=>$oid]);
+
+	// societe
+	$data['societe'] = DB::d_get_row('societe', ['rowid'=>$data['socpeople']['fk_soc']]);
+	$data['societe_extrafields'] = DB::d_get_row('societe_extrafields', ['fk_object'=>$data['socpeople']['fk_soc']]);
+
+	return $data;
+}
+
+function p_data($otype, $oid)
+{
+	$data = [];
+	$data['address'] = DB::p_get_row('address', ['id_address'=>$oid]);
+	
+	// customer
+	if ($data['address']['id_customer'])
+		$data['customer'] = DB::p_get_row('customer', ['id_customer'=>$data['address']['id_customer']]);
+	
+	// supplier
+	if ($data['address']['id_supplier'])
+		$data['supplier'] = DB::p_get_row('supplier', ['id_supplier'=>$data['address']['id_supplier']]);
+	
+	// manufacturer
+	if ($data['address']['id_manufacturer'])
+		$data['manufacturer'] = DB::p_get_row('manufacturer', ['id_manufacturer'=>$data['address']['id_manufacturer']]);
+	
+	// warehouse
+	if ($data['address']['id_warehouse'])
+		$data['warehouse'] = DB::p_get_row('warehouse', ['id_warehouse'=>$data['address']['id_warehouse']]);
+	
+	return $data;
+}
+
+}

+ 131 - 0
src/sync/address/dp.inc.php

@@ -0,0 +1,131 @@
+<?php
+
+class sync_address_dp extends sync_address
+{
+
+// -- ACTIONS --
+
+public function execute_osync($otype, $oid)
+{
+	return $this->dp_execute_osync($otype, $oid);
+}
+
+public function execute_create($otype, $oid)
+{
+	return $this->create($otype, $oid);
+}
+
+// -- SUB ACTIONS --
+
+public function create($otype, $oid)
+{
+	return $this->dp_create($otype, $oid);
+}
+
+public function osync($o)
+{
+	return $this->dp_osync($o);
+}
+
+// -- MAPPING --
+
+public function map_update(&$o, &$d_data, &$p_data=[])
+{
+	$p_data_map = [
+		'address' => [
+			'id_customer' => static::_dp_map_fk_customer($d_data),
+			'id_country' => static::_dp_map_fk_country($d_data),
+			'lastname' => $d_data['socpeople']['lastname'],
+			'firstname' => $d_data['socpeople']['firstname'],
+			'address1' => $d_data['socpeople']['address'],
+			'address2' => '',
+			'postcode' => $d_data['socpeople']['zip'],
+			'city' => $d_data['socpeople']['town'],
+			'other' => '',
+			'phone' => $d_data['socpeople']['phone'],
+			'phone_mobile' => $d_data['socpeople']['phone_mobile'],
+			'date_upd' => $d_data['socpeople']['tms'],
+			'deleted' => (isset($d_data['socpeople_extrafields']) ?(!empty($d_data['socpeople_extrafields']['p_deleted']) ?1 :0) :0),
+		],
+	];
+	//var_dump($p_data_map);
+
+	return $p_data_map;
+}
+
+public function map_create(&$o, &$d_data)
+{
+	$p_data_map = [
+		'address' => [
+			//'id_address' => '',
+			'id_country' => static::_dp_map_fk_country($d_data),
+			'id_state' => static::_dp_map_fk_state($d_data),
+			'id_customer' => static::_dp_map_fk_customer($d_data),
+			'id_manufacturer' => static::_dp_map_fk_manufacturer($d_data),
+			'id_supplier' => static::_dp_map_fk_supplier($d_data),
+			'id_warehouse' => 0,
+			'alias' => '',
+			'company' => '',
+			'lastname' => '',
+			'firstname' => '',
+			'address1' => '',
+			'address2' => '',
+			'postcode' => '',
+			'city' => '',
+			'other' => '',
+			'phone' => '',
+			'phone_mobile' => '',
+			'vat_number' => '',
+			'dni' => '',
+			'date_add' => $d_data['socpeople']['datec'],
+			'date_upd' => '0000-00-00 00:00:00',
+			'active' => '1',
+			'deleted' => '0',
+		],
+	];
+
+	return $p_data_map;
+}
+
+// -----------
+
+// Field MAPPING
+
+public static function _dp_map_fk_customer(&$orig_data, &$dest_tree=[])
+{
+	if (!$orig_data['societe']['client'])
+		return 0;
+	return static::_dp_map_fk('customer', 'societe', $orig_data['socpeople']['fk_soc']);
+}
+public static function _dp_map_fk_manufacturer(&$orig_data, &$dest_tree=[])
+{
+	if (true) // @todo
+		return 0;
+	return static::_dp_map_fk('manufacturer', 'societe', $orig_data['socpeople']['fk_soc']);
+}
+public static function _dp_map_fk_supplier(&$orig_data, &$dest_tree=[])
+{
+	if (!$orig_data['societe']['fournisseur'])
+		return 0;
+	return static::_dp_map_fk('supplier', 'societe', $orig_data['socpeople']['fk_soc']);
+}
+
+public static function _dp_map_fk_country(&$orig_data, &$dest_tree=[])
+{
+	static::_country_load();
+
+	if (!empty($orig_data['socpeople']['fk_pays'])) {
+		$id_country = $orig_data['socpeople']['fk_pays'];
+		if (isset(static::$_dp_country[$id_country]))
+			return static::$_dp_country[$id_country];
+	}
+}
+
+public static function _dp_map_fk_state(&$orig_data, &$dest_tree=[])
+{
+	return 0; // @todo
+}
+
+}
+
+sync_address_dp::__init();

+ 192 - 0
src/sync/address/pd.inc.php

@@ -0,0 +1,192 @@
+<?php
+
+class sync_address_pd extends sync_address
+{
+
+// -- ACTIONS --
+
+public function execute_osync($otype, $oid)
+{
+	return $this->pd_execute_osync($otype, $oid);
+}
+
+public function execute_create($otype, $oid)
+{
+	return $this->create($otype, $oid);
+}
+
+// -- SUB ACTIONS --
+
+public function create($otype, $oid)
+{
+	return $this->pd_create($otype, $oid);
+}
+
+public function osync($o)
+{
+	return $this->pd_osync($o);
+}
+public function pd_update_more(&$o, &$d_data_map, &$d_data_map_create, &$d_data, &$p_data)
+{
+	$id = $o['d_oid'];
+	// @todo
+	
+	if (!empty($d_data['socpeople_extrafields']))
+		static::d_update_row('socpeople_extrafields', ['rowid'=>$d_data['socpeople_extrafields']['rowid']], $d_data_map, $d_data_map_create, $d_data);
+	else {
+		$d_data_map_insert = [
+			'socpeople_extrafields' => $d_data_map_create['socpeople_extrafields'],
+		];
+		static::_array_merge_recursive($d_data_map_insert, $d_data_map);
+		DB::d_insert_row('socpeople_extrafields', ['fk_object'=>$id], $d_data_map_insert, $d_data);
+	}
+}
+
+// -- MAPPING --
+
+public function map_update(&$o, &$p_data, &$d_data=[])
+{
+	$d_data_map = [
+		'socpeople' => [
+			//'rowid' => NULL,
+			//'datec' => $p_data['address']['date_add'],
+			'tms' => $p_data['address']['date_upd'],
+			//'fk_soc' => static::_pd_map_fk_soc($p_data),
+			//'entity' => 1,
+			//'ref_ext' => '',
+			//'civility' => '',
+			'lastname' => $p_data['address']['lastname'],
+			'firstname' => $p_data['address']['firstname'],
+			'address' => $p_data['address']['address1'].($p_data['address']['address2'] ?"\r\n".$p_data['address']['address2'] :''),
+			'zip' => $p_data['address']['postcode'],
+			'town' => $p_data['address']['city'],
+			//'fk_departement' => NULL,
+			'fk_pays' => static::_pd_map_fk_pays($p_data),
+			'birthday' => (isset($p_data['customer']['birthday']) ?$p_data['customer']['birthday'] :NULL),
+			//'poste' => '',
+			'phone' => $p_data['address']['phone'],
+			//'phone_perso' => '',
+			'phone_mobile' => $p_data['address']['phone_mobile'],
+			//'fax' => '',
+			'email' => (isset($p_data['customer']['email']) ?$p_data['customer']['email'] :''),
+			//'socialnetworks' => '[]',
+			//'jabberid' => NULL,
+			//'skype' => NULL,
+			//'twitter' => NULL,
+			//'facebook' => NULL,
+			//'linkedin' => NULL,
+			//'instagram' => NULL,
+			//'snapchat' => NULL,
+			//'googleplus' => NULL,
+			//'youtube' => NULL,
+			//'whatsapp' => NULL,
+			//'photo' => '',
+			//'no_email' => 0,
+			//'priv' => 0,
+			//'fk_user_creat' => static::D_K_USER,
+			'fk_user_modif' => static::D_K_USER,
+			//'note_private' => NULL,
+			//'note_public' => NULL,
+			//'default_lang' => NULL,
+			//'canvas' => NULL,
+			//'import_key' => NULL,
+			//'statut' => 1,
+			//'fk_prospectcontactlevel' => NULL,
+			//'fk_stcommcontact' => 0,
+		],
+		'socpeople_extrafields' => [
+			'p_deleted' => (!empty($p_data['address']['deleted']) ?1 :0),
+		]
+	];
+
+	return $d_data_map;
+}
+
+public function map_create(&$o, &$p_data)
+{
+	$d_data_map = [
+		'socpeople' => [
+			//'rowid' => NULL,
+			'datec' => $p_data['address']['date_add'],
+			'tms' => $p_data['address']['date_upd'],
+			'fk_soc' => static::_pd_map_fk_soc($p_data),
+			'entity' => 1,
+			'ref_ext' => '',
+			'civility' => '',
+			'lastname' => $p_data['address']['lastname'],
+			'firstname' => $p_data['address']['firstname'],
+			'address' => $p_data['address']['address1'].($p_data['address']['address2'] ?"\r\n".$p_data['address']['address2'] :''),
+			'zip' => $p_data['address']['postcode'],
+			'town' => $p_data['address']['city'],
+			'fk_departement' => NULL,
+			'fk_pays' => static::_pd_map_fk_pays($p_data),
+			'birthday' => (isset($p_data['customer']['birthday']) ?$p_data['customer']['birthday'] :NULL),
+			'poste' => '',
+			'phone' => $p_data['address']['phone'],
+			'phone_perso' => '',
+			'phone_mobile' => $p_data['address']['phone_mobile'],
+			'fax' => '',
+			'email' => (isset($p_data['customer']['email']) ?$p_data['customer']['email'] :''),
+			'socialnetworks' => '[]',
+			'jabberid' => NULL,
+			'skype' => NULL,
+			'twitter' => NULL,
+			'facebook' => NULL,
+			'linkedin' => NULL,
+			'instagram' => NULL,
+			'snapchat' => NULL,
+			'googleplus' => NULL,
+			'youtube' => NULL,
+			'whatsapp' => NULL,
+			'photo' => '',
+			'no_email' => 0,
+			'priv' => 0,
+			'fk_user_creat' => static::D_K_USER,
+			'fk_user_modif' => static::D_K_USER,
+			'note_private' => NULL,
+			'note_public' => NULL,
+			'default_lang' => NULL,
+			'canvas' => NULL,
+			'import_key' => NULL,
+			'statut' => 1,
+			'fk_prospectcontactlevel' => NULL,
+			'fk_stcommcontact' => 0,
+		],
+		'socpeople_extrafields' => [
+			'p_deleted' => (!empty($p_data['address']['deleted']) ?1 :0),
+		]
+	];
+
+	return $d_data_map;
+}
+
+// -----------
+
+// Field MAPPING
+
+public static function _pd_map_fk_soc(&$orig_data, &$dest_tree=[])
+{
+	if (!empty($orig_data['address']['id_supplier']))
+		return static::_pd_map_fk('supplier', 'supplier', $orig_data['address']['id_supplier']);
+	if (!empty($orig_data['address']['id_customer']))
+		return static::_pd_map_fk('customer', 'customer', $orig_data['address']['id_customer']);
+	if (!empty($orig_data['address']['id_manufacturer']))
+		return static::_pd_map_fk('manufacturer', 'manufacturer', $orig_data['address']['id_manufacturer']);
+	if (!empty($orig_data['address']['id_warehouse']))
+		return static::_pd_map_fk('warehouse', 'warehouse', $orig_data['address']['id_warehouse']);
+}
+
+public static function _pd_map_fk_pays(&$orig_data, &$dest_tree=[])
+{
+	static::_country_load();
+
+	if (!empty($orig_data['address']['id_country'])) {
+		$id_country = $orig_data['address']['id_country'];
+		if (isset(static::$_pd_country[$id_country]))
+			return static::$_pd_country[$id_country];
+	}
+}
+
+}
+
+sync_address_pd::__init();

+ 15 - 0
src/sync/common.inc.php

@@ -0,0 +1,15 @@
+<?php
+
+define('SYNC_PATH', SRC_PATH.'/sync');
+
+function sync_error($message)
+{
+	//header($_SERVER['SERVER_PROTOCOL'] . ' 500 Internal Server Error', true, 500);
+	mail(DEBUG_EMAIL_TO, 'Erreur Synchro DoliPresta', $message, 'From: '.DEBUG_EMAIL_FROM);
+	echo '<p>ERREUR :</p>';
+	var_dump($message);
+	//die(json_encode(['message'=>$message]));
+}
+
+require_once('db.inc.php');
+require_once('sync.inc.php');

+ 44 - 0
src/sync/customer/common.inc.php

@@ -0,0 +1,44 @@
+<?php
+
+// Order
+
+class sync_customer extends sync
+{
+
+const MODEL_NAME = 'customer';
+const MODEL_ID = 14;
+const MAP_ID = 4;
+const D_TABLE_MAIN = 'societe';
+const D_TABLE_MAIN_REF = 'code_client';
+const P_TABLE_MAIN = 'customer';
+const P_TABLE_MAIN_ID = 'id_customer';
+
+protected static $nb;
+const D_CODE = 'CU';
+
+function d_data($otype, $oid)
+{
+	$data = [];
+	$data['societe'] = DB::d_get_row('societe', ['rowid'=>$oid]);
+	
+	// adress
+	$data['socpeople'] = DB::d_get_rows('socpeople', ['fk_soc'=>$oid]);
+
+	return $data;
+}
+
+function p_data($otype, $oid)
+{
+	$data = [];
+	$data['customer'] = DB::p_get_row('customer', ['id_customer'=>$oid]);
+	
+	// adress
+	$data['address'] = DB::p_get_rows('address', ['id_customer'=>$oid], 'id_address DESC');
+	
+	// last order
+	$data['order'] = DB::p_get_row('orders', ['id_customer'=>$oid], 'id_order DESC');
+	
+	return $data;
+}
+
+}

+ 95 - 0
src/sync/customer/dp.inc.php

@@ -0,0 +1,95 @@
+<?php
+
+class sync_customer_dp extends sync_customer
+{
+
+// -- ACTIONS --
+
+public function execute_osync($otype, $oid)
+{
+	return $this->dp_execute_osync($otype, $oid);
+}
+
+public function execute_create($otype, $oid)
+{
+	return $this->create($otype, $oid);
+}
+
+// -- SUB ACTIONS --
+
+public function create($otype, $oid)
+{
+	return $this->dp_create($otype, $oid);
+}
+
+public function osync($o)
+{
+	return $this->dp_osync($o);
+}
+
+// -- MAPPING --
+
+public function map_update(&$o, &$d_data, &$p_data=[])
+{
+	$p_data_map = [
+		'customer' => [
+			'email' => $d_data['societe']['email'],
+			'siret' => $d_data['societe']['siret'],
+			'ape' => $d_data['societe']['ape'],
+			//'date_upd' => $d_data['societe']['tms'],
+		],
+	];
+
+	return $p_data_map;
+}
+
+public function map_create(&$o, &$d_data)
+{
+	$p_data_map = [
+		'customer' => [
+			//'id_customer' => '',
+			'id_shop_group' => static::P_ID_SHOP_GROUP,
+			'id_shop' => static::P_ID_SHOP,
+			'id_gender' => 0, // 1 H 2 F
+			'id_default_group' => static::P_ID_GROUP_DEFAULT,
+			'id_lang' => static::P_ID_LANG,
+			'id_risk' => 0,
+			'company' => $d_data['societe']['name_alias'],
+			'siret' => $d_data['societe']['siret'],
+			'ape' => $d_data['societe']['ape'],
+			'firstname' => $d_data['societe']['nom'],
+			'lastname' => '',
+			'email' => $d_data['societe']['email'],
+			'passwd' => '',
+			'last_passwd_gen' => NULL,
+			'birthday' => '0000-00-00',
+			'newsletter' => 0,
+			'ip_registration_newsletter' => NULL,
+			'newsletter_date_add' => '0000-00-00 00:00:00',
+			'optin' => 0,
+			'website' => NULL,
+			'outstanding_allow_amount' => 0,
+			'show_public_prices' => 0,
+			'max_payment_days' => 0,
+			'secure_key' => '',
+			'note' => NULL,
+			'active' => 1,
+			'is_guest' => 0,
+			'deleted' => 0,
+			'date_add' => $d_data['societe']['datec'],
+			'date_upd' => $d_data['societe']['tms'],
+			'reset_password_token' => NULL,
+			'reset_password_validity' => '0000-00-00 00:00:00',
+		],
+	];
+
+	return $p_data_map;
+}
+
+// -----------
+
+// Field MAPPING
+
+}
+
+sync_customer_dp::__init();

+ 384 - 0
src/sync/customer/pd.inc.php

@@ -0,0 +1,384 @@
+<?php
+
+class sync_customer_pd extends sync_customer
+{
+
+// -- ACTIONS --
+
+public function execute_osync($otype, $oid)
+{
+	return $this->pd_execute_osync($otype, $oid);
+}
+
+public function execute_create($otype, $oid)
+{
+	return $this->create($otype, $oid);
+}
+
+// -- SUB ACTIONS --
+
+public function create($otype, $oid)
+{
+	return $this->pd_create($otype, $oid);
+}
+
+public function osync($o)
+{
+	return $this->pd_osync($o);
+}
+
+// -- MAPPING --
+
+public function map_update(&$o, &$p_data, &$d_data=[])
+{
+	$d_data_map = [
+		'societe' => [
+			//'rowid' => NULL,
+			'nom' => $p_data['customer']['firstname'].' '.$p_data['customer']['lastname'],
+			'name_alias' => $p_data['customer']['company'],
+			//'entity' => 1,
+			//'ref_ext' => NULL,
+			//'ref_int' => NULL,
+			//'statut' => 0,
+			//'parent' => NULL,
+			//'status' => 1,
+			//'code_client' => static::_p_map_code_client($p_data),
+			//'code_fournisseur' => NULL,
+			//'code_compta' => NULL,
+			//'code_compta_fournisseur' => NULL,
+			//'address' => '',
+			//'zip' => NULL,
+			//'town' => NULL,
+			//'fk_departement' => NULL,
+			//'fk_pays' => NULL,
+			//'fk_account' => NULL,
+			//'phone' => NULL,
+			//'fax' => NULL,
+			//'url' => NULL,
+			'email' => $p_data['customer']['email'],
+			//'socialnetworks' => NULL,
+			//'skype' => NULL,
+			//'twitter' => NULL,
+			//'facebook' => NULL,
+			//'linkedin' => NULL,
+			//'instagram' => NULL,
+			//'snapchat' => NULL,
+			//'googleplus' => NULL,
+			//'youtube' => NULL,
+			//'whatsapp' => NULL,
+			//'fk_effectif' => NULL,
+			//'fk_typent' => 0,
+			//'fk_forme_juridique' => NULL,
+			//'fk_currency' => NULL,
+			//'siren' => '',
+			'siret' => $p_data['customer']['siret'],
+			'ape' => $p_data['customer']['ape'],
+			//'idprof4' => '',
+			//'idprof5' => '',
+			//'idprof6' => '',
+			//'tva_intra' => '',
+			//'capital' => NULL,
+			//'fk_stcomm' => 0,
+			//'note_private' => NULL,
+			//'note_public' => NULL,
+			//'model_pdf' => NULL,
+			//'prefix_comm' => NULL,
+			//'client' => 1,
+			//'fournisseur' => 0,
+			//'supplier_account' => NULL,
+			//'fk_prospectlevel' => '',
+			//'fk_incoterms' => 0,
+			//'location_incoterms' => NULL,
+			//'customer_bad' => 0,
+			//'customer_rate' => 0,
+			//'supplier_rate' => 0,
+			//'remise_client' => 0,
+			//'remise_supplier' => 0,
+			//'mode_reglement' => NULL,
+			//'cond_reglement' => NULL,
+			//'mode_reglement_supplier' => NULL,
+			//'cond_reglement_supplier' => NULL,
+			//'fk_shipping_method' => NULL,
+			//'tva_assuj' => 1,
+			//'localtax1_assuj' => NULL,
+			//'localtax1_value' => 0,
+			//'localtax2_assuj' => NULL,
+			//'localtax2_value' => 0,
+			//'barcode' => NULL,
+			//'fk_barcode_type' => 0,
+			//'price_level' => NULL,
+			//'outstanding_limit' => NULL,
+			//'order_min_amount' => NULL,
+			//'supplier_order_min_amount' => NULL,
+			//'default_lang' => NULL,
+			//'logo' => NULL,
+			//'logo_squarred' => NULL,
+			//'canvas' => NULL,
+			//'webservices_url' => NULL,
+			//'webservices_key' => NULL,
+			//'tms' => $p_data['customer']['date_upd'], //@todo tms
+			//'datec' => $p_data['customer']['date_add'],
+			//'fk_user_creat' => static::D_K_USER,
+			//'fk_user_modif' => static::D_K_USER,
+			//'fk_multicurrency' => 0,
+			//'multicurrency_code' => '',
+			//'import_key' => NULL,
+			//'transport_mode' => NULL,
+			//'transport_mode_supplier' => NULL,
+			//'fk_warehouse' => NULL,
+			//'accountancy_code_sell' => NULL,
+			//'accountancy_code_buy' => NULL,
+		],
+	];
+	
+	$map_invoice = $this->map_invoice($o, $p_data);
+	static::_array_merge_recursive($d_data_map, $map_invoice);
+
+	return $d_data_map;
+}
+
+public function map_create(&$o, &$p_data)
+{
+	$d_data_map = [
+		'societe' => [
+			//'rowid' => NULL,
+			'nom' => $p_data['customer']['firstname'].' '.$p_data['customer']['lastname'],
+			'name_alias' => $p_data['customer']['company'],
+			'entity' => 1,
+			'ref_ext' => NULL,
+			'ref_int' => NULL,
+			'statut' => 0,
+			'parent' => NULL,
+			'status' => 1,
+			'code_client' => static::d_ref($p_data['customer']['date_add']),
+			'code_fournisseur' => NULL,
+			'code_compta' => NULL,
+			'code_compta_fournisseur' => NULL,
+			'address' => '',
+			'zip' => NULL,
+			'town' => NULL,
+			'fk_departement' => NULL,
+			'fk_pays' => NULL,
+			'fk_account' => NULL,
+			'phone' => NULL,
+			'fax' => NULL,
+			'url' => NULL,
+			'email' => $p_data['customer']['email'],
+			'socialnetworks' => NULL,
+			'skype' => NULL,
+			'twitter' => NULL,
+			'facebook' => NULL,
+			'linkedin' => NULL,
+			'instagram' => NULL,
+			'snapchat' => NULL,
+			'googleplus' => NULL,
+			'youtube' => NULL,
+			'whatsapp' => NULL,
+			'fk_effectif' => NULL,
+			'fk_typent' => 0,
+			'fk_forme_juridique' => NULL,
+			'fk_currency' => NULL,
+			'siren' => '',
+			'siret' => $p_data['customer']['siret'],
+			'ape' => $p_data['customer']['ape'],
+			'idprof4' => '',
+			'idprof5' => '',
+			'idprof6' => '',
+			'tva_intra' => '',
+			'capital' => NULL,
+			'fk_stcomm' => 0,
+			'note_private' => NULL,
+			'note_public' => NULL,
+			'model_pdf' => NULL,
+			'prefix_comm' => NULL,
+			'client' => 1,
+			'fournisseur' => 0,
+			'supplier_account' => NULL,
+			'fk_prospectlevel' => '',
+			'fk_incoterms' => 0,
+			'location_incoterms' => NULL,
+			'customer_bad' => 0,
+			'customer_rate' => 0,
+			'supplier_rate' => 0,
+			'remise_client' => 0,
+			'remise_supplier' => 0,
+			'mode_reglement' => NULL,
+			'cond_reglement' => NULL,
+			'mode_reglement_supplier' => NULL,
+			'cond_reglement_supplier' => NULL,
+			'fk_shipping_method' => NULL,
+			'tva_assuj' => 1,
+			'localtax1_assuj' => NULL,
+			'localtax1_value' => 0,
+			'localtax2_assuj' => NULL,
+			'localtax2_value' => 0,
+			'barcode' => NULL,
+			'fk_barcode_type' => 0,
+			'price_level' => NULL,
+			'outstanding_limit' => NULL,
+			'order_min_amount' => NULL,
+			'supplier_order_min_amount' => NULL,
+			'default_lang' => NULL,
+			'logo' => NULL,
+			'logo_squarred' => NULL,
+			'canvas' => NULL,
+			'webservices_url' => NULL,
+			'webservices_key' => NULL,
+			'tms' => $p_data['customer']['date_upd'],
+			'datec' => $p_data['customer']['date_add'],
+			'fk_user_creat' => static::D_K_USER,
+			'fk_user_modif' => static::D_K_USER,
+			'fk_multicurrency' => 0,
+			'multicurrency_code' => '',
+			'import_key' => NULL,
+			'transport_mode' => NULL,
+			'transport_mode_supplier' => NULL,
+			'fk_warehouse' => NULL,
+			'accountancy_code_sell' => NULL,
+			'accountancy_code_buy' => NULL,
+		],
+	];
+	
+	$map_invoice = $this->map_invoice($o, $p_data);
+	static::_array_merge_recursive($d_data_map, $map_invoice);
+
+	return $d_data_map;
+}
+
+public function map_invoice(&$o, &$p_data)
+{
+	if(!empty($p_data['order'])) {
+		$id_address_invoice = $p_data['order']['id_address_invoice'];
+		$id_address_delivery = $p_data['order']['id_address_delivery'];
+	}
+	elseif (!empty($p_data['address'])) {
+		$id_address_invoice = $p_data['address'][0]['id_address'];
+		$id_address_delivery = $p_data['address'][0]['id_address'];
+	}
+	
+	if (!empty($p_data['address'])) foreach($p_data['address'] as $address) {
+		if ($address['id_address']==$id_address_invoice)
+			$address_invoice = $address;
+		if ($address['id_address']==$id_address_delivery)
+			$address_delivery = $address;
+	}
+	
+	if (!empty($address_invoice)) {
+		$d_data_map = [
+			'societe' => [
+				//'rowid' => NULL,
+				//'nom' => $p_data['customer']['firstname'].' '.$p_data['customer']['lastname'],
+				//'name_alias' => $p_data['customer']['company'],
+				//'entity' => 1,
+				//'ref_ext' => NULL,
+				//'ref_int' => NULL,
+				//'statut' => 0,
+				//'parent' => NULL,
+				//'status' => 1,
+				//'code_client' => static::_p_map_code_client($p_data),
+				//'code_fournisseur' => NULL,
+				//'code_compta' => NULL,
+				//'code_compta_fournisseur' => NULL,
+				'address' => $address_invoice['address1'].($address_invoice['address2'] ?"\r\n".$address_invoice['address2'] :''),
+				'zip' => $address_invoice['postcode'],
+				'town' => $address_invoice['city'],
+				//'fk_departement' => NULL,
+				//'fk_pays' => NULL,
+				//'fk_account' => NULL,
+				'phone' => ($address_invoice['phone_mobile'] ?$address_invoice['phone_mobile'] :$address_invoice['phone']),
+				//'fax' => NULL,
+				//'url' => NULL,
+				//'email' => $p_data['customer']['email'],
+				//'socialnetworks' => NULL,
+				//'skype' => NULL,
+				//'twitter' => NULL,
+				//'facebook' => NULL,
+				//'linkedin' => NULL,
+				//'instagram' => NULL,
+				//'snapchat' => NULL,
+				//'googleplus' => NULL,
+				//'youtube' => NULL,
+				//'whatsapp' => NULL,
+				//'fk_effectif' => NULL,
+				//'fk_typent' => 0,
+				//'fk_forme_juridique' => NULL,
+				//'fk_currency' => NULL,
+				//'siren' => '',
+				//'siret' => $p_data['customer']['siret'],
+				//'ape' => $p_data['customer']['ape'],
+				//'idprof4' => '',
+				//'idprof5' => '',
+				//'idprof6' => '',
+				//'tva_intra' => '',
+				//'capital' => NULL,
+				//'fk_stcomm' => 0,
+				//'note_private' => NULL,
+				//'note_public' => NULL,
+				//'model_pdf' => NULL,
+				//'prefix_comm' => NULL,
+				//'client' => 1,
+				//'fournisseur' => 0,
+				//'supplier_account' => NULL,
+				//'fk_prospectlevel' => '',
+				//'fk_incoterms' => 0,
+				//'location_incoterms' => NULL,
+				//'customer_bad' => 0,
+				//'customer_rate' => 0,
+				//'supplier_rate' => 0,
+				//'remise_client' => 0,
+				//'remise_supplier' => 0,
+				//'mode_reglement' => NULL,
+				//'cond_reglement' => NULL,
+				//'mode_reglement_supplier' => NULL,
+				//'cond_reglement_supplier' => NULL,
+				//'fk_shipping_method' => NULL,
+				//'tva_assuj' => 1,
+				//'localtax1_assuj' => NULL,
+				//'localtax1_value' => 0,
+				//'localtax2_assuj' => NULL,
+				//'localtax2_value' => 0,
+				//'barcode' => NULL,
+				//'fk_barcode_type' => 0,
+				//'price_level' => NULL,
+				//'outstanding_limit' => NULL,
+				//'order_min_amount' => NULL,
+				//'supplier_order_min_amount' => NULL,
+				//'default_lang' => NULL,
+				//'logo' => NULL,
+				//'logo_squarred' => NULL,
+				//'canvas' => NULL,
+				//'webservices_url' => NULL,
+				//'webservices_key' => NULL,
+				//'tms' => $p_data['customer']['date_upd'], //@todo tms
+				//'datec' => $p_data['customer']['date_add'],
+				//'fk_user_creat' => static::D_K_USER,
+				//'fk_user_modif' => static::D_K_USER,
+				//'fk_multicurrency' => 0,
+				//'multicurrency_code' => '',
+				//'import_key' => NULL,
+				//'transport_mode' => NULL,
+				//'transport_mode_supplier' => NULL,
+				//'fk_warehouse' => NULL,
+				//'accountancy_code_sell' => NULL,
+				//'accountancy_code_buy' => NULL,
+			],
+		];
+		
+		return $d_data_map;
+	}
+	
+	return [];
+}
+
+// -----------
+
+// Field MAPPING
+
+public static function _pd_map_fk_pays(&$orig_data, &$dest_tree=[])
+{
+	return 1; // @todo
+}
+
+}
+
+sync_customer_pd::__init();

+ 474 - 0
src/sync/db.inc.php

@@ -0,0 +1,474 @@
+<?php
+
+// -- DATABASE --
+
+class DB
+{
+
+const DB_P_PREFIX = 'ps_';
+
+const DB_D_PREFIX = 'llx_';
+
+public static function __init()
+{
+	
+}
+
+/**
+ * management tables
+ */
+public static function o_query($sql)
+{
+	global $db;
+	$q = $db->query($sql);
+	echo '<p>'.htmlentities($sql).' : '.$db->affected_rows.' / '.$db->insert_id.' / '.$db->error.'</p>';
+	return $q;
+}
+public static function o_select($sql)
+{
+	global $db;
+	$q = $db->query($sql);
+	echo '<p>'.htmlentities($sql).' : '.$q->num_rows.' / '.$db->error.'</p>';
+	return $q;
+}
+public static function o_count($sql)
+{
+	global $db;
+	$q = $db->query($sql);
+	echo '<p>'.htmlentities($sql).' : '.$q->num_rows.' / '.$db->error.'</p>';
+	list($nb) = $q->fetch_row();
+	return $nb;
+}
+public static function o_insert($sql)
+{
+	global $db;
+	$q = $db->query($sql);
+	echo '<p>'.htmlentities($sql).' : '.$db->insert_id.' / '.$db->error.'</p>';
+	if ($q)
+		return $db->insert_id;
+}
+public static function o_update($sql)
+{
+	global $db;
+	$q = $db->query($sql);
+	echo '<p>'.htmlentities($sql).' : '.$db->affected_rows.' / '.$db->error.'</p>';
+	if ($q)
+		return $db->affected_rows;
+}
+public static function o_delete($sql)
+{
+	global $db;
+	$q = $db->query($sql);
+	echo '<p>'.htmlentities($sql).' : '.$db->affected_rows.' / '.$db->error.'</p>';
+	if ($q)
+		return $db->affected_rows;
+}
+
+// -- D QUERY SQL --
+
+public static function d_query($sql)
+{
+	global $db_d;
+
+	$q = $db_d->query($sql);
+
+	echo '<p>'.htmlentities($sql).' : '.$db_d->affected_rows.' / '.$db_d->insert_id.' / '.$db_d->error.'</p>';
+	return $q;
+}
+public static function d_select($sql)
+{
+	global $db_d;
+
+	$q = $db_d->query($sql);
+
+	echo '<p>'.htmlentities($sql).' : '.$q->num_rows.' / '.$db_d->error.'</p>';
+	return $q;
+}
+public static function d_count($sql)
+{
+	global $db_d;
+	$q = $db_d->query($sql);
+	echo '<p>'.htmlentities($sql).' : '.$q->num_rows.' / '.$db_d->error.'</p>';
+	list($nb) = $q->fetch_row();
+	return $nb;
+}
+public static function d_insert($sql)
+{
+	global $db_d;
+
+	$q = $db_d->query($sql);
+	var_dump($q);
+	echo '<p>'.htmlentities($sql).' : '.$db_d->insert_id.' / '.$db_d->error.'</p>';
+	if ($q)
+		return $db_d->insert_id;
+}
+public static function d_update($sql)
+{
+	global $db_d;
+	$q = $db_d->query($sql);
+	echo '<p>'.htmlentities($sql).' : '.$db_d->affected_rows.' / '.$db_d->error.'</p>';
+	if ($q)
+		return $db_d->affected_rows;
+}
+public static function d_delete($sql)
+{
+	global $db_d;
+	$q = $db_d->query($sql);
+	echo '<p>'.htmlentities($sql).' : '.$db_d->affected_rows.' / '.$db_d->error.'</p>';
+	if ($q)
+		return $db_d->affected_rows;
+}
+
+/**
+ * d_get
+ */
+public static function d_get_rows($tablename, $params)
+{
+	$sql = static::d_get_sql($tablename, $params);
+	$q = static::d_query($sql);
+
+	$r = [];
+	while ($row=$q->fetch_assoc())
+		$r[] = $row;
+	return $r;
+}
+public static function d_get_row($tablename, $params, $orderby=NULL)
+{
+	$sql = static::d_get_sql($tablename, $params, $orderby).' LIMIT 1';
+	$q = static::d_query($sql);
+	return $q->fetch_assoc();
+}
+public static function d_get_sql($tablename, $params, $orderby=NULL)
+{
+	$sql = 'SELECT *
+		FROM `'.static::DB_D_PREFIX.$tablename.'`
+		WHERE ';
+	if (is_numeric($params)) {
+		$sql .= 'rowid='.$params;
+	}
+	elseif (is_array($params)) {
+		$sql_params = [];
+		foreach($params as $i=>$j)
+			$sql_params[] = '`'.$i.'`="'.$j.'"';
+		$sql .= implode(' AND ', $sql_params);
+	}
+	elseif (is_string($params)) {
+		$sql .= $params;
+	}
+	
+	if ($orderby)
+		$sql .= ' ORDER BY '.$orderby;
+
+	return $sql;
+}
+
+/**
+ * d_insert
+ */
+public static function d_insert_row($tablename, $data)
+{
+	$sql = static::d_insert_sql($tablename, $data);
+	return static::d_insert($sql);
+}
+public static function d_insert_sql($tablename, $data)
+{
+	$sql = 'INSERT INTO `'.static::DB_D_PREFIX.$tablename.'` ';
+	$f = [];
+	$v = [];
+	foreach($data as $i=>$j) {
+		$f[] = '`'.$i.'`';
+		$v[] = !is_null($j) ?'"'.addslashes($j).'"' :'NULL';
+	}
+	$sql .= '('.implode(', ', $f).') VALUES ('.implode(', ', $v).')';
+
+	return $sql;
+}
+
+/**
+ * d_delete
+ */
+public static function d_delete_row($tablename, $params)
+{
+	$sql = static::d_delete_sql($tablename, $params);
+	return static::d_delete($sql);
+}
+public static function d_delete_sql($tablename, $params)
+{
+	$sql = 'DELETE FROM `'.static::DB_P_PREFIX.$tablename.'` ';
+	if (is_numeric($params)) {
+		$sql .= ' WHERE `rowid`='.$params;
+	}
+	elseif (is_array($params)) {
+		$u = [];
+		foreach($params as $i=>$j) {
+			$u[] = '`'.$i.'`'. (!is_null($j) ?'="'.addslashes($j).'"' :'IS NULL');
+		}
+		$sql .= ' WHERE ('.implode(' AND ', $u).')';
+	}
+	else { // NADA par défaut
+		$sql .= ' WHERE FALSE';
+	}
+
+	return $sql;
+}
+
+/**
+ * d_update
+ */
+public static function d_update_row($tablename, $data, $params, $data_orig=[])
+{
+	// Limit to updated data
+	if (!empty($data_orig)) {
+		foreach($data as $i=>$j) {
+			if($j===$data_orig[$i] || (is_numeric($j) && is_numeric($data_orig[$i]) && $j==$data_orig[$i])) {
+				unset($data[$i]);
+				unset($data_orig[$i]);
+			}
+		}
+		// Remove useless orig data
+		foreach($data_orig as $i=>&$j)
+			if (!array_key_exists($i, $data))
+				unset($data_orig[$i]);
+	}
+
+	if (empty($data))
+		return;
+
+	$sql = static::d_update_sql($tablename, $data, $params);
+	if ($r = static::d_update($sql)) {
+		$keys = array_keys($params);
+		$key = array_pop($keys);
+		$oid = $params[$key];
+		$sql = 'INSERT INTO `_d_log`
+			(`tid`, `oid`, `rev`, `action`, `fields`, `details`)
+			SELECT `id`, "'.$oid.'", "0", "u", "'.addslashes(json_encode(array_keys($data))).'", "'.addslashes(json_encode($data)).'"
+			FROM `_d_tables`
+			WHERE `ref`="'.$tablename.'"';
+		//echo $sql;
+		static::o_insert($sql);
+	}
+	return $r;
+}
+public static function d_update_sql($tablename, $data, $params)
+{
+	$sql = 'UPDATE `'.static::DB_D_PREFIX.$tablename.'` ';
+	$u = [];
+	foreach($data as $i=>$j) {
+		$u[] = '`'.$i.'`='.(!is_null($j) ?'"'.addslashes($j).'"' :'NULL');
+	}
+	$w = [];
+	foreach($params as $i=>$j) {
+		$w[] = '`'.$i.'`'.(!is_null($j) ?'="'.addslashes($j).'"' :' IS NULL');
+	}
+	$sql .= 'SET '.implode(', ', $u).' WHERE '.implode(' AND ', $w);
+
+	return $sql;
+}
+
+// -- P QUERY SQL --
+
+public static function p_query($sql)
+{
+	global $db_p;
+
+	$q = $db_p->query($sql);
+
+	echo '<p>'.htmlentities($sql).' : '.$db_p->affected_rows.' / '.$db_p->insert_id.' / '.$db_p->error.'</p>';
+	return $q;
+}
+public static function p_select($sql)
+{
+	global $db_p;
+
+	$q = $db_p->query($sql);
+
+	echo '<p>'.htmlentities($sql).' : '.$q->num_rows.' / '.$db_p->error.'</p>';
+	return $q;
+}
+public static function p_count($sql)
+{
+	global $db_p;
+	$q = $db_p->query($sql);
+	echo '<p>'.htmlentities($sql).' : '.$q->num_rows.' / '.$db_p->error.'</p>';
+	list($nb) = $q->fetch_row();
+	return $nb;
+}
+public static function p_insert($sql)
+{
+	global $db_p;
+
+	$q = $db_p->query($sql);
+
+	echo '<p>'.htmlentities($sql).' : '.$db_p->insert_id.' / '.$db_p->error.'</p>';
+	if ($q)
+		return $db_p->insert_id;
+}
+public static function p_update($sql)
+{
+	global $db_p;
+
+	$q = $db_p->query($sql);
+
+	echo '<p>'.htmlentities($sql).' : '.$db_p->affected_rows.' / '.$db_p->error.'</p>';
+	if ($q)
+		return $db_p->affected_rows;
+}
+public static function p_delete($sql)
+{
+	global $db_p;
+
+	$q = $db_p->query($sql);
+
+	echo '<p>'.htmlentities($sql).' : '.$db_p->affected_rows.' / '.$db_p->error.'</p>';
+	if ($q)
+		return $db_p->affected_rows;
+}
+
+/**
+ * p_get
+ */
+public static function p_get_rows($tablename, $params)
+{
+	$sql = static::p_get_sql($tablename, $params);
+	$q = static::p_query($sql);
+
+	$r = [];
+	while ($row=$q->fetch_assoc())
+		$r[] = $row;
+	return $r;
+}
+public static function p_get_row($tablename, $params, $orderby=NULL)
+{
+	$sql = static::p_get_sql($tablename, $params, $orderby).' LIMIT 1';
+	$q = static::p_query($sql);
+	return $q->fetch_assoc();
+}
+public static function p_get_sql($tablename, $params, $orderby=NULL)
+{
+	$sql = 'SELECT *
+		FROM `'.static::DB_P_PREFIX.$tablename.'`
+		WHERE ';
+	if (is_numeric($params)) {
+		$sql .= 'id='.$params;
+	}
+	elseif (is_array($params)) {
+		$sql_params = [];
+		foreach($params as $i=>$j)
+			$sql_params[] = '`'.$i.'`="'.$j.'"';
+		$sql .= implode(' AND ', $sql_params);
+	}
+	elseif (is_string($params)) {
+		$sql .= $params;
+	}
+	
+	if ($orderby)
+		$sql .= ' ORDER BY '.$orderby;
+
+	return $sql;
+}
+
+/**
+ * p_insert
+ */
+public static function p_insert_row($tablename, $data)
+{
+	$sql = static::p_insert_sql($tablename, $data);
+	return static::p_insert($sql);
+}
+public static function p_insert_sql($tablename, $data)
+{
+	$sql = 'INSERT INTO `'.static::DB_P_PREFIX.$tablename.'` ';
+	$f = [];
+	$v = [];
+	foreach($data as $i=>$j) {
+		$f[] = '`'.$i.'`';
+		$v[] = !is_null($j) ?'"'.addslashes($j).'"' :'NULL';
+	}
+	$sql .= '('.implode(', ', $f).') VALUES ('.implode(', ', $v).')';
+
+	return $sql;
+}
+
+/**
+ * p_delete
+ */
+public static function p_delete_row($tablename, $params)
+{
+	$sql = static::p_delete_sql($tablename, $params);
+	return static::p_delete($sql);
+}
+public static function p_delete_sql($tablename, $params)
+{
+	$sql = 'DELETE FROM `'.static::DB_P_PREFIX.$tablename.'` ';
+	if (is_array($params)) {
+		$u = [];
+		foreach($params as $i=>$j) {
+			$u[] = '`'.$i.'`'. (!is_null($j) ?'="'.addslashes($j).'"' :'IS NULL');
+		}
+		$sql .= ' WHERE ('.implode(' AND ', $u).')';
+	}
+	else { // NADA par défaut
+		$sql .= ' WHERE FALSE';
+	}
+
+	return $sql;
+}
+
+/**
+ * p_update
+ */
+public static function p_update_row($tablename, $data, $params, $data_orig=[])
+{
+	// Limit to updated data
+	if (!empty($data_orig)) {
+		foreach($data as $i=>&$j) {
+			if($j===$data_orig[$i] || (is_numeric($j) && is_numeric($data_orig[$i]) && $j==$data_orig[$i])) {
+				unset($data[$i]);
+				unset($data_orig[$i]);
+			}
+		}
+		// Remove useless orig data
+		foreach($data_orig as $i=>&$j)
+			if (!array_key_exists($i, $data))
+				unset($data_orig[$i]);
+	}
+
+	if (empty($data))
+		return;
+
+	//@todo : encore $data and $data_orig with int key using $fields
+	$sql = static::p_update_sql($tablename, $data, $params);
+	if ($r = static::p_update($sql)) {
+		$keys = array_keys($params);
+		$key = array_pop($keys);
+		$oid = $params[$key];
+		$sql = 'INSERT INTO `_p_log`
+			(`tid`, `oid`, `rev`, `action`, `fields`, `details`, `orig`)
+			SELECT `id`, "'.$oid.'", "0", "u", "'.addslashes(json_encode(array_keys($data))).'", "'.addslashes(json_encode($data)).'", "'.addslashes(json_encode($data_orig)).'"
+			FROM `_p_tables`
+			WHERE `ref`="'.$tablename.'"';
+		//echo $sql;
+		static::o_insert($sql);
+	}
+	return $r;
+
+}
+public static function p_update_sql($tablename, $data, $params)
+{
+	$sql = 'UPDATE `'.static::DB_P_PREFIX.$tablename.'` ';
+	$u = [];
+	foreach($data as $i=>$j) {
+		$u[] = '`'.$i.'`='.(!is_null($j) ?'"'.addslashes($j).'"' :'NULL');
+	}
+	$w = [];
+	foreach($params as $i=>$j) {
+		$w[] = '`'.$i.'`'.(!is_null($j) ?'="'.addslashes($j).'"' :' IS NULL');
+	}
+	$sql .= 'SET '.implode(', ', $u).' WHERE '.implode(' AND ', $w);
+
+	return $sql;
+}
+
+}
+
+DB::__init();

+ 175 - 0
src/sync/order/common.inc.php

@@ -0,0 +1,175 @@
+<?php
+
+// Order
+
+class sync_order extends sync
+{
+
+const MODEL_NAME = 'order';
+const MODEL_ID = 15;
+const MAP_ID = 8;
+const D_TABLE_MAIN = 'commande';
+const D_TABLE_MAIN_REF = 'ref';
+const P_TABLE_MAIN = 'orders';
+const P_TABLE_MAIN_ID = 'id_order';
+
+protected static $nb;
+const D_CODE = 'CO';
+
+protected static $_d_status = [
+	//-3 => 'Pas assez de stock',
+	-1 => 'Annulé',
+	0 => 'Brouillon',
+	1 => 'Validé',
+	2 => 'Expédition en cours',
+	3 => 'Fermé',
+];
+// Retrieved from DB
+protected static $_p_order_state_map = [];
+
+protected static $d_contact_map = [
+	'shipping' => 102,
+	'invoice' => 100,
+];
+
+public static function __init()
+{
+	parent::__init();
+	
+	if (empty(static::$_p_order_state_map))
+		static::_p_order_state_map_load();
+}
+
+public static function _p_order_state_map_load()
+{
+	$sql = 'SELECT *
+		FROM llx_ps_order_state';
+	$q = DB::d_select($sql);
+	while($row=$q->fetch_assoc()) {
+		static::$_p_order_state_map[$row['id_order_state']] = $row;
+	}
+	//var_dump(static::$_p_order_state_map);
+}
+
+public static function _p_order_state_map()
+{
+	return static::$_p_order_state_map;
+}
+
+function d_data($otype, $oid)
+{
+	$data = [];
+	$data['commande'] = DB::d_get_row('commande', ['rowid'=>$oid]);
+	$data['commandedet'] = DB::d_get_rows('commandedet', ['fk_commande'=>$oid]);
+	
+	$data['commande_extrafields'] = DB::d_get_row('commande_extrafields', ['fk_object'=>$oid]);
+	
+	// Association contact
+	$data['element_contact_shipping'] = DB::d_get_row('element_contact', 'element_id='.$oid.' AND fk_c_type_contact='.static::$d_contact_map['shipping']);
+	$data['element_contact_invoice'] = DB::d_get_row('element_contact', 'element_id='.$oid.' AND fk_c_type_contact='.static::$d_contact_map['invoice']);
+
+	// association autres objets
+	$data['element_element_source'] = DB::d_get_rows('element_element', ['fk_source'=>$oid, 'sourcetype'=>'commande']);
+	$data['element_element_target'] = DB::d_get_rows('element_element', ['fk_target'=>$oid, 'targettype'=>'commande']);
+
+	$data['expedition'] = [];
+	foreach($data['element_element_source'] as $row) if ($row['targettype']=='shipping')
+		$data['expedition'][] = DB::d_get_row('expedition', ['rowid'=>$row['fk_target']]);
+
+	return $data;
+}
+
+function p_data($otype, $oid)
+{
+	$data = [];
+	$data['orders'] = DB::p_get_row('orders', ['id_order'=>$oid]);
+	if (empty($data['orders']))
+		return;
+	
+	// order_cart_rule
+	$data['order_cart_rule'] = DB::p_get_rows('order_cart_rule', ['id_order'=>$oid]);
+	
+	// Details & tax
+	$data['order_detail'] = DB::p_get_rows('order_detail', ['id_order'=>$oid]);
+	//$data['order_detail_tax'] = DB::p_get_rows('order_detail_tax', ['id_order_detail'=>$oid]);
+	if (PRODUCT_LOT)
+		$data['products_dlc_dluo_orders'] = DB::p_get_rows('products_dlc_dluo_orders', ['id_order'=>$oid]);
+
+	// Carrier
+	$data['order_carrier'] = DB::p_get_rows('order_carrier', ['id_order'=>$oid]);
+
+	// Payment
+	$data['etransactions_order'] = DB::p_get_row('etransactions_order', ['id_order'=>$oid]);
+	
+	if (empty($data['orders']))
+		return $data;
+	
+	// customer
+	$data['customer'] = DB::p_get_row('customer', ['id_customer'=>$data['orders']['id_customer']]);
+	if (empty($data['customer'])) {
+		$customer_id = $this->p_customer_insert($data);
+		$data['customer'] = DB::p_get_row('customer', ['id_customer'=>$data['orders']['id_customer']]);
+		//var_dump($data['customer']); die();
+	}
+	
+	$ref = $data['orders']['reference'];
+	$data['order_payment'] = DB::p_get_rows('order_payment', ['order_reference'=>$ref]);
+	
+	// cart
+	$data['cart'] = DB::p_get_row('cart', ['id_cart'=>$data['orders']['id_cart']]);
+
+	$data['carrier'] = DB::p_get_row('carrier', ['id_carrier'=>$data['orders']['id_carrier']]);
+
+	// invoice_addr
+	//$data['invoice_address'] = DB::p_get_row('address', ['id_address'=>$data['orders']['id_address_invoice']]);
+	// shipping_addr
+	//$data['shipping_address'] = DB::p_get_row('address', ['id_address'=>$data['orders']['id_address_delivery']]);
+	
+	// Stock mvt
+	//$data['stock_mvt'] = DB::p_get_rows('stock_mvt', ['id_order'=>$oid]);
+
+	return $data;
+}
+
+function p_customer_insert(&$p_data)
+{
+	$data = [
+		'id_customer' => $p_data['orders']['id_customer'],
+		'id_shop_group' => static::P_ID_SHOP_GROUP,
+		'id_shop' => static::P_ID_SHOP,
+		'id_gender' => 0, // 1 H 2 F
+		'id_default_group' => static::P_ID_GROUP_DEFAULT,
+		'id_lang' => static::P_ID_LANG,
+		'id_risk' => 0,
+		'company' => NULL,
+		'siret' => NULL,
+		'ape' => NULL,
+		'firstname' => 'Anonymous',
+		'lastname' => 'Deleted Recovered',
+		'email' => 'anonymous@'.P_DOMAIN,
+		'passwd' => '',
+		'last_passwd_gen' => NULL,
+		'birthday' => '0000-00-00',
+		'newsletter' => 0,
+		'ip_registration_newsletter' => NULL,
+		'newsletter_date_add' => '0000-00-00 00:00:00',
+		'optin' => 0,
+		'website' => NULL,
+		'outstanding_allow_amount' => 0,
+		'show_public_prices' => 0,
+		'max_payment_days' => 0,
+		'secure_key' => '',
+		'note' => NULL,
+		'active' => 1,
+		'is_guest' => 0,
+		'deleted' => 0,
+		'date_add' => $p_data['orders']['date_add'],
+		'date_upd' => $p_data['orders']['date_upd'],
+		'reset_password_token' => NULL,
+		'reset_password_validity' => '0000-00-00 00:00:00',
+	];
+	
+	return DB::p_insert_row('customer', $data);
+}
+
+}

+ 246 - 0
src/sync/order/dp.inc.php

@@ -0,0 +1,246 @@
+<?php
+
+class sync_order_dp extends sync_order
+{
+
+// -- ACTIONS --
+
+public function execute_osync($otype, $oid)
+{
+	return $this->dp_execute_osync($otype, $oid);
+}
+
+public function execute_create($otype, $oid)
+{
+	return $this->create($otype, $oid);
+}
+
+// -- SUB ACTIONS --
+
+public function create($otype, $oid)
+{
+	return $this->dp_create($otype, $oid);
+}
+
+public function osync($o)
+{
+	return $this->dp_osync($o);
+}
+
+public function dp_insert_more(&$o, &$p_data_map, &$d_data)
+{
+	// identique update_more...
+}
+
+public function dp_update_more(&$o, &$p_data_map, &$p_data_map_create, &$p_data, &$d_data)
+{
+	$id = $o['p_oid'];
+	
+	// Sous-totaux
+	$orders = [
+		// Products
+		'total_products' => 0,
+		'total_products_wt' => 0,
+		// Discounts
+		'total_discounts' => 0,
+		'total_discounts_tax_excl' => 0,
+		'total_discounts_tax_incl' => 0,
+		// Shipping
+		'total_shipping' => 0,
+		'total_shipping_tax_excl' => 0,
+		'total_shipping_tax_incl' => 0,
+	];
+
+	// Product details
+	if (!empty($d_data['commandedet'])) foreach($d_data['commandedet'] as $row) {
+		// Shipping detail
+		$product_row = DB::d_get_row('product', ['rowid'=>$row['fk_product']]);
+		
+		if ($product_row['ref']=='PrestaShipping' || $product_row['label']=='PrestaShipping') {
+			$orders['total_shipping'] += $row['total_ttc'];
+			$orders['total_shipping_tax_excl'] += $row['total_ht'];
+			$orders['total_shipping_tax_incl'] += $row['total_ttc'];
+		}
+		// Discount detail
+		elseif ($product_row['ref']=='PrestaDiscount' || $product_row['label']=='PrestaDiscount') {
+			$orders['total_discounts'] += -$row['total_ttc'];
+			$orders['total_discounts_tax_excl'] += -$row['total_ht'];
+			$orders['total_discounts_tax_incl'] += -$row['total_ttc'];
+			// Synchro ligne
+			static::_action('order_detail', 'dp', 'osync', 'commandedet', $row['rowid']);
+		}
+		// Produit/Service "normal"
+		else {
+			$orders['total_products'] += $row['total_ht'];
+			$orders['total_products_wt'] += $row['total_ttc'];
+			// Synchro ligne
+			static::_action('order_detail', 'dp', 'osync', 'commandedet', $row['rowid']);
+		}
+	}
+
+	foreach($orders as $i=>$j)
+		$orders[$i] = round($j, 5);
+
+	//var_dump($orders);
+	DB::p_update_row('orders', $orders, ['id_order'=>$id], $p_data['orders']);
+
+	// Log state modif
+	if (!empty($p_data_map['orders']['current_state']) && $p_data_map['orders']['current_state']!=$p_data['orders']['current_state']) {
+		// @todo log state modif
+		DB::p_insert_row('order_history', ['id_order'=>$id, 'id_employee'=>0, 'id_order_state'=>$p_data_map['orders']['current_state'], 'date_add'=>static::$datetime]);
+	}
+
+}
+
+
+// -- MAPPING --
+
+public function map_update(&$o, &$d_data, &$p_data=[])
+{
+	$p_data_map = [
+		'orders' => [
+			//'reference' => substr($d_data['commande']['ref'], 0, 9),
+
+			'id_customer' => static::_dp_map_fk_customer($d_data),
+			'id_address_delivery' => static::_dp_map_fk_address_delivery($d_data),
+			'id_address_invoice' => static::_dp_map_fk_address_invoice($d_data),
+			
+			'total_paid' => $d_data['commande']['total_ttc'],
+			'total_paid_tax_incl' => $d_data['commande']['total_ttc'],
+			'total_paid_tax_excl' => $d_data['commande']['total_ht'],
+			
+			'date_upd' => $d_data['commande']['tms'],
+		],
+	];
+
+
+	if (is_numeric($current_state = static::_dp_map_state($d_data)))
+		$p_data_map['orders']['current_state'] = $current_state;
+
+	return $p_data_map;
+}
+
+public function map_create(&$o, &$d_data)
+{
+	$p_data_map = [
+		'orders' => [
+			//'id_order' => '',
+			'reference' => $d_data['commande']['ref'],
+			'id_shop_group' => static::P_ID_SHOP_GROUP,
+			'id_shop' => static::P_ID_SHOP,
+			'id_carrier' => 0, // @todo static::P_ID_CARRIER_DEFAULT,
+			'id_lang' => static::P_ID_LANG,
+			'id_customer' => static::_dp_map_fk_customer($d_data),
+			'id_cart' => 0, // @todo créer un panier
+			'id_currency' => static::P_ID_CURRENCY,
+			'id_address_delivery' => static::_dp_map_fk_address_delivery($d_data),
+			'id_address_invoice' => static::_dp_map_fk_address_invoice($d_data),
+			'current_state' => static::_dp_map_state($d_data),
+			'secure_key' => '',
+			'payment' => '',
+			'conversion_rate' => 1,
+			'module' => static::_dp_map_module($d_data),
+			'recyclable' => 0,
+			'gift' => 0,
+			'gift_message' => '',
+			'mobile_theme' => 0,
+			'shipping_number' => '', //@todo
+			'total_discounts' => 0, //@todo
+			'total_discounts_tax_incl' => 0,
+			'total_discounts_tax_excl' => 0,
+			'total_paid' => $d_data['commande']['total_ttc'],
+			'total_paid_tax_incl' => $d_data['commande']['total_ttc'],
+			'total_paid_tax_excl' => $d_data['commande']['total_ht'],
+			'total_paid_real' => 0, // ?
+			'total_products' => 0,
+			'total_products_wt' => 0,
+			'total_shipping' => 0, //@todo
+			'total_shipping_tax_incl' => 0,
+			'total_shipping_tax_excl' => 0,
+			'carrier_tax_rate' => 20, // @todo calcul
+			'total_wrapping' => 0, // @todo ?
+			'total_wrapping_tax_incl' => 0,
+			'total_wrapping_tax_excl' => 0,
+			'round_mode' => 2,
+			'round_type' => 2,
+			'invoice_number' => 0, // @todo invoice
+			'delivery_number' => 0, // @todo delivery
+			'invoice_date' => '0000-00-00 00:00:00',
+			'delivery_date' => '0000-00-00 00:00:00',
+			'valid' => 1, //@todo ?
+			'date_add' => $d_data['commande']['date_creation'],
+			'date_upd' => $d_data['commande']['tms'],
+		],
+	];
+
+	return $p_data_map;
+}
+
+// -----------
+
+// Field MAPPING
+
+public static function _dp_map_state(&$orig_data, &$dest_tree=[])
+{
+	switch($orig_data['commande']['fk_statut']) {
+		//'Pas assez de stock',
+		case -3:
+			return 9;
+		// Annulé
+		case -1:
+			return 6;
+		// 'Brouillon' => on laisse, trop de choix possibles... @todo : ou bien mettre 'En attente de traitement'
+		//case 0:
+		// Validé => on sait pas à quoi ça correspond
+		//case 1:
+		// Expédition en cours
+		case 2:
+			return 3;
+		// Fermé
+		case 3:
+			return 5;
+	}
+}
+
+public static function _dp_map_payment(&$orig_data, &$dest_tree=[])
+{
+	// @todo
+	return 'E-Transactions';
+}
+
+public static function _dp_map_module(&$orig_data, &$dest_tree=[])
+{
+	// @todo
+	return 'etransactions';
+}
+
+public static function _dp_map_fk_customer(&$orig_data, &$dest_tree=[])
+{
+	return static::_dp_map_fk('customer', 'societe', $orig_data['commande']['fk_soc']);
+}
+
+public static function _dp_map_fk_address_delivery(&$orig_data, &$dest_tree=[])
+{
+	if (!empty($orig_data['element_contact_shipping'])) {
+		// address
+		if (!($o = static::d_o_oid('address', 'socpeople', $orig_data['element_contact_shipping']['fk_socpeople']))) {
+			$o = static::_action('address', 'dp', 'create', 'socpeople', $orig_data['element_contact_shipping']['fk_socpeople']);
+		}
+		return $o['p_oid'];
+	}
+}
+
+public static function _dp_map_fk_address_invoice(&$orig_data, &$dest_tree=[])
+{
+	if (!empty($orig_data['element_contact_invoice'])) {
+		// address
+		if (!($o = static::d_o_oid('address', 'socpeople', $orig_data['element_contact_invoice']['fk_socpeople']))) {
+			$o = static::_action('address', 'dp', 'create', 'socpeople', $orig_data['element_contact_invoice']['fk_socpeople']);
+		}
+		return $o['p_oid'];
+	}
+}
+
+}
+
+sync_order_dp::__init();

+ 558 - 0
src/sync/order/pd.inc.php

@@ -0,0 +1,558 @@
+<?php
+
+class sync_order_pd extends sync_order
+{
+
+// -- ACTIONS --
+
+public function execute_osync($otype, $oid)
+{
+	return $this->pd_execute_osync($otype, $oid);
+}
+
+public function execute_create($otype, $oid)
+{
+	return $this->create($otype, $oid);
+}
+
+// -- SUB ACTIONS --
+
+public function create($otype, $oid)
+{
+	return $this->pd_create($otype, $oid);
+}
+
+public function osync($o)
+{
+	return $this->pd_osync($o);
+}
+
+public function pd_insert_more(&$o, &$d_data, &$p_data)
+{
+	$id = $o['d_oid'];
+	
+	// Expédition à créer pour le picking
+	$this->pd_expedition_create($id, $d_data, $p_data);
+}
+
+public function pd_update_more(&$o, &$d_data_map, &$d_data_map_create, &$d_data, &$p_data)
+{
+	$id = $o['d_oid'];
+	
+	// commande_extrafields
+	if (!empty($d_data['commande_extrafields']))
+		DB::d_update_row('commande_extrafields', $d_data_map['commande_extrafields'], ['rowid'=>$d_data['commande_extrafields']['rowid']], $d_data['commande_extrafields']);
+	else
+		DB::d_insert_row('commande_extrafields', array_merge($d_data_map_create['commande_extrafields'], $d_data_map['commande_extrafields'], ['fk_object'=>$id]));
+
+	// Product details
+	if (!empty($p_data['order_detail'])) foreach($p_data['order_detail'] as $row) {
+		static::_action('order_detail', 'pd', 'osync', 'order_detail', $row['id_order_detail']);
+	}
+
+	// Discount details
+	if (!empty($p_data['order_cart_rule'])) foreach($p_data['order_cart_rule'] as $row) {
+		static::_action('order_detail', 'pd', 'osync', 'order_cart_rule', $row['id_order_cart_rule']);
+	}
+
+	// Shipping row
+	if ($row = $this->shipping_row($p_data, $id)) {
+		$sok = false;
+		// Recherche une ligne livraison correspondante
+		if (!empty($d_data['commandedet'])) foreach($d_data['commandedet'] as $crow) {
+			if ($crow['fk_product']==static::D_K_SHIPPING_PRODUCT) {
+				// Update existante
+				DB::d_update_row('commandedet', $row, ['rowid'=>$crow['rowid']], $crow);
+				$sok = true;
+				break;
+			}
+		}
+		// Création si pas trouvé
+		if (!$sok)
+			DB::d_insert_row('commandedet', $row);
+	}
+	
+	// invoice
+	if (!empty($d_data['element_contact_invoice']))
+		DB::d_update_row('element_contact', $d_data_map['element_contact_invoice'], ['rowid'=>$d_data['element_contact_invoice']['rowid']], $d_data['element_contact_invoice']);
+	else
+		DB::d_insert_row('element_contact', array_merge($d_data_map_create['element_contact_invoice'], $d_data_map['element_contact_invoice'], ['element_id'=>$id]));
+	
+	// shipping
+	if (!empty($d_data['element_contact_shipping']))
+		DB::d_update_row('element_contact', $d_data_map['element_contact_shipping'], ['rowid'=>$d_data['element_contact_shipping']['rowid']], $d_data['element_contact_shipping']);
+	else
+		DB::d_insert_row('element_contact', array_merge($d_data_map_create['element_contact_shipping'], $d_data_map['element_contact_shipping'], ['element_id'=>$id]));
+}
+
+public function pd_expedition_create($id, $d_data, $p_data)
+{
+	return;
+	
+	// expedition
+	$data = $this->pd_expedition_insert_map($id, $d_data, $p_data);
+	//$e_id = DB::d_insert_row('expedition', $row);
+
+	// expedition_extrafields
+	// expeditiondet
+	// expeditiondet_extrafields
+	// expeditiondet_batch
+
+}
+public function pd_expedition_insert_map($id, $d_data, $p_data)
+{
+	//
+	$row = [
+		//'rowid' => NULL,
+		'tms' => static::$datetime,
+		'ref' => '', // @todo
+		'entity' => 1,
+		'fk_soc' => $d_data['commande']['fk_soc'],
+		'fk_projet' => 0,
+		'ref_ext' => NULL,
+		'ref_int' => NULL,
+		'ref_customer' => NULL,
+		'date_creation' => static::$datetime,
+		'fk_user_author' => static::D_K_USER,
+		'fk_user_modif' => NULL,
+		'date_valid' => NULL,
+		'fk_user_valid' => NULL,
+		'date_delivery' => NULL,
+		'date_expedition' => NULL,
+		'fk_address' => '',
+		'fk_shipping_method' => '',
+		'tracking_number' => '',
+		'fk_statut' => '',
+		'billed' => '',
+		'height' => '',
+		'width' => '',
+		'size_units' => '',
+		'size' => '',
+		'weight_units' => '',
+		'weight' => '',
+		'note_private' => '',
+		'note_public' => '',
+		'model_pdf' => '',
+		'last_main_doc' => '',
+		'fk_incoterms' => '',
+		'location_incoterms' => '',
+		'import_key' => '',
+		'extraparams' => '',
+	];
+
+	return $row;
+}
+public function pd_expedition_update_map($id, $d_data, $p_data)
+{
+	//
+	$row = [
+		'tms' => static::$datetime,
+		'fk_user_modif' => static::D_K_USER,
+	];
+
+	return $row;
+}
+
+public function shipping_row(&$p_data, $fk_order)
+{
+	if (empty($p_data['orders']['total_shipping_tax_incl']) || $p_data['orders']['total_shipping_tax_incl']==0)
+		return;
+
+	// @todo
+	$p_data['order_carrier'];
+	var_dump($p_data['orders']['total_shipping_tax_excl']);
+	var_dump($p_data['orders']['total_shipping_tax_excl']!=0);
+	$tva_tx = (isset($p_data['orders']['total_shipping_tax_excl']) && $p_data['orders']['total_shipping_tax_excl']!=0 ?round(100*($p_data['orders']['total_shipping_tax_incl']/$p_data['orders']['total_shipping_tax_excl']-1), 2) :0);
+	
+	// @todo: fk_product_fournisseur_price
+	
+	$row = [
+		//'rowid' => NULL,
+		'fk_commande' => $fk_order,
+		'fk_parent_line' => NULL,
+		'fk_product' => static::D_K_SHIPPING_PRODUCT,
+		'label' => static::D_SHIPPING_PRODUCT_NAME,
+		'description' => '',
+		'vat_src_code' => '',
+		'tva_tx' => $tva_tx,
+		'localtax1_tx' => 0,
+		'localtax1_type' => 0,
+		'localtax2_tx' => 0,
+		'localtax2_type' => 0,
+		'qty' => 1,
+		'remise_percent' => 0,
+		'remise' => 0,
+		'fk_remise_except' => NULL,
+		'price' => $p_data['orders']['total_shipping_tax_excl'],
+		'subprice' => $p_data['orders']['total_shipping_tax_excl'],
+		'total_ht' => $p_data['orders']['total_shipping_tax_excl'],
+		'total_tva' => ($p_data['orders']['total_shipping_tax_incl']-$p_data['orders']['total_shipping_tax_excl']),
+		'total_localtax1' => 0,
+		'total_localtax2' => 0,
+		'total_ttc' => $p_data['orders']['total_shipping_tax_incl'],
+		'product_type' => 1,
+		'date_start' => NULL,
+		'date_end' => NULL,
+		'info_bits' => 0,
+		'buy_price_ht' => $p_data['orders']['total_shipping_tax_excl'],
+		'fk_product_fournisseur_price' => NULL, // @todo
+		'special_code' => 0,
+		'rang' => 0,
+		'fk_unit' => NULL,
+		'import_key' => NULL,
+		'fk_commandefourndet' => NULL,
+		'fk_multicurrency' => NULL,
+		'multicurrency_code' => 'EUR',
+		'multicurrency_subprice' => $p_data['orders']['total_shipping_tax_excl'],
+		'multicurrency_total_ht' => $p_data['orders']['total_shipping_tax_excl'],
+		'multicurrency_total_tva' => ($p_data['orders']['total_shipping_tax_incl']-$p_data['orders']['total_shipping_tax_excl']),
+		'multicurrency_total_ttc' => $p_data['orders']['total_shipping_tax_incl'],
+		'ref_ext' => NULL,
+	];
+	
+	return $row;
+}
+
+public function discount_rows(&$p_data, $fk_order)
+{
+	if (!isset($p_data['orders']['total_discounts_tax_incl']) || $p_data['orders']['total_discounts_tax_incl']==0)
+		return;
+	
+	$lines = [];
+	foreach($p_data['order_cart_rule'] as $row) {
+		$tva = ($row['value']-$row['value_tax_excl']);
+		$tva_tx = ($row['value_tax_excl']!=0 ?round(100*$tva/$row['value_tax_excl'], 2) :0);
+
+		$lines[] = [
+			//'rowid' => NULL,
+			'fk_commande' => $fk_order,
+			'fk_parent_line' => NULL,
+			'fk_product' => static::D_K_DISCOUNT_PRODUCT,
+			'label' => static::D_DISCOUNT_PRODUCT_NAME,
+			'description' => $row['name'],
+			'vat_src_code' => '',
+			'tva_tx' => $tva_tx, // @todo
+			'localtax1_tx' => 0,
+			'localtax1_type' => 0,
+			'localtax2_tx' => 0,
+			'localtax2_type' => 0,
+			'qty' => 1,
+			'remise_percent' => 0,
+			'remise' => 0,
+			'fk_remise_except' => NULL,
+			'price' => $row['value_tax_excl'],
+			'subprice' => $row['value_tax_excl'],
+			'total_ht' => -$row['value_tax_excl'],
+			'total_tva' => -$tva,
+			'total_localtax1' => 0,
+			'total_localtax2' => 0,
+			'total_ttc' => -$row['value'],
+			'product_type' => 1,
+			'date_start' => NULL,
+			'date_end' => NULL,
+			'info_bits' => 0,
+			'buy_price_ht' => 0,
+			'fk_product_fournisseur_price' => NULL, // @todo
+			'special_code' => 0,
+			'rang' => 0,
+			'fk_unit' => NULL,
+			'import_key' => NULL,
+			'fk_commandefourndet' => NULL,
+			'fk_multicurrency' => NULL,
+			'multicurrency_code' => 'EUR',
+			'multicurrency_subprice' => $row['value_tax_excl'],
+			'multicurrency_total_ht' => -$row['value_tax_excl'],
+			'multicurrency_total_tva' => -$tva,
+			'multicurrency_total_ttc' => -$row['value'],
+			'ref_ext' => NULL,
+		];
+	}
+	return $lines;
+}
+
+// -- MAPPING --
+
+public function map_update(&$o, &$p_data, &$d_data=[])
+{
+	// Sous condition expédié/livré, on bloque les modifications P=>D
+	if (false) if (!empty($d_data['commande']) && in_array($d_data['commande']['fk_statut'], [1, 2, 3]))
+		return [
+			'commande_extrafields' => [
+				'p_state' => $p_data['orders']['current_state'],
+				'p_ref' => $p_data['orders']['reference'],
+			],
+		];
+	
+	//$id = $o['d_oid'];
+	
+	$d_data_map = [
+		'commande' => [
+			//'ref' => $ref,
+
+			'total_ht' => $p_data['orders']['total_paid_tax_excl'],
+			'total_tva' => ($p_data['orders']['total_paid_tax_incl']-$p_data['orders']['total_paid_tax_excl']),
+			'total_ttc' => $p_data['orders']['total_paid_tax_incl'],
+			'multicurrency_total_ht' => $p_data['orders']['total_paid_tax_excl'], // @todo
+			'multicurrency_total_tva' => ($p_data['orders']['total_paid_tax_incl']-$p_data['orders']['total_paid_tax_excl']), // @todo
+			'multicurrency_total_ttc' => $p_data['orders']['total_paid_tax_incl'], // @todo
+			
+			//'date_commande' => substr(($p_data['orders']['invoice_date']&&$p_data['orders']['invoice_date']!='0000-00-00 00:00:00' ?$p_data['orders']['invoice_date'] :$p_data['orders']['date_add']), 0, 10),
+			
+			//'fk_statut' => static::_pd_map_fk_status($p_data),
+			'fk_mode_reglement' => static::_pd_map_fk_mode_reglement($p_data),
+			'fk_shipping_method' => static::_pd_map_fk_shipping_method($p_data),
+
+			'fk_user_modif' => static::D_K_USER,
+			'tms' => $p_data['orders']['date_upd'],
+		],
+		'commande_extrafields' => [
+			'p_state' => $p_data['orders']['current_state'],
+			'p_ref' => $p_data['orders']['reference'],
+		],
+		'element_contact_shipping' => [
+			//'datecreate' => static::$datetime,
+			//'statut' => 4,
+			//'element_id' => $id,
+			//'fk_c_type_contact' => static::$d_contact_map['shipping'],
+			'fk_socpeople' => static::_pd_map_fk_shipping_addr($p_data),
+		],
+		'element_contact_invoice' => [
+			//'datecreate' => static::$datetime,
+			//'statut' => 4,
+			//'element_id' => $id,
+			//'fk_c_type_contact' => static::$d_contact_map['invoice'],
+			'fk_socpeople' => static::_pd_map_fk_invoice_addr($p_data),
+		],
+	];
+
+	// Uniquement les status pris en charge !
+	if (is_numeric($fk_statut=static::_pd_map_fk_status($p_data))) {
+		// Expédié sans expédition => on laisse
+		if ($fk_statut>=2 && empty($d_data['expedition']) && (empty($d_data['commande']['fk_statut']) || $d_data['commande']['fk_statut']<=1))
+			$d_data_map['commande']['fk_statut'] = 1;
+		else
+			$d_data_map['commande']['fk_statut'] = $fk_statut;
+	}
+	
+	return $d_data_map;
+}
+
+public function map_create(&$o, &$p_data)
+{
+	//$id = $o['d_oid'];
+
+	$date_commande = substr(($p_data['orders']['invoice_date']&&$p_data['orders']['invoice_date']!='0000-00-00 00:00:00' ?$p_data['orders']['invoice_date'] :$p_data['orders']['date_add']), 0, 10);
+	$ref = static::d_ref($date_commande);
+		
+	$d_data_map = [
+		'commande' => [
+			//'rowid' => NULL,
+			'ref' => $ref,
+			'entity' => 1,
+			'ref_ext' => NULL,
+			'ref_int' => NULL,
+			'ref_client' => '', // @todo ?? kesako ? ref
+			'fk_soc' => static::_pd_map_fk_customer($p_data),
+			'fk_projet' => NULL,
+			'tms' => $p_data['orders']['date_upd'],
+			'date_creation' => $p_data['orders']['date_add'],
+			'date_valid' => NULL,
+			'date_cloture' => NULL,
+			'date_commande' => $date_commande,
+			'fk_user_author' => static::D_K_USER,
+			'fk_user_modif' => static::D_K_USER,
+			'fk_user_valid' => NULL,
+			'fk_user_cloture' => NULL,
+			'source' => NULL,
+			'fk_statut' => static::_pd_map_fk_status($p_data),
+			'amount_ht' => '0',
+			'remise_percent' => '0',
+			'remise_absolue' => NULL,
+			'remise' => '0', // @todo
+			'total_tva' => ($p_data['orders']['total_paid_tax_incl']-$p_data['orders']['total_paid_tax_excl']),
+			'localtax1' => '0',
+			'localtax2' => '0',
+			'total_ht' => $p_data['orders']['total_paid_tax_excl'],
+			'total_ttc' => $p_data['orders']['total_paid_tax_incl'],
+			'note_private' => NULL,
+			'note_public' => NULL,
+			'model_pdf' => NULL,
+			'last_main_doc' => '',
+			'module_source' => NULL,
+			'pos_source' => NULL,
+			'facture' => '0', // 1 = facture ?
+			'fk_account' => NULL,
+			'fk_currency' => NULL,
+			'fk_cond_reglement' => NULL,
+			'fk_mode_reglement' => static::_pd_map_fk_mode_reglement($p_data),
+			'date_livraison' => NULL,
+			'fk_shipping_method' => static::_pd_map_fk_shipping_method($p_data),
+			'fk_warehouse' => NULL,
+			'fk_availability' => NULL,
+			'fk_input_reason' => 1, // @todo kesako ?
+			'fk_delivery_address' => NULL, // @todo
+			'fk_incoterms' => 0,
+			'location_incoterms' => '',
+			'import_key' => NULL,
+			'extraparams' => NULL,
+			'fk_multicurrency' => 0,
+			'multicurrency_code' => 'EUR',
+			'multicurrency_tx' => '1',
+			'multicurrency_total_ht' => $p_data['orders']['total_paid_tax_excl'], // @todo
+			'multicurrency_total_tva' => ($p_data['orders']['total_paid_tax_incl']-$p_data['orders']['total_paid_tax_excl']), // @todo
+			'multicurrency_total_ttc' => $p_data['orders']['total_paid_tax_incl'], // @todo
+			//'total_tva ' => '', // @todo
+		],
+		'commande_extrafields' => [
+			'p_state' => $p_data['orders']['current_state'],
+			'p_ref' => $p_data['orders']['reference'],
+		],
+		'element_contact_shipping' => [
+			'datecreate' => static::$datetime,
+			'statut' => 4,
+			//'element_id' => $id,
+			'fk_c_type_contact' => static::$d_contact_map['shipping'],
+			'fk_socpeople' => static::_pd_map_fk_shipping_addr($p_data),
+		],
+		'element_contact_invoice' => [
+			'datecreate' => static::$datetime,
+			'statut' => 4,
+			//'element_id' => $id,
+			'fk_c_type_contact' => static::$d_contact_map['invoice'],
+			'fk_socpeople' => static::_pd_map_fk_invoice_addr($p_data),
+		],
+	];
+	
+	// 
+	if (empty($d_data_map['commande']['fk_soc'])) {
+		// Pas de id_customer => Réaffectation
+		if (empty($p_data['orders']['id_customer'])) {
+		}
+		// id_customer mais pas de customer => recréation customer avec données par défaut
+		elseif (empty($p_data['customer'])) {
+			$customer_data = [
+				'id_customer' => $p_data['orders']['id_customer'],
+				'',
+			];
+			//$q = DB::p_insert_row('customer', $customer_data);
+		}
+	}
+	
+	return $d_data_map;
+}
+
+// -----------
+
+// Field MAPPING
+
+public static function _pd_map_id_tax_rules_group(&$orig_data, &$dest_tree=[])
+{
+	// @todo mapper les taxes proprement !!
+	switch($orig_data['product']['tva_tx']) {
+		case '20':
+			return 1;
+		case '10':
+			return 2;
+		case '5.5':
+			return 3;
+		case '2.1':
+			return 4;
+		case '0':
+			return 5;
+	}
+}
+
+public static function _pd_map_fk_invoice_addr(&$orig_data, &$dest_tree=[])
+{
+	return static::_pd_map_fk('address', 'address', $orig_data['orders']['id_address_invoice']);
+}
+
+public static function _pd_map_fk_shipping_addr(&$orig_data, &$dest_tree=[])
+{
+	return static::_pd_map_fk('address', 'address', $orig_data['orders']['id_address_delivery']);
+}
+
+public static function _pd_map_fk_customer(&$orig_data, &$dest_tree=[])
+{
+	return static::_pd_map_fk('customer', 'customer', $orig_data['orders']['id_customer']);
+}
+
+// Presta : champs payment & module
+// Doli : champs fk_mode_reglement associé à table c_paiement
+// @todo : Attention, le mapping se fait à la mano, voir pour améliorer
+
+protected static $_modes_reglement = [
+	'cheque' => [
+		'p' => [
+			'payment' => 'Chèque',
+			'module' => 'ps_checkpayment',
+		],
+		'd' => [
+			'fk_mode_reglement' => 7,
+		],
+	],
+	'virement' => [
+		'p' => [
+			'payment' => 'Virement',
+			'module' => 'ps_wirepayment',
+		],
+		'd' => [
+			'fk_mode_reglement' => 2,
+		],
+	],
+	'etransactions' => [
+		'p' => [
+			'payment' => 'E-Transactions',
+			'module' => 'etransactions',
+		],
+		'd' => [
+			'fk_mode_reglement' => 54,
+		],
+	],
+	'manuel' => [
+		'p' => [
+			'payment' => 'Commande depuis le backoffice',
+			'module' => 'bo_order',
+		],
+		'd' => [
+			'fk_mode_reglement' => NULL,
+		],
+	],
+];
+
+public static function _pd_map_fk_mode_reglement(&$orig_data, &$dest_tree=[])
+{
+	foreach(static::$_modes_reglement as $mode) {
+		if ($orig_data['orders']['module'] == $mode['p']['module'])
+			return $mode['d']['fk_mode_reglement'];
+	}
+}
+
+protected static function _pd_map_fk_shipping_method(&$orig_data, &$dest_tree=[])
+{
+	if (empty($orig_data['carrier']))
+		return;
+	// Welco
+	if($orig_data['carrier']['external_module_name']=='welco')
+		return 15;
+	// DPD
+	elseif($orig_data['carrier']['external_module_name']=='dpdfrance') {
+		// DPD predict
+		return 13;
+		// Relais pickup
+		//return 16;
+	}
+	// Retrait boutique
+	//return 9;
+}
+
+public static function _pd_map_fk_status(&$orig_data, &$dest_tree=[])
+{
+	return (isset($orig_data['orders']['current_state']) && isset(static::$_p_order_state_map[$orig_data['orders']['current_state']])
+		?static::$_p_order_state_map[$orig_data['orders']['current_state']]['statut']
+		:NULL);
+}
+
+}
+
+sync_order_pd::__init();

+ 66 - 0
src/sync/order_detail/common.inc.php

@@ -0,0 +1,66 @@
+<?php
+
+// Order
+
+class sync_order_detail extends sync
+{
+
+const MODEL_NAME = 'order_detail';
+const MODEL_ID = 16;
+const MAP_ID = 9;
+const D_TABLE_MAIN = 'commandedet';
+const P_TABLE_MAIN = 'order_detail';
+const P_TABLE_MAIN_ID = 'id_order_detail';
+
+function d_data($otype, $oid)
+{
+	$data = [];
+
+	// commandedet
+	if ($otype=='commandedet') {
+		$data['commandedet'] = DB::d_get_row('commandedet', ['rowid'=>$oid]);
+		$data['commandedet_extrafields'] = DB::d_get_row('commandedet_extrafields', ['fk_object'=>$oid]);
+		if (!empty($data['commandedet']['fk_product']))
+			$data['product'] = DB::d_get_row('product', ['rowid'=>$data['commandedet']['fk_product']]);
+	}
+
+	return $data;
+}
+
+function p_data($otype, $oid)
+{
+	$data = [];
+
+	// order_detail
+	if ($otype=='order_detail') {
+		// order_detail
+		$data['order_detail'] = DB::p_get_row('order_detail', ['id_order_detail'=>$oid]);
+		// order_detail_tax
+		$data['order_detail_tax'] = DB::p_get_row('order_detail_tax', ['id_order_detail'=>$oid]);
+		
+		$o_id = $data['order_detail']['id_order'];
+		$p_id = $data['order_detail']['product_id'];
+		$pa_id = $data['order_detail']['product_attribute_id'];
+
+		// products_dlc_dluo_orders
+		if (PRODUCT_LOT)
+			$data['products_dlc_dluo_orders'] = DB::p_get_rows('products_dlc_dluo_orders', ['id_order'=>$o_id, 'id_product'=>$p_id, 'id_combinaison'=>$pa_id]);
+
+		// product
+		$data['product'] = DB::p_get_row('product', ['id_product'=>$p_id]);
+		// product_lang
+		//$data['product_lang'] = DB::p_get_row('product_lang', ['id_product'=>$p_id, 'id_lang'=>static::P_ID_LANG]);
+		// product_attribute
+		if ($pa_id)
+			$data['product_attribute'] = DB::p_get_row('product_attribute', ['id_product_attribute'=>$pa_id]);
+	}
+	// order_cart_rule
+	elseif ($otype=='order_cart_rule') {
+		// order_cart_rule
+		$data['order_cart_rule'] = DB::p_get_row('order_cart_rule', ['id_order_cart_rule'=>$oid]);
+	}
+
+	return $data;
+}
+
+}

+ 289 - 0
src/sync/order_detail/dp.inc.php

@@ -0,0 +1,289 @@
+<?php
+
+class sync_order_detail_dp extends sync_order_detail
+{
+
+// -- ACTIONS --
+
+public function execute_osync($otype, $oid)
+{
+	return $this->dp_execute_osync($otype, $oid);
+}
+
+public function execute_create($otype, $oid)
+{
+	return $this->create($otype, $oid);
+}
+
+public function execute_delete($otype, $oid)
+{
+	return $this->dp_delete($otype, $oid);
+}
+
+// -- SUB ACTIONS --
+
+public function create($otype, $oid)
+{
+	return $this->dp_create($otype, $oid);
+}
+
+public function osync($o)
+{
+	return $this->dp_osync($o);
+}
+
+public function dp_insert_more(&$o, &$p_data_map, &$d_data)
+{
+	if ($o['p_tref']=='order_detail') {
+		static::p_insert_row('order_detail_tax', ['id_order_detail'=>$o['p_oid']], $p_data_map);
+	}
+}
+
+public function dp_update_more(&$o, &$p_data_map, &$p_data_map_create, &$p_data, &$d_data)
+{
+	if ($o['p_tref']=='order_detail') {
+		static::p_update_row('order_detail_tax', ['id_order_detail'=>$o['p_oid']], $p_data_map, $p_data_map_create, $p_data);
+	}
+}
+
+// -- MAPPING --
+
+public function map_update(&$o, &$d_data, &$p_data=[])
+{
+	if ($o['p_tref']=='order_cart_rule') {
+		$p_data_map = [
+			'order_cart_rule' => [
+				//'id_order_cart_rule' => NULL,
+				//'id_order' => static::_dp_map_fk_order($d_data),
+				'id_cart_rule' => static::P_K_DISCOUNT_CART_RULE, // 
+				//'id_order_invoice' => 0,
+				'name' => ($d_data['commandedet']['label'] ?$d_data['commandedet']['label'] :$d_data['product']['label']),
+				'value' => -$d_data['commandedet']['total_ttc'],
+				'value_tax_excl' => -$d_data['commandedet']['total_ht'],
+				'free_shipping' => 1, // si static::P_K_FREESHIPPING_CART_RULE
+			],
+		];
+
+	}
+	elseif ($o['p_tref']=='order_detail') {
+		$product = static::_dp_map_product($d_data);
+		$id_tax = static::_dp_map_id_tax_rules_group($d_data);
+
+		$p_data_map = [
+			'order_detail' => [
+				//'id_order_detail' => '',
+				//'id_order' => static::_dp_map_fk_order($d_data),
+				//'id_order_invoice' => '0',
+				//'id_warehouse' => '0',
+				//'id_shop' => static::P_ID_SHOP,
+				'product_id' => $product['id_product'],
+				'product_attribute_id' => $product['id_product_attribute'],
+				'id_customization' => '0', // @todo !
+				'product_name' => ($d_data['commandedet']['label'] ?$d_data['commandedet']['label'] :$d_data['product']['label']),
+				'product_quantity' => $d_data['commandedet']['qty'],
+				'product_quantity_in_stock' => $d_data['commandedet']['qty'], // @todo
+				//'product_quantity_refunded' => 0,
+				//'product_quantity_return' => 0,
+				//'product_quantity_reinjected' => 0,
+				'product_price' => $d_data['commandedet']['price'],
+				'reduction_percent' => $d_data['commandedet']['remise_percent'],
+				'reduction_amount' => $d_data['commandedet']['remise'],
+				'reduction_amount_tax_incl' => $d_data['commandedet']['remise'],
+				'reduction_amount_tax_excl' => $d_data['commandedet']['remise'], // @todo TVA
+				//'group_reduction' => 0,
+				//'product_quantity_discount' => 0, // @todo
+				'product_ean13' => static::_dp_map_ean13($d_data),
+				'product_isbn' => static::_dp_map_isbn($d_data),
+				'product_upc' => static::_dp_map_upc($d_data),
+				'product_reference' => $d_data['product']['ref'],
+				'product_supplier_reference' => '', // @todo $d_data['product']['ref'],
+				'product_weight' => ($d_data['product']['weight'] ?$d_data['product']['weight'] :0),
+				'id_tax_rules_group' => $id_tax,
+				'tax_computation_method' => 0,
+				//'tax_name' => '',
+				'tax_rate' => $d_data['product']['tva_tx'],
+				//'ecotax' => 0,
+				//'ecotax_tax_rate' => 0,
+				//'discount_quantity_applied' => 0,
+				//'download_hash' => '',
+				//'download_nb' => 0,
+				//'download_deadline' => '0000-00-00 00:00:00',
+				'total_price_tax_incl' => $d_data['commandedet']['total_ttc'],
+				'total_price_tax_excl' => $d_data['commandedet']['total_ht'],
+				'unit_price_tax_incl' => $d_data['commandedet']['price']*(1+$d_data['product']['tva_tx']/100),
+				'unit_price_tax_excl' => $d_data['commandedet']['price'],
+				'total_shipping_price_tax_incl' => 0, // @todo shipping product specific
+				'total_shipping_price_tax_excl' => 0,
+				'purchase_supplier_price' => $d_data['commandedet']['buy_price_ht'],
+				'original_product_price' => $d_data['commandedet']['subprice'],
+				'original_wholesale_price' => $d_data['commandedet']['buy_price_ht'],
+				//'sc_attr_infos_v1' => NULL,
+				//'sc_qc_product_price' => 0,
+			],
+			'order_detail_tax' => [
+				//'id_order_detail' => NULL,
+				'id_tax' => $id_tax,
+				'unit_amount' => $d_data['commandedet']['price']*($d_data['product']['tva_tx']/100),
+				'total_amount' => $d_data['commandedet']['total_ttc']-$d_data['commandedet']['total_ht'],
+			],
+		];
+	}
+
+	return $p_data_map;
+}
+
+public function map_create(&$o, &$d_data)
+{
+	// Discount => Cart rule
+	if (isset($d_data['commandedet']) && $d_data['commandedet']['fk_product']==static::D_K_DISCOUNT_PRODUCT) {
+		$o['p_tref'] = 'order_cart_rule';
+
+		$p_data_map = [
+			'order_cart_rule' => [
+				//'id_order_cart_rule' => NULL,
+				'id_order' => static::_dp_map_fk_order($d_data),
+				'id_cart_rule' => static::P_K_DISCOUNT_CART_RULE, // 
+				'id_order_invoice' => 0,
+				'name' => ($d_data['commandedet']['label'] ?$d_data['commandedet']['label'] :$d_data['product']['label']),
+				'value' => -$d_data['commandedet']['total_ttc'],
+				'value_tax_excl' => -$d_data['commandedet']['total_ht'],
+				'free_shipping' => 1, // si static::P_K_FREESHIPPING_CART_RULE
+			],
+		];
+	}
+	else {
+		$o['p_tref'] = 'order_detail';
+		$product = static::_dp_map_product($d_data);
+		$id_tax = static::_dp_map_id_tax_rules_group($d_data);
+
+		$p_data_map = [
+			'order_detail' => [
+				//'id_order_detail' => '',
+				'id_order' => static::_dp_map_fk_order($d_data),
+				'id_order_invoice' => '0',
+				'id_warehouse' => '0',
+				'id_shop' => static::P_ID_SHOP,
+				'product_id' => $product['id_product'],
+				'product_attribute_id' => $product['id_product_attribute'],
+				'id_customization' => '0', // @todo !
+				'product_name' => ($d_data['commandedet']['label'] ?$d_data['commandedet']['label'] :$d_data['product']['label']),
+				'product_quantity' => $d_data['commandedet']['qty'],
+				'product_quantity_in_stock' => $d_data['commandedet']['qty'], // @todo
+				'product_quantity_refunded' => 0,
+				'product_quantity_return' => 0,
+				'product_quantity_reinjected' => 0,
+				'product_price' => $d_data['commandedet']['price'],
+				'reduction_percent' => $d_data['commandedet']['remise_percent'],
+				'reduction_amount' => $d_data['commandedet']['remise'],
+				'reduction_amount_tax_incl' => $d_data['commandedet']['remise'],
+				'reduction_amount_tax_excl' => $d_data['commandedet']['remise'], // @todo TVA
+				'group_reduction' => 0,
+				'product_quantity_discount' => 0, // @todo
+				'product_ean13' => static::_dp_map_ean13($d_data),
+				'product_isbn' => static::_dp_map_isbn($d_data),
+				'product_upc' => static::_dp_map_upc($d_data),
+				'product_reference' => $d_data['product']['ref'],
+				'product_supplier_reference' => '', // @todo $d_data['product']['ref'],
+				'product_weight' => ($d_data['product']['weight'] ?$d_data['product']['weight'] :0),
+				'id_tax_rules_group' => $id_tax,
+				'tax_computation_method' => 0,
+				'tax_name' => '',
+				'tax_rate' => $d_data['product']['tva_tx'],
+				'ecotax' => 0,
+				'ecotax_tax_rate' => 0,
+				'discount_quantity_applied' => 0,
+				'download_hash' => '',
+				'download_nb' => 0,
+				'download_deadline' => '0000-00-00 00:00:00',
+				'total_price_tax_incl' => $d_data['commandedet']['total_ttc'],
+				'total_price_tax_excl' => $d_data['commandedet']['total_ht'],
+				'unit_price_tax_incl' => $d_data['commandedet']['price']*(1+$d_data['product']['tva_tx']/100),
+				'unit_price_tax_excl' => $d_data['commandedet']['price'],
+				'total_shipping_price_tax_incl' => 0, // @todo shipping product specific
+				'total_shipping_price_tax_excl' => 0,
+				'purchase_supplier_price' => $d_data['commandedet']['buy_price_ht'],
+				'original_product_price' => $d_data['commandedet']['subprice'],
+				'original_wholesale_price' => $d_data['commandedet']['buy_price_ht'],
+				'sc_attr_infos_v1' => NULL,
+				'sc_qc_product_price' => 0,
+			],
+			'order_detail_tax' => [
+				//'id_order_detail' => NULL,
+				'id_tax' => $id_tax,
+				'unit_amount' => $d_data['commandedet']['price']*($d_data['product']['tva_tx']/100),
+				'total_amount' => $d_data['commandedet']['total_ttc']-$d_data['commandedet']['total_ht'],
+			],
+		];
+	}
+
+	return $p_data_map;
+}
+
+// -----------
+
+// Field MAPPING
+
+public static function _dp_map_product(&$orig_data, &$dest_tree=[])
+{
+	$product = [];
+	// Déjà mappé
+	if ($o = static::d_o_oid('product', 'product', $orig_data['commandedet']['fk_product'])) {
+		// Combi
+		if ($o['p_tref']=='product_attribute') {
+			$row = DB::p_get_row('product_attribute', ['id_product_attribute'=>$o['p_oid']]);
+			return ['id_product'=>$row['id_product'], 'id_product_attribute'=>$o['p_oid']];
+		}
+		// Simple
+		else {
+			return ['id_product'=>$o['p_oid'], 'id_product_attribute'=>0];
+		}
+	}
+	// Création simple
+	else {
+		$o = static::_action('product', 'dp', 'create', 'product', $orig_data['commandedet']['fk_product']);
+		return ['id_product'=>$o['p_oid'], 'id_product_attribute'=>0];
+	}
+}
+
+public static function _dp_map_fk_order(&$orig_data, &$dest_tree=[])
+{
+	return static::_dp_map_fk('order', 'commande', $orig_data['commandedet']['fk_commande']);
+}
+
+public static function _dp_map_id_tax_rules_group(&$orig_data, &$dest_tree=[])
+{
+	// @todo mapper les taxes proprement !!
+	switch($orig_data['product']['tva_tx']) {
+		case '20':
+			return 1;
+		case '10':
+			return 2;
+		case '5.5':
+			return 3;
+		case '2.1':
+			return 4;
+		case '0':
+			return 5;
+	}
+}
+
+public static function _dp_map_ean13(&$orig_data, &$dest_tree=[])
+{
+	return ($orig_data['product']['fk_barcode_type']==2) ?substr($orig_data['product']['barcode'], 0, 13) :NULL;
+}
+public static function _dp_map_ean8(&$orig_data, &$dest_tree=[])
+{
+	return ($orig_data['product']['fk_barcode_type']==1) ?$orig_data['product']['barcode'] :NULL;
+}
+public static function _dp_map_isbn(&$orig_data, &$dest_tree=[])
+{
+	return ($orig_data['product']['fk_barcode_type']==4) ?$orig_data['product']['barcode'] :NULL;
+}
+public static function _dp_map_upc(&$orig_data, &$dest_tree=[])
+{
+	return ($orig_data['product']['fk_barcode_type']==3) ?$orig_data['product']['barcode'] :NULL;
+}
+
+}
+
+sync_order_detail_dp::__init();

+ 340 - 0
src/sync/order_detail/pd.inc.php

@@ -0,0 +1,340 @@
+<?php
+
+class sync_order_detail_pd extends sync_order_detail
+{
+
+// -- ACTIONS --
+
+public function execute_osync($otype, $oid)
+{
+	return $this->pd_execute_osync($otype, $oid);
+}
+
+public function execute_create($otype, $oid)
+{
+	return $this->create($otype, $oid);
+}
+
+public function execute_delete($otype, $oid)
+{
+	//$o = static::p_o_oid(static::MODEL_NAME, $otype, $oid);
+	//return $this->pd_delete($otype, $oid);
+}
+
+// -- SUB ACTIONS --
+
+public function create($otype, $oid)
+{
+	return $this->pd_create($otype, $oid);
+}
+
+public function osync($o)
+{
+	return $this->pd_osync($o);
+}
+
+public function pd_insert_more(&$o, &$d_data, &$p_data)
+{
+	$id = $o['d_oid'];
+	// Pack produit => élatement
+	var_dump($p_data);
+	if ($o['p_tref']='order_detail' && !empty($p_data['product']['cache_is_pack'])) {
+		$o_id = $p_data['order_detail']['id_order'];
+		$p_id = $p_data['order_detail']['product_id'];
+
+		$l = DB::p_get_rows('pack', ['id_product_pack'=>$p_id]);
+		//var_dump($l);
+		echo '<p>Eclatement du pack</p>';
+		foreach($l as $row) {
+			$p = DB::p_get_row('product_lang', ['id_product'=>$row['id_product_item']]);
+			$row2 = [
+				'id_product_pack' => $row['id_product_pack'],
+				'id_order' => $o_id,
+				'id_product' => $row['id_product_item'],
+				'id_product_attribute' => $row['id_product_attribute_item'],
+				'qte' => ($row['quantity']*$p_data['order_detail']['product_quantity']),
+				'product_name' => $p['name'].' (dans box/pack)',
+				'product_description' => 'Contenu dans : '.$p_data['order_detail']['product_name'],
+			];
+			var_dump($row); var_dump($row2);
+			$d_map_row = $this->map_create_pack($o, $row2);
+			var_dump($d_map_row);
+			$oid = DB::d_insert_row('commandedet', $d_map_row['commandedet']);
+			$oid = DB::d_insert_row('commandedet_extrafields', array_merge($d_map_row['commandedet_extrafields'], ['fk_object'=>$oid]));
+		}
+	}
+}
+public function pd_update_more(&$o, &$d_data_map, &$d_data_map_create, &$d_data, &$p_data)
+{
+	//$this->pd_insert_more($o, $d_data_map, $p_data);
+}
+
+// -- MAPPING --
+
+public function map_update(&$o, &$p_data, &$d_data=[])
+{
+	$p_id = isset($o['p_oid']) ?$o['p_oid'] :NULL;
+
+	if ($o['p_tref']=='order_detail') {
+		$d_data_map = [
+			'commandedet' => [
+				'qty' => $p_data['order_detail']['product_quantity'],
+				'remise_percent' => $p_data['order_detail']['reduction_percent'],
+				'remise' => $p_data['order_detail']['reduction_amount'],
+				'price' => $p_data['order_detail']['product_price'],
+				'subprice' => $p_data['order_detail']['original_product_price'],
+				'total_ht' => $p_data['order_detail']['total_price_tax_excl'],
+				'total_tva' => ($p_data['order_detail']['total_price_tax_incl']-$p_data['order_detail']['total_price_tax_excl']),
+				'total_ttc' => $p_data['order_detail']['total_price_tax_incl'],
+				'buy_price_ht' => $p_data['order_detail']['original_wholesale_price'],
+				'multicurrency_subprice' => $p_data['order_detail']['original_product_price'],
+				'multicurrency_total_ht' => $p_data['order_detail']['total_price_tax_excl'],
+				'multicurrency_total_tva' => ($p_data['order_detail']['total_price_tax_incl']-$p_data['order_detail']['total_price_tax_excl']),
+				'multicurrency_total_ttc' => $p_data['order_detail']['total_price_tax_incl'],
+			],
+		];
+	}
+	elseif ($o['p_tref']=='order_cart_rule') {
+		$row = $p_data['order_cart_rule'];
+
+		$tva = ($row['value']-$row['value_tax_excl']);
+		$tva_tx = round(100*$tva/$row['value_tax_excl'], 2);
+
+		$d_data_map = [
+			'commandedet' => [
+				//'rowid' => NULL,
+				'price' => $row['value_tax_excl'],
+				'subprice' => $row['value_tax_excl'],
+				'total_ht' => -$row['value_tax_excl'],
+				'total_tva' => -$tva,
+				'total_ttc' => -$row['value'],
+				'multicurrency_subprice' => $row['value_tax_excl'],
+				'multicurrency_total_ht' => -$row['value_tax_excl'],
+				'multicurrency_total_tva' => -($row['value']-$row['value_tax_excl']),
+				'multicurrency_total_ttc' => -$row['value'],
+			],
+		];
+	}
+
+	return $d_data_map;
+}
+
+public function map_create_pack(&$o, &$row)
+{
+	if ($row['id_product_attribute'])
+		$fk_product = static::_pd_map_fk('product', 'product_attribute', $row['id_product_attribute']);
+	else
+		$fk_product = static::_pd_map_fk('product', 'product', $row['id_product']);
+
+	$d_data_map = [
+		'commandedet' => [
+			//'rowid' => NULL,
+			'fk_commande' => static::_pd_map_fk('order', 'orders', $row['id_order']),
+			'fk_parent_line' => NULL,
+			'fk_product' => $fk_product,
+			'label' => $row['product_name'],
+			'description' => $row['product_description'],
+			'vat_src_code' => '',
+			'tva_tx' => 0, // @todo
+			'localtax1_tx' => 0,
+			'localtax1_type' => 0,
+			'localtax2_tx' => 0,
+			'localtax2_type' => 0,
+			'qty' => $row['qte'],
+			'remise_percent' => 0,
+			'remise' => 0,
+			'fk_remise_except' => NULL,
+			'price' => 0,
+			'subprice' => 0,
+			'total_ht' => 0,
+			'total_tva' => 0,
+			'total_localtax1' => 0,
+			'total_localtax2' => 0,
+			'total_ttc' => 0,
+			'product_type' => 0,
+			'date_start' => NULL,
+			'date_end' => NULL,
+			'info_bits' => 0,
+			'buy_price_ht' => 0,
+			'fk_product_fournisseur_price' => NULL, // @todo
+			'special_code' => 0,
+			'rang' => 0,
+			'fk_unit' => NULL,
+			'import_key' => NULL,
+			'fk_commandefourndet' => NULL,
+			'fk_multicurrency' => NULL,
+			'multicurrency_code' => 'EUR',
+			'multicurrency_subprice' => 0,
+			'multicurrency_total_ht' => 0,
+			'multicurrency_total_tva' => 0,
+			'multicurrency_total_ttc' => 0,
+			'ref_ext' => NULL,
+		],
+		'commandedet_extrafields' => [
+			//'fk_object' => '',
+			'tms' => static::$datetime,
+			'fk_parent_pack' => $o['d_oid'],
+		],
+	];
+
+	return $d_data_map;
+}
+
+public function map_create(&$o, &$p_data)
+{
+	$fk_order = static::_pd_map_fk_order($p_data);
+
+	if (isset($p_data['order_detail'])) {
+		$d_data_map = [
+			'commandedet' => [
+				//'rowid' => NULL,
+				'fk_commande' => $fk_order,
+				'fk_parent_line' => NULL,
+				'fk_product' => static::_pd_map_fk_product($p_data),
+				'label' => $p_data['order_detail']['product_name'],
+				'description' => '',
+				'vat_src_code' => '',
+				'tva_tx' => static::_pd_map_tva_taux($p_data), // @todo
+				'localtax1_tx' => 0,
+				'localtax1_type' => 0,
+				'localtax2_tx' => 0,
+				'localtax2_type' => 0,
+				'qty' => $p_data['order_detail']['product_quantity'],
+				'remise_percent' => $p_data['order_detail']['reduction_percent'],
+				'remise' => $p_data['order_detail']['reduction_amount'],
+				'fk_remise_except' => NULL,
+				'price' => $p_data['order_detail']['product_price'],
+				'subprice' => $p_data['order_detail']['original_product_price'],
+				'total_ht' => $p_data['order_detail']['total_price_tax_excl'],
+				'total_tva' => ($p_data['order_detail']['total_price_tax_incl']-$p_data['order_detail']['total_price_tax_excl']),
+				'total_localtax1' => 0,
+				'total_localtax2' => 0,
+				'total_ttc' => $p_data['order_detail']['total_price_tax_incl'],
+				'product_type' => 0,
+				'date_start' => NULL,
+				'date_end' => NULL,
+				'info_bits' => 0,
+				'buy_price_ht' => $p_data['order_detail']['original_wholesale_price'],
+				'fk_product_fournisseur_price' => NULL, // @todo
+				'special_code' => 0,
+				'rang' => 0,
+				'fk_unit' => NULL,
+				'import_key' => NULL,
+				'fk_commandefourndet' => NULL,
+				'fk_multicurrency' => NULL,
+				'multicurrency_code' => 'EUR',
+				'multicurrency_subprice' => $p_data['order_detail']['original_product_price'],
+				'multicurrency_total_ht' => $p_data['order_detail']['total_price_tax_excl'],
+				'multicurrency_total_tva' => ($p_data['order_detail']['total_price_tax_incl']-$p_data['order_detail']['total_price_tax_excl']),
+				'multicurrency_total_ttc' => $p_data['order_detail']['total_price_tax_incl'],
+				'ref_ext' => NULL,
+			],
+			'commandedet_extrafields' => [
+				'tms' => static::$datetime,
+			],
+		];
+	}
+	elseif (isset($p_data['order_cart_rule'])) {
+		$row = $p_data['order_cart_rule'];
+
+		$tva = ($row['value']-$row['value_tax_excl']);
+		$tva_tx = round(100*$tva/$row['value_tax_excl'], 2);
+
+		$d_data_map = [
+			'commandedet' => [
+				//'rowid' => NULL,
+				'fk_commande' => $fk_order,
+				'fk_parent_line' => NULL,
+				'fk_product' => static::D_K_DISCOUNT_PRODUCT,
+				'label' => static::D_DISCOUNT_PRODUCT_NAME,
+				'description' => $row['name'],
+				'vat_src_code' => '',
+				'tva_tx' => $tva_tx, // @todo
+				'localtax1_tx' => 0,
+				'localtax1_type' => 0,
+				'localtax2_tx' => 0,
+				'localtax2_type' => 0,
+				'qty' => 1,
+				'remise_percent' => 0,
+				'remise' => 0,
+				'fk_remise_except' => NULL,
+				'price' => $row['value_tax_excl'],
+				'subprice' => $row['value_tax_excl'],
+				'total_ht' => -$row['value_tax_excl'],
+				'total_tva' => -$tva,
+				'total_localtax1' => 0,
+				'total_localtax2' => 0,
+				'total_ttc' => -$row['value'],
+				'product_type' => 1,
+				'date_start' => NULL,
+				'date_end' => NULL,
+				'info_bits' => 0,
+				'buy_price_ht' => 0,
+				'fk_product_fournisseur_price' => NULL, // @todo
+				'special_code' => 0,
+				'rang' => 0,
+				'fk_unit' => NULL,
+				'import_key' => NULL,
+				'fk_commandefourndet' => NULL,
+				'fk_multicurrency' => NULL,
+				'multicurrency_code' => 'EUR',
+				'multicurrency_subprice' => $row['value_tax_excl'],
+				'multicurrency_total_ht' => -$row['value_tax_excl'],
+				'multicurrency_total_tva' => -$tva,
+				'multicurrency_total_ttc' => -$row['value'],
+				'ref_ext' => NULL,
+			],
+			'commandedet_extrafields' => [
+				'tms' => static::$datetime,
+			],
+		];
+	}	
+	return $d_data_map;
+}
+
+// -----------
+
+// Field MAPPING
+
+public static function _pd_map_id_tax_rules_group(&$orig_data, &$dest_tree=[])
+{
+	// @todo mapper les taxes proprement !!
+	switch($orig_data['product']['tva_tx']) {
+		case '20':
+			return 1;
+		case '10':
+			return 2;
+		case '5.5':
+			return 3;
+		case '2.1':
+			return 4;
+		case '0':
+			return 5;
+	}
+}
+
+public static function _pd_map_fk_product(&$orig_data, &$dest_tree=[])
+{
+	// Product type
+	if ($orig_data['order_detail']['product_attribute_id'])
+		return static::_pd_map_fk('product', 'product_attribute', $orig_data['order_detail']['product_attribute_id']);
+	else
+		return static::_pd_map_fk('product', 'product', $orig_data['order_detail']['product_id']);
+}
+
+public static function _pd_map_fk_order(&$orig_data, &$dest_tree=[])
+{
+	if (isset($orig_data['order_detail']))
+		return static::_pd_map_fk('order', 'orders', $orig_data['order_detail']['id_order']);
+	elseif ($orig_data['order_cart_rule'])
+		return static::_pd_map_fk('order', 'orders', $orig_data['order_cart_rule']['id_order']);
+}
+
+public static function _pd_map_tva_taux(&$orig_data, &$dest_tree=[])
+{
+	return 5.5; // @todo
+}
+
+}
+
+sync_order_detail_pd::__init();

+ 48 - 0
src/sync/payment/common.inc.php

@@ -0,0 +1,48 @@
+<?php
+
+// Order
+
+class sync_payment extends sync
+{
+
+const MODEL_NAME = 'payment';
+const MODEL_ID = 19;
+const MAP_ID = 13;
+const D_TABLE_MAIN = 'paiement';
+const P_TABLE_MAIN = 'order_payment';
+const P_TABLE_MAIN_ID = 'id_order_payment';
+
+function d_data($otype, $oid)
+{
+	$data = [];
+
+	// paiement
+	$data['paiement'] = DB::d_get_row('paiement', ['rowid'=>$oid]);
+
+	return $data;
+}
+
+function p_data($otype, $oid)
+{
+	$data = [];
+
+	// order_payment
+	$data['order_payment'] = DB::p_get_row('order_payment', ['id_order_payment'=>$oid]);
+	$o_ref = $data['order_payment']['order_reference'];
+	var_dump($o_ref);
+
+	// orders
+	$data['orders'] = DB::p_get_row('orders', ['reference'=>$o_ref]);
+	$o_id = $data['orders']['id_order'];
+	$c_id = $data['orders']['id_customer'];
+
+	// etransactions_order
+	$data['etransactions_order'] = DB::p_get_row('etransactions_order', ['id_order'=>$o_id]);
+
+	// customer
+	$data['customer'] = DB::p_get_row('customer', ['id_customer'=>$c_id]);
+
+	return $data;
+}
+
+}

+ 74 - 0
src/sync/payment/dp.inc.php

@@ -0,0 +1,74 @@
+<?php
+
+class sync_payment_dp extends sync_payment
+{
+
+// -- ACTIONS --
+
+public function execute_osync($otype, $oid)
+{
+	return $this->dp_execute_osync($otype, $oid);
+}
+
+public function execute_create($otype, $oid)
+{
+	// Pour l'instant pas de synchro => Presta
+	return;
+
+	return $this->create($otype, $oid);
+}
+
+// -- SUB ACTIONS --
+
+public function create($otype, $oid)
+{
+	return $this->dp_create($otype, $oid);
+}
+
+public function osync($o)
+{
+	return $this->dp_osync($o);
+}
+
+// -- MAPPING --
+
+public function map_update(&$o, &$d_data, &$p_data=[])
+{
+	$p_data_map = [
+		'order_payment' => [
+		],
+	];
+
+	return $p_data_map;
+}
+
+public function map_create(&$o, &$d_data)
+{
+	$p_data_map = [
+		'order_payment' => [
+			//'id_order_payment' => '',
+			'order_reference' => '',
+			'id_currency' => '',
+			'amount' => '',
+			'payment_method' => '',
+			'conversion_rate' => '',
+			'transaction_id' => '',
+			'card_number' => '',
+			'card_brand' => '',
+			'card_expiration' => '',
+			'card_holder' => '',
+			'date_add' => '',
+		],
+	];
+
+	return $p_data_map;
+}
+
+// -----------
+
+// Field MAPPING
+
+
+}
+
+sync_payment_dp::__init();

+ 121 - 0
src/sync/payment/pd.inc.php

@@ -0,0 +1,121 @@
+<?php
+
+class sync_payment_pd extends sync_payment
+{
+
+// -- ACTIONS --
+
+public function execute_osync($otype, $oid)
+{
+	return $this->pd_execute_osync($otype, $oid);
+}
+
+public function execute_create($otype, $oid)
+{
+	return $this->create($otype, $oid);
+}
+
+// -- SUB ACTIONS --
+
+public function create($otype, $oid)
+{
+	return $this->pd_create($otype, $oid);
+}
+
+public function osync($o)
+{
+	// Pas de synchro update Presta => Dolibarr
+	return;
+	
+	return $this->pd_osync($otype, $oid);
+}
+
+// -- MAPPING --
+
+public function map_update(&$o, &$p_data, &$d_data=[])
+{
+	return;
+	
+	$d_data_map = [
+	];
+
+	return $d_data_map;
+}
+
+public function map_create(&$o, &$p_data)
+{
+	//$p_data['order_payment']['id_currency'],
+
+	$d_data_map = [
+		'paiement' => [
+			//'rowid' => '',
+			'ref' => '', // PAY0000-0000
+			'entity' => 1,
+			'datec' => $p_data['order_payment']['date_add'],
+			'tms' => static::$datetime,
+			'datep' => '',
+			'amount' => $p_data['order_payment']['amount'],
+			'multicurrency_amount' => '',
+			'fk_paiement' => static::_pd_map_fk_paiement($p_data),
+			'num_paiement' => NULL,
+			'note' => NULL,
+			'ext_payment_id' => NULL,
+			'ext_payment_site' => NULL,
+			'fk_bank' => static::_pd_map_fk_bank($p_data),
+			'fk_user_creat' => static::D_K_USER,
+			'fk_user_modif' => static::D_K_USER,
+			'statut' => 0,
+			'fk_export_compta' => 0,
+		],
+	];
+	
+	if (false) {
+		$d_data_map['paiement_facture'] = [
+			//'rowid' => NULL,
+			//'fk_paiement' => , => rowid
+			'fk_facture' => '',
+			'amount' => $p_data['order_payment']['amount'],
+			'multicurrency_code' => NULL,
+			'multicurrency_tx' => '1',
+			'multicurrency_amount' => $p_data['order_payment']['amount'],
+		];
+	}
+
+	return $d_data_map;
+}
+
+// -----------
+
+// Field MAPPING
+
+public static function _pd_map_fk_paiement(&$orig_data, &$dest_tree=[])
+{
+	if ($p_data['order_payment']['payment_method'] == 'E-Transactions')
+		return 57;
+
+	$l = [
+		1 =>'TIP',
+		2 =>'Transfer',
+		3 =>'Debit order',
+		4 =>'Cash',
+		6 =>'Credit card',
+		7 =>'Cheque',
+		50 =>'Online payment',
+		51 =>'Traite',
+		52 =>'LCR',
+		53 =>'Factor',
+		54 =>'Payment by check',
+		55 =>'Bank wire',
+		56 =>'Transfert bancaire',
+		57 =>'E-Transactions',
+	];
+}
+
+public static function _pd_map_fk_bank(&$orig_data, &$dest_tree=[])
+{
+	return static::D_K_BANK_ETRANSACTION;
+}
+
+}
+
+sync_payment_pd::__init();

+ 88 - 0
src/sync/product/common.inc.php

@@ -0,0 +1,88 @@
+<?php
+
+// Product
+
+class sync_product extends sync
+{
+
+const MODEL_NAME='product';
+const MODEL_ID = 1;
+const MAP_ID = 1; // default
+const MAP_SIMPLE_ID = 1;
+const MAP_COMBI_ID = 2;
+
+const D_TABLE_MAIN = 'product';
+
+const P_TABLE_MAIN = 'product';
+const P_SIMPLE_TABLE_MAIN = 'product';
+const P_SIMPLE_TABLE_MAIN_ID = 'id_product';
+const P_COMBI_TABLE_MAIN = 'product_attribute';
+const P_COMBI_TABLE_MAIN_ID = 'id_product_attribute';
+
+function d_data($otype, $oid)
+{
+	$data = [];
+
+	$data['product'] = DB::d_get_row('product', ['rowid'=>$oid]);
+	$data['product_extrafields'] = DB::d_get_row('product_extrafields', ['fk_object'=>$oid]);
+
+	// Pack/Kit
+	$data['product_association'] = DB::d_get_rows('product_association', ['fk_product_pere'=>$oid]);
+
+	// stock entrepot 1
+	$data['product_stock'] = DB::d_get_row('product_stock', ['fk_product'=>$oid, 'fk_entrepot'=>static::D_K_WAREHOUSE]);
+
+	// lots
+	$data['product_lot'] = DB::d_get_rows('product_lot', ['fk_product'=>$oid]);
+
+	return $data;
+}
+
+function p_data($otype, $oid)
+{
+	$data = [];
+
+	if ($otype=='product_attribute') {
+		$pa_id = $oid;
+		if (empty($data['product_attribute'] = DB::p_get_row('product_attribute', ['id_product_attribute'=>$pa_id])))
+			return;
+		$p_id = $data['product_attribute']['id_product'];
+		$data['product_attribute_shop'] = DB::p_get_row('product_attribute_shop', ['id_product'=>$p_id, 'id_product_attribute'=>$pa_id, 'id_shop'=>static::P_ID_SHOP]);
+		$data['product_attribute_combination'] = DB::p_get_rows('product_attribute_combination', ['id_product_attribute'=>$pa_id]);
+	}
+	elseif ($otype=='product') {
+		$pa_id = 0;
+		$p_id = $oid;
+	}
+	else {
+		return;
+	}
+	
+	if (empty($data['product'] = DB::p_get_row('product', ['id_product'=>$p_id])))
+		return;
+	
+	$data['product_lang'] = DB::p_get_row('product_lang', ['id_product'=>$p_id, 'id_shop'=>static::P_ID_SHOP, 'id_lang'=>static::P_ID_LANG]);
+	$data['product_shop'] = DB::p_get_row('product_shop', ['id_product'=>$p_id, 'id_shop'=>static::P_ID_SHOP]);
+
+	// Pack/Kit
+	$data['pack'] = DB::p_get_rows('pack', ['id_product_pack'=>$p_id]);
+
+	// @todo mapping !
+
+	// Stock
+	$data['stock_available'] = DB::p_get_row('stock_available', ['id_product'=>$p_id, 'id_product_attribute'=>$pa_id, 'id_shop'=>static::P_ID_SHOP]);
+
+	// Lot
+	if (PRODUCT_LOT)
+		$data['products_dlc_dluo'] = DB::p_get_rows('products_dlc_dluo', ['id_product'=>$p_id, 'id_combinaison'=>$pa_id]);
+
+	// Supplier
+	$data['product_supplier'] = DB::p_get_rows('product_supplier', ['id_product'=>$p_id, 'id_product_attribute'=>$pa_id]);
+
+	// Categories
+	$data['category_product'] = DB::p_get_rows('category_product', ['id_product'=>$p_id]);
+
+	return $data;
+}
+
+}

+ 625 - 0
src/sync/product/dp.inc.php

@@ -0,0 +1,625 @@
+<?php
+
+class sync_product_dp extends sync_product
+{
+
+public function execute_osync($otype, $oid)
+{
+	return $this->dp_execute_osync($otype, $oid);
+}
+
+public function execute_create($otype, $oid)
+{
+	return $this->create($otype, $oid);
+}
+
+// -- SUB ACTIONS --
+
+public function create($otype, $oid)
+{
+	return $this->dp_create($otype, $oid);
+}
+
+public function dp_insert_more(&$o, &$p_data_map, &$d_data)
+{
+	// Produit simple
+	if ($o['p_tref']=='product')
+		return $this->dp_insert_simple_more($o);
+	// Produit décliné
+	elseif ($o['p_tref']=='product_attribute')
+		return $this->dp_insert_combi_more($o);
+	// @see update
+}
+public function dp_insert_simple_more(&$o, &$p_data_map, &$d_data)
+{
+	// @see update
+}
+
+public function osync($o)
+{
+	return $this->dp_osync($o);
+}
+
+public function dp_update_more(&$o, &$p_data_map, &$p_data_map_create, &$p_data, &$d_data)
+{
+	// Produit simple
+	if ($o['p_tref']=='product')
+		return $this->dp_update_simple_more($o, $p_data_map, $p_data_map_create, $p_data, $d_data);
+	// Produit décliné
+	elseif ($o['p_tref']=='product_attribute')
+		return $this->dp_update_combi_more($o, $p_data_map, $p_data_map_create, $p_data, $d_data);
+}
+
+public function dp_update_common_more(&$o, &$p_data_map, &$p_data_map_create, &$p_data, &$d_data)
+{
+	$id = $o['p_oid'];
+	
+	// category_product (par defaut)
+	if (empty($p_data['category_product']) && isset($p_data_map_create['category_product'])) {
+		static::p_insert_row('category_product', ['id_product'=>$id, 'id_category'=>static::P_ID_CATEGORY_DEFAULT], $p_data_map, $p_data_map_create, $p_data);
+	}
+
+	// product_lang
+	static::p_update_row('product_lang', ['id_product'=>$id, 'id_lang'=>static::P_ID_LANG, 'id_shop'=>static::P_ID_SHOP], $p_data_map, $p_data_map_create, $p_data);
+
+	// product_shop
+	static::p_update_row('product_shop', ['id_product'=>$id, 'id_shop'=>static::P_ID_SHOP], $p_data_map, $p_data_map_create, $p_data);
+
+	// Objets liés
+
+	// stock
+	if (!empty($d_data['product_stock']))
+		static::_action('stock', 'dp', 'osync', 'product_stock', $d_data['product_stock']['rowid']);
+}
+
+public function dp_update_simple_more(&$o, &$p_data_map, &$p_data_map_create, &$p_data, &$d_data)
+{
+	$this->dp_update_common_more($o, $p_data_map, $p_data_map_create, $p_data, $d_data);
+	
+	$id = $o['p_oid'];
+	
+	// pack
+	if (isset($p_data_map['pack'])) {
+		// analyse old assoc
+		$p_assoc_old = [];
+		if (isset($p_data['pack'])) {
+			foreach($p_data['pack'] as &$row)
+				$p_assoc_old[$row['id_product_item'].'-'.$row['id_product_attribute_item']] = $row;
+		}
+		$p_assoc_update = [];
+		$p_assoc_insert = [];
+		$p_assoc_delete = [];
+		// analyse new assoc => look into new and updated
+		foreach($p_data_map['pack'] as $row) {
+			// ajout id père
+			$row['id_product_pack'] = $id;
+			if (empty($p_assoc_old[$row['id_product_item'].'-'.$row['id_product_attribute_item']]))
+				$p_assoc_insert[] = $row;
+			else
+				$p_assoc_update[$row['id_product_item'].'-'.$row['id_product_attribute_item']] = $row;
+		}
+		// Remove deleted
+		foreach($p_assoc_old as $k=>$row) {
+			if (!isset($p_assoc_update[$k]))
+				$p_assoc_delete[] = [$row['id_product_item'], $row['id_product_attribute_item']];
+		}
+
+		var_dump($p_assoc_insert);
+		var_dump($p_assoc_update);
+		var_dump($p_assoc_delete);
+		//die();
+
+		// DB actions
+		foreach($p_assoc_insert as $row)
+			DB::p_insert_row('pack', $row);
+		foreach($p_assoc_update as $k=>$row)
+			DB::p_update_row('pack', $row, ['id_product_pack'=>$p_assoc_old[$k]['id_product_pack'], 'id_product_item'=>$p_assoc_old[$k]['id_product_item'], 'id_product_attribute_item'=>$p_assoc_old[$k]['id_product_attribute_item']], $p_assoc_old[$k]);
+		foreach($p_assoc_delete as $k)
+			DB::p_delete('DELETE FROM `'.DB_P_PREFIX.'pack` WHERE id_product_pack='.$id.' AND id_product_item='.$k[0].' AND id_product_attribute_item='.$k[1]);
+	}	
+	elseif(!empty($p_data['pack'])) {
+		// Suppression de tout, puisque on a rien en entrée
+		DB::p_delete('DELETE FROM `'.DB_P_PREFIX.'pack` WHERE `id_product_pack`='.$id);
+	}
+}
+
+public function dp_update_combie_more(&$o, &$p_data_map, &$p_data_map_create, &$p_data, &$d_data)
+{
+	$this->dp_update_common_more($o, $p_data_map, $p_data_map_create, $p_data, $d_data);
+
+	// product
+	DB::p_update_row('product', $p_data_map['product'], ['id_product'=>$p_data['product']['id_product']], $p_data['product']);
+	// product_attribute_shop
+	DB::p_update_row('product_attribute_shop', $p_data_map['product_attribute_shop'], ['id_product_attribute'=>$p_data['product_attribute_shop']['id_product_attribute']], $p_data['product_attribute_shop']);
+}
+
+// -- MAPPING --
+
+
+public function dp_map_create(&$o, &$d_data)
+{
+	return $this->map_create_simple($o, $d_data);
+}
+public function map_create_simple(&$o, &$d_data)
+{
+	$o['p_tref'] = 'product';
+	
+	$p_data_map = [
+		'product' => [
+			//'id_product' => NULL,
+			//'id_supplier' => '',
+			//'id_manufacturer' => '',
+			'id_category_default' => static::P_ID_CATEGORY_DEFAULT,
+			'id_shop_default' => static::P_ID_SHOP,
+			'id_tax_rules_group' => static::P_ID_TAX_RULES_GROUP,
+			'on_sale' => '0',
+			//'online_only' => '0',
+			//'ean13' => NULL,
+			//'isbn' => null
+			//'upc' => null
+			'ecotax' => '0',
+			'quantity' => '0',
+			'minimal_quantity' => '1',
+			'low_stock_threshold' => '0',
+			'low_stock_alert' => '1',
+			//'price' => '0',
+			//'wholesale_price' => '0',
+			'unity' => '',
+			'unit_price_ratio' => 1,
+			'additional_shipping_cost' => 0,
+			'reference' => '',
+			'supplier_reference' => '',
+			'location' => '',
+			'width' => 0,
+			'height' => 0,
+			'depth' => 0,
+			'weight' => 0,
+			'out_of_stock' => 0,
+			'additional_delivery_times' => 0,
+			'quantity_discount' => 0,
+			'customizable' => 0,
+			'uploadable_files' => 0,
+			'text_fields' => '0',
+			'active' => '0',
+			'redirect_type' => '301-category',
+			'id_type_redirected' => '0',
+			'available_for_order' => '1',
+			'available_date' => '0000-00-00',
+			'show_condition' => '0',
+			'condition' => 'new',
+			'show_price' => '1',
+			'indexed' => '1',
+			'visibility' => 'both',
+			'cache_is_pack' => '0',
+			'cache_has_attachments' => '0',
+			'is_virtual' => '0',
+			'cache_default_attribute' => '0',
+			'date_add' => static::$datetime,
+			'date_upd' => static::$datetime,
+			//'date_online' => $datetime,
+			'advanced_stock_management' => '0',
+			'pack_stock_type' => '3',
+			'state' => '1',
+		],
+		'product_lang' => [
+			//'id_product' => NULL,
+			'id_shop' => static::P_ID_SHOP,
+			'id_lang' => static::P_ID_LANG,
+			'description' => '',
+			'link_rewrite' => '',
+			'meta_description' => '',
+			'meta_keywords' => '',
+			'meta_title' => '',
+			'name' => '',
+			//'available_now' => '',
+			//'available_later' => '',
+			//'delivery_in_stock' => '',
+			//'delivery_out_stock' => '',
+			//'expert_advice_image' => '',
+			//'expert_advice_name' => '',
+			//'expert_advice_text' => '',
+			'gluten_free_certification_number' => '',
+			//'product_tab_link_label' => '',
+			//'product_tab_link_anchor' => '',
+			//'product_brand_link' => '',
+		],
+		'product_shop' => [
+			//'id_product' => NULL,
+			'id_shop' => static::P_ID_SHOP,
+			'id_category_default' => static::P_ID_CATEGORY_DEFAULT,
+			'id_tax_rules_group' => static::P_ID_TAX_RULES_GROUP,
+			'on_sale' => '0',
+			'online_only' => '0',
+			'ecotax' => '0',
+			'minimal_quantity' => static::P_MINIMAL_QUANTITY,
+			'low_stock_threshold' => static::P_LOW_STOCK_THRESHOLD,
+			'low_stock_alert' => static::P_LOW_STOCK_ALERT,
+			'price' => '0',
+			'wholesale_price' => '0',
+			'unity' => static::P_UNITY,
+			'unit_price_ratio' => '0',
+			'additional_shipping_cost' => '0',
+			'customizable' => '0',
+			'uploadable_files' => '0',
+			'text_fields' => '0',
+			'active' => '0',
+			'redirect_type' => '301-category',
+			'id_type_redirected' => '0',
+			'available_for_order' => '1',
+			'available_date' => '0000-00-00',
+			'show_condition' => '0',
+			'condition' => 'new',
+			'show_price' => '1',
+			'indexed' => '1',
+			'visibility' => 'both',
+			'cache_default_attribute' => '0',
+			'advanced_stock_management' => '0',
+			'date_add' => static::$datetime,
+			'date_upd' => static::$datetime,
+			//'date_online' => NULL,
+			'pack_stock_type' => static::P_PACK_STOCK_TYPE,
+		],
+		'category_product' => [],
+	];
+
+	return $p_data_map;
+}
+
+public function dp_map_update(&$o, &$d_data, &$p_data=[])
+{
+	if ($o['d_tref']=='product')
+		return $this->map_update_simple($o, $d_data, $p_data);
+	elseif ($o['d_tref']=='product_attribute')
+		return $this->map_update_combi($o, $d_data, $p_data);
+}
+
+public function map_update_simple(&$o, &$d_data, &$p_data=[])
+{
+	$tms = max((isset($p_data['product']['date_upd']) ?$p_data['product']['date_upd'] :'0000-00-00') :$d_data['product']['tms']);
+	
+	$p_data_map = [
+		'product' => [
+			//'id_product' => $p_id,
+			//'id_supplier' => '',
+			//'id_manufacturer' => '',
+			//'id_category_default' => static::P_ID_CATEGORY_DEFAULT,
+			//'id_shop_default' => static::P_ID_SHOP,
+			'id_tax_rules_group' => static::_dp_map_id_tax_rules_group($d_data),
+			//'on_sale' => string '0',
+			'online_only' => (isset($d_data['product_extrafields']) ?($d_data['product_extrafields']['p_online_only'] ?1 :0) :'1'),
+			'ean13' => static::_dp_map_ean13($d_data),
+			'isbn' => static::_dp_map_isbn($d_data),
+			'upc' => static::_dp_map_upc($d_data),
+			//'ecotax' => '0',
+			//'quantity' => '0',
+			//'minimal_quantity' => '1',
+			//'low_stock_threshold' => '0',
+			//'low_stock_alert' => '1',
+			'price' => $d_data['product']['price'],
+			'wholesale_price' => ($d_data['product']['cost_price'] ?$d_data['product']['cost_price'] :0), // achat hors tva
+			//'unity' => '',
+			//'unit_price_ratio' => 1,
+			//'additional_shipping_cost' => 0,
+			'reference' => $d_data['product']['ref'],
+			//'supplier_reference' => '',
+			//'location' => '',
+			'width' => ($d_data['product']['width'] ?$d_data['product']['width'] :0),
+			'height' => ($d_data['product']['height'] ?$d_data['product']['height'] :0),
+			'depth' => ($d_data['product']['length'] ?$d_data['product']['length'] :0),
+			'weight' => ($d_data['product']['weight'] ?$d_data['product']['weight'] :0),
+			//'out_of_stock' => '0',
+			//'additional_delivery_times' => '0',
+			//'quantity_discount' => '0',
+			//'customizable' => '0',
+			//'uploadable_files' => '0',
+			//'text_fields' => '0',
+			'active' => (isset($d_data['product_extrafields']) ?($d_data['product_extrafields']['p_active'] ?1 :0) :'1'),
+			//'redirect_type' => '301-category',
+			//'id_type_redirected' => '0',
+			'available_for_order' => (isset($d_data['product_extrafields']) ?($d_data['product_extrafields']['p_available_for_order'] ?1 :0) :'1'),
+			//'available_date' => '',
+			//'show_condition' => '0',
+			//'condition' => 'new',
+			//'show_price' => '1',
+			//'indexed' => '1',
+			//'visibility' => 'both',
+			//'cache_is_pack' => '0',
+			//'cache_has_attachments' => '0',
+			//'is_virtual' => '0', // @todo
+			//'cache_default_attribute' => '0',
+			//'date_add' => $datetime,
+			'date_upd' => $tms,
+			//'date_online' => $datetime,
+			//'advanced_stock_management' => '0', // @todo
+			//'pack_stock_type' => '3',
+			//'state' => '1',
+
+		],
+		'product_lang' => [
+			//'id_product' => $p_id,
+			//'id_shop' => static::P_ID_SHOP,
+			//'id_lang' => static::P_ID_LANG,
+			//'description' => html_entity_decode($d_data['product']['description']),
+			'link_rewrite' => static::_dp_map_link_rewrite($d_data),
+			//'meta_description' => html_entity_decode($d_data['product']['description']),
+			//'meta_keywords' => '',
+			//'meta_title' => html_entity_decode($d_data['product']['label']),
+			'name' => html_entity_decode($d_data['product']['label']),
+			//'available_now' => '',
+			//'available_later' => '',
+			//'delivery_in_stock' => '',
+			//'delivery_out_stock' => '',
+			//'expert_advice_image' => '',
+			//'expert_advice_name' => '',
+			//'expert_advice_text' => '',
+			//'gluten_free_certification_number' => '',
+			//'product_tab_link_label' => '',
+			//'product_tab_link_anchor' => '',
+			//'product_brand_link' => '',
+		],
+		'product_shop' => [
+			//'id_product' => $p_id,
+			//'id_shop' => static::P_ID_SHOP,
+			//'id_category_default' => static::P_ID_CATEGORY_DEFAULT
+			'id_tax_rules_group' => static::_dp_map_id_tax_rules_group($d_data),
+			//'on_sale' => '0',
+			'online_only' => (isset($d_data['product_extrafields']) ?($d_data['product_extrafields']['p_online_only'] ?1 :0) :'1'),
+			//'ecotax' => '0',
+			//'minimal_quantity' => static::P_MINIMAL_QUANTITY,
+			//'low_stock_threshold' => static::P_LOW_STOCK_THRESHOLD,
+			//'low_stock_alert' => static::P_LOW_STOCK_ALERT,
+			'price' => $d_data['product']['price'],
+			'wholesale_price' => ($d_data['product']['cost_price'] ?$d_data['product']['cost_price'] :0), // achat hors tva
+			//'unity' => static:P_UNITY,
+			//'unit_price_ratio' => '0',
+			//'additional_shipping_cost' => '0',
+			//'customizable' => '0',
+			//'uploadable_files' => '0',
+			//'text_fields' => '0',
+			'active' => (isset($d_data['product_extrafields']) ?($d_data['product_extrafields']['p_active'] ?1 :0) :'1'),
+			//'redirect_type' => '301-category',
+			//'id_type_redirected' => '0',
+			'available_for_order' => (isset($d_data['product_extrafields']) ?($d_data['product_extrafields']['p_available_for_order'] ?1 :0) :'1'),
+			//'available_date' => '0000-00-00',
+			//'show_condition' => '0',
+			//'condition' => 'new',
+			//'show_price' => '1',
+			//'indexed' => '1',
+			//'visibility' => 'both',
+			//'cache_default_attribute' => '0',
+			//'advanced_stock_management' => '0',
+			//'date_add' => static::$datetime,
+			'date_upd' => $tms,
+			//'date_online' => NULL,
+			//'pack_stock_type' => static::P_PACK_STOCK_TYPE,
+		],
+	];
+
+	$p_data_map['pack'] = [];
+	if (isset($d_data['product_association'])) {
+		foreach($d_data['product_association'] as $row) {
+			$product_fils = static::_dp_product_fils($row);
+			if (!empty($product_fils['id_product'])) {
+				$p_data_map['pack'][] = [
+					//'id_product_pack' => $o['p_oid'],
+					'id_product_item' => $product_fils['id_product'],
+					'id_product_attribute_item' => $product_fils['id_product_attribute'],
+					'quantity' => $row['qty'],
+				];
+			}
+		}
+	}
+
+	return $p_data_map;
+}
+
+public function map_update_combi(&$o, &$d_data, &$p_data=[])
+{
+	$tms = max((isset($p_data['product']['date_upd']) ?$p_data['product']['date_upd'] :'0000-00-00') :$d_data['product']['tms']);
+	
+	$p_data_map = [
+		'product' => [
+			//'id_product' => $p_id,
+			//'id_supplier' => '',
+			//'id_manufacturer' => '',
+			//'id_category_default' => static::P_ID_CATEGORY_DEFAULT,
+			//'id_shop_default' => static::P_ID_SHOP,
+			'id_tax_rules_group' => static::_dp_map_id_tax_rules_group($d_data),
+			//'on_sale' => string '0',
+			//'online_only' => '0',
+			'ean13' => '', //static::_dp_map_ean13($d_data),
+			'isbn' => '', //static::_dp_map_isbn($d_data),
+			'upc' => '', //static::_dp_map_upc($d_data),
+			//'ecotax' => '0',
+			//'quantity' => '0',
+			//'minimal_quantity' => '1',
+			//'low_stock_threshold' => '0',
+			//'low_stock_alert' => '1',
+			'price' => 0,
+			'wholesale_price' => 0, // achat hors tva
+			//'unity' => '',
+			//'unit_price_ratio' => 1,
+			//'additional_shipping_cost' => 0,
+			//'reference' => '', //$d_data['product']['ref'], // on laisse tel quel, c'est la liste des différentes déclinaisons
+			//'supplier_reference' => '',
+			//'location' => '',
+			'width' => 0,
+			'height' => 0,
+			'depth' => 0,
+			'weight' => 0,
+			//'out_of_stock' => '0',
+			//'additional_delivery_times' => '0',
+			//'quantity_discount' => '0',
+			//'customizable' => '0',
+			//'uploadable_files' => '0',
+			//'text_fields' => '0',
+			//'active' => '0',
+			//'redirect_type' => '301-category',
+			//'id_type_redirected' => '0',
+			//'available_for_order' => '1',
+			//'available_date' => '',
+			//'show_condition' => '0',
+			//'condition' => 'new',
+			//'show_price' => '1',
+			//'indexed' => '1',
+			//'visibility' => 'both',
+			//'cache_is_pack' => '0',
+			//'cache_has_attachments' => '0',
+			//'is_virtual' => '0', // @todo
+			//'cache_default_attribute' => '0',
+			//'date_add' => $datetime,
+			'date_upd' => $tms,
+			//'date_online' => $datetime,
+			//'advanced_stock_management' => '0', // @todo
+			//'pack_stock_type' => '3',
+			//'state' => '1',
+		],
+		'product_lang' => [
+			//'id_product' => $p_id,
+			//'id_shop' => static::P_ID_SHOP,
+			//'id_lang' => static::P_ID_LANG,
+			//'description' => html_entity_decode($d_data['product']['description']),
+			//'link_rewrite' => static::_dp_map_link_rewrite($d_data),
+			//'meta_description' => html_entity_decode($d_data['product']['description']),
+			//'meta_keywords' => '',
+			//'meta_title' => html_entity_decode($d_data['product']['label']),
+			//'name' => html_entity_decode($d_data['product']['label']),
+			//'available_now' => '',
+			//'available_later' => '',
+			//'delivery_in_stock' => '',
+			//'delivery_out_stock' => '',
+			//'expert_advice_image' => '',
+			//'expert_advice_name' => '',
+			//'expert_advice_text' => '',
+			//'gluten_free_certification_number' => '',
+			//'product_tab_link_label' => '',
+			//'product_tab_link_anchor' => '',
+			//'product_brand_link' => '',
+		],
+		'product_shop' => [
+			//'id_product' => $p_id,
+			//'id_shop' => static::P_ID_SHOP,
+			//'id_category_default' => static::P_ID_CATEGORY_DEFAULT
+			'id_tax_rules_group' => static::_dp_map_id_tax_rules_group($d_data),
+			//'on_sale' => '0',
+			//'online_only' => '0',
+			//'ecotax' => '0',
+			//'minimal_quantity' => static::P_MINIMAL_QUANTITY,
+			//'low_stock_threshold' => static::P_LOW_STOCK_THRESHOLD,
+			//'low_stock_alert' => static::P_LOW_STOCK_ALERT,
+			'price' => 0,
+			'wholesale_price' => 0, // achat hors tva
+			//'unity' => static:P_UNITY,
+			//'unit_price_ratio' => '0',
+			//'additional_shipping_cost' => '0',
+			//'customizable' => '0',
+			//'uploadable_files' => '0',
+			//'text_fields' => '0',
+			//'active' => '0',
+			//'redirect_type' => '301-category',
+			//'id_type_redirected' => '0',
+			//'available_for_order' => '1',
+			//'available_date' => '0000-00-00',
+			//'show_condition' => '0',
+			//'condition' => 'new',
+			//'show_price' => '1',
+			//'indexed' => '1',
+			//'visibility' => 'both',
+			//'cache_default_attribute' => '0',
+			//'advanced_stock_management' => '0',
+			//'date_add' => static::$datetime,
+			'date_upd' => $tms,
+			//'date_online' => NULL,
+			//'pack_stock_type' => static::P_PACK_STOCK_TYPE,
+		],
+		'product_attribute' => [
+			'price' => $d_data['product']['price'],
+			'wholesale_price' => ($d_data['product']['cost_price'] ?$d_data['product']['cost_price'] :0), // achat hors tva
+			'ean13' => static::_dp_map_ean13($d_data),
+			'isbn' => static::_dp_map_isbn($d_data),
+			'upc' => static::_dp_map_upc($d_data),
+		],
+		'product_attribute_shop' => [
+			'price' => $d_data['product']['price'],
+			'wholesale_price' => ($d_data['product']['cost_price'] ?$d_data['product']['cost_price'] :0), // achat hors tva
+		],
+	];
+
+	return $p_data_map;
+}
+
+public function map_update_pack(&$o, &$d_data, &$p_data=[])
+{
+	$p_data_map = $this->map_update_common($o, $d_data, $p_data);
+
+	$p_data_map['product']['cache_is_pack'] = 1;
+
+	return $p_data_map;
+}
+
+// -----------
+
+// Field MAPPING
+
+public static function _dp_product_fils(&$orig_data)
+{
+	$product = [];
+	// Déjà mappé
+	if ($o = static::d_o_oid('product', 'product', $orig_data['fk_product_fils'])) {
+		// Combi
+		if ($o['p_tref']=='product_attribute') {
+			$row = DB::p_get_row('product_attribute', ['id_product_attribute'=>$o['p_oid']]);
+			return ['id_product'=>$row['id_product'], 'id_product_attribute'=>$o['p_oid']];
+		}
+		// Simple
+		else {
+			return ['id_product'=>$o['p_oid'], 'id_product_attribute'=>0];
+		}
+	}
+	// Création simple
+	else {
+		$o = static::_action('product', 'dp', 'create', 'product', $orig_data['fk_product_fils']);
+		return ['id_product'=>$o['p_oid'], 'id_product_attribute'=>0];
+	}
+}
+
+public static function _dp_map_id_tax_rules_group(&$orig_data, &$dest_tree=[])
+{
+	// @todo mapper les taxes proprement !!
+	switch($orig_data['product']['tva_tx']) {
+		case '20':
+			return 1;
+		case '10':
+			return 2;
+		case '5.5':
+			return 3;
+		case '2.1':
+			return 4;
+		case '0':
+			return 5;
+	}
+}
+public static function _dp_map_ean13(&$orig_data, &$dest_tree=[])
+{
+	return ($orig_data['product']['fk_barcode_type']==2) ?substr($orig_data['product']['barcode'], 0, 13) :NULL;
+}
+public static function _dp_map_ean8(&$orig_data, &$dest_tree=[])
+{
+	return ($orig_data['product']['fk_barcode_type']==1) ?$orig_data['product']['barcode'] :NULL;
+}
+public static function _dp_map_isbn(&$orig_data, &$dest_tree=[])
+{
+	return ($orig_data['product']['fk_barcode_type']==4) ?$orig_data['product']['barcode'] :NULL;
+}
+public static function _dp_map_upc(&$orig_data, &$dest_tree=[])
+{
+	return ($orig_data['product']['fk_barcode_type']==3) ?$orig_data['product']['barcode'] :NULL;
+}
+public static function _dp_map_link_rewrite(&$orig_data, &$dest_tree=[])
+{
+	return static::_slugify($orig_data['product']['label']);
+}
+
+}
+
+sync_product_dp::__init();

+ 519 - 0
src/sync/product/pd.inc.php

@@ -0,0 +1,519 @@
+<?php
+
+class sync_product_pd extends sync_product
+{
+
+// -- ACTIONS --
+
+public function execute_osync($otype, $oid)
+{
+	return $this->pd_execute_osync($otype, $oid);
+}
+
+public function execute_create($otype, $oid)
+{
+	if ($otype=='product' && $this->check_combi($oid))
+		return false;
+	
+	return $this->pd_create($otype, $oid);
+}
+
+public function check_combi($oid)
+{
+	// Check simple ou combi
+	$sql = 'SELECT id_product_attribute
+		FROM ps_product_attribute
+		WHERE id_product='.$oid.'
+		LIMIT 1';
+	$q = DB::p_select($sql);
+
+	if ($q->num_rows) {
+		var_dump('COMBI ! ACHTUNG !');
+		return true;
+	}
+}
+
+// -- SUB ACTIONS --
+
+public function pd_insert_more(&$o, &$d_data_map, &$p_data)
+{
+	// Un seul cas de $otype/$d_tref dans Doli
+	$id = $o['d_oid'];
+
+	// product_extrafields
+	static::d_insert_row('product_extrafields', ['fk_object' => $id], $d_data_map);
+
+	//var_dump($p_data);
+
+	// stock_available
+	if (!empty($p_data['stock_available']))
+		static::_pd_map_fk('stock', 'stock_available', $p_data['stock_available']['id_stock_available']);
+
+	// products_dlc_dluo
+	if (!empty($p_data['products_dlc_dluo'])) foreach($p_data['products_dlc_dluo'] as $row)
+		static::_pd_map_fk('product_lot', 'products_dlc_dluo', $row['id']);
+	
+}
+
+public function osync($o)
+{
+	return $this->pd_osync($o);
+}
+public function pd_update_more(&$o, &$d_data_map, &$d_data_map_create, &$d_data, &$p_data)
+{
+	$id = $o['d_oid'];
+	$p_id = $p_data['product']['id_product'];
+	$pa_id = (!empty($p_data['product_attribute']) ?$p_data['product_attribute']['id_product_attribute'] :0);
+	
+	// product_extrafields
+	static::d_update_row('product_extrafields', ['fk_object'=>$id], $d_data_map, $d_data_map_create, $d_data);
+	
+	// pack
+	if (isset($d_data_map['product_association'])) {
+		// analyse old assoc
+		$d_assoc_old = [];
+		if (isset($d_data['product_association'])) {
+			foreach($d_data['product_association'] as &$row)
+				$d_assoc_old[$row['fk_product_fils']] = $row;
+		}
+		$d_assoc_update = [];
+		$d_assoc_insert = [];
+		$d_assoc_delete = [];
+		// analyse new assoc => look into new and updated
+		foreach($d_data_map['product_association'] as $row) {
+			// ajout id père
+			$row['fk_product_pere'] = $id;
+			if (empty($d_assoc_old[$row['fk_product_fils']]))
+				$d_assoc_insert[] = $row;
+			else
+				$d_assoc_update[$row['fk_product_fils']] = $row;
+		}
+		// Remove deleted
+		foreach($d_assoc_old as $k=>$row) {
+			if (!isset($d_assoc_update[$k]))
+				$d_assoc_delete[] = $row['rowid'];
+		}
+
+		var_dump($d_assoc_insert);
+		var_dump($d_assoc_update);
+		var_dump($d_assoc_delete);
+		
+		// DB actions
+		foreach($d_assoc_insert as $row)
+			DB::d_insert_row('product_association', $row);
+		foreach($d_assoc_update as $k=>$row)
+			DB::d_update_row('product_association', $row, ['rowid'=>$d_assoc_old[$k]['rowid']], $d_assoc_old[$k]);
+		if (!empty($d_assoc_delete))
+			DB::d_delete('DELETE FROM `'.DB_D_PREFIX.'product_association` WHERE rowid IN ('.implode(', ', $d_assoc_delete).')');
+	}
+	elseif(!empty($d_data['product_association'])) {
+		// Suppression de tout, puisque on a rien en entrée
+		DB::d_delete('DELETE FROM `'.DB_D_PREFIX.'product_association` WHERE `fk_product_pere`='.$id);
+	}
+
+	$this->pd_image_sync($o, $p_data);
+
+	// product_supplier
+	if (!empty($p_data['product_supplier'])) foreach($p_data['product_supplier'] as $row)
+		static::_action('supplier_price', 'pd', 'osync', 'product_supplier', $row['id_product_supplier']);
+}
+
+public function pd_image_sync($o, $p_data)
+{
+	$p_id = $p_data['product']['id_product'];
+	$pa_id = (!empty($p_data['product_attribute']) ?$p_data['product_attribute']['id_product_attribute'] :0);
+	
+	// Image
+	$sql = 'SELECT id_image
+		FROM ps_image
+		WHERE id_product='.$p_id.'
+		ORDER BY IF(cover=1, 0, 1)
+		LIMIT 1';
+	$q = DB::p_select($sql);
+	
+	foreach($q as $row) {
+		$i_id = $row['id_image'];
+		$p_filename = $i_id.'.jpg';
+		$p_folder = P_FOLDER_ROOT.'/img/p';
+		$n = strlen($i_id);
+		for($i=0; $i<$n; $i++)
+			$p_folder .= '/'.substr($i_id, $i, 1);
+		var_dump($p_f=$p_folder.'/'.$p_filename);
+		if (file_exists($p_f=$p_folder.'/'.$p_filename)) {
+			//var_dump($p_f);
+			if (!empty($p_data['product_attribute']))
+				$ref = $p_data['product_attribute']['reference'];
+			else
+				$ref = $p_data['product']['reference'];
+			if (empty($ref))
+				break;
+			$d_filename = $ref.'-'.$i_id.'.jpg';
+			$d_folder = D_FOLDER_ROOT.'/documents/produit/'.$ref;
+			if (!file_exists($d_folder))
+				mkdir($d_folder);
+			$d_f = $d_folder.'/'.$d_filename;
+			var_dump($d_f);
+			exec("rsync -a \"$p_f\" \"$d_f\"");
+		}
+	}
+}
+
+// -- MAPPING --
+
+public function pd_map_update(&$o, &$p_data, &$d_data=[])
+{
+	if ($o['p_tref']=='product_attribute')
+		return $this->pd_map_update_combi($o, $p_data, $d_data);
+	else
+		return $this->pd_map_update_simple($o, $p_data, $d_data);
+}
+public function pd_map_update_common(&$o, &$p_data, &$d_data=[])
+{
+}
+public function pd_map_update_simple(&$o, &$p_data, &$d_data=[])
+{
+	$d_data_map = [
+		'product' => [
+			'ref' => $p_data['product']['reference'],
+			'tms' => $p_data['product']['date_upd'],
+			'cost_price' => $p_data['product']['wholesale_price'],
+			'price' => $p_data['product']['price'],
+			'price_ttc' => $p_data['product']['price'],
+			'weight' => $p_data['product']['weight'],
+			//'tosell' => (($p_data['product']['available_for_order'] && ) ?1 :0),
+			'barcode' => static::_pd_map_barcode($p_data),
+			'fk_barcode_type' => static::_pd_map_barcode_type($p_data),
+		],
+		'product_extrafields' => [
+			'p_active' => $p_data['product']['active'],
+			'p_available_for_order' => $p_data['product']['available_for_order'],
+			'p_online_only' => $p_data['product']['online_only'],
+		],
+	];
+
+	// Pack
+	$d_data_map['product_association'] = [];
+	if (isset($p_data['pack'])) {
+		foreach($p_data['pack'] as $row) {
+			if ($row['id_product_attribute_item'])
+				$fk_product_fils = static::_pd_map_fk('product', 'product_attribute', $row['id_product_attribute_item']);
+			else
+				$fk_product_fils = static::_pd_map_fk('product', 'product', $row['id_product_item']);
+			if (!empty($fk_product_fils)) {
+				$d_data_map['product_association'][] = [
+					//'fk_product_pere' => $o['d_oid'],
+					'fk_product_fils' => $fk_product_fils,
+					'qty' => $row['quantity'],
+					'incdec' => 1,
+				];
+			}
+		}
+	}
+
+	return $d_data_map;
+}
+public function pd_map_update_combi(&$o, &$p_data, &$d_data=[])
+{
+	$d_data_map = [
+		'product' => [
+			'ref' => $p_data['product_attribute']['reference'],
+			'tms' => max($p_data['product_attribute']['date_upd'], $p_data['product']['date_upd'], (isset($d_data['product']['tms']) ?$d_data['product']['tms'] :0)),
+			'cost_price' => $p_data['product_attribute']['wholesale_price'],
+			'price' => ($p_data['product']['price']+$p_data['product_attribute']['price']),
+			'price_ttc' => ($p_data['product']['price']+$p_data['product_attribute']['price']),
+			'weight' => round($p_data['product']['weight']+$p_data['product_attribute']['weight'], 5),
+			'barcode' => static::_pd_map_barcode($p_data),
+			'fk_barcode_type' => static::_pd_map_barcode_type($p_data),
+		],
+		'product_extrafields' => [
+			'p_active' => $p_data['product']['active'],
+			'p_available_for_order' => $p_data['product']['available_for_order'],
+			'p_online_only' => $p_data['product']['online_only'],
+		],
+	];
+	
+	return $d_data_map;
+}
+
+public function pd_map_create(&$o, &$p_data)
+{
+	$o['d_tref'] = static::D_TABLE_MAIN;
+
+	if ($o['p_tref'] == 'product')
+		return $this->pd_map_create_simple($o, $p_data);
+	elseif ($o['p_tref'] == 'product_attribute')
+		return $this->pd_map_create_combi($o, $p_data);
+}
+public function pd_map_create_common(&$o, &$p_data)
+{
+}
+public function pd_map_create_simple(&$o, &$p_data)
+{
+	if (empty($p_data['product']['reference']))
+		return;
+
+	$d_data_map = [
+		'product' => [
+			//'rowid' => '',
+			'ref' => $p_data['product']['reference'],
+			'entity' => 1,
+			'ref_ext' => NULL,
+			'datec' => $p_data['product']['date_add'],
+			'tms' => $p_data['product']['date_upd'],
+			'fk_parent' => 0, // @todo voir si combi !
+			'label' => $p_data['product_lang']['name'],
+			'description' => $p_data['product_lang']['description_short'],
+			'note_public' => NULL,
+			'note' => NULL,
+			'customcode' => NULL,
+			'fk_country' => NULL,
+			'price' => $p_data['product']['price'],
+			'price_ttc' => $p_data['product']['price'],
+			'price_min' => '0',
+			'price_min_ttc' => '0',
+			'price_base_type' => 'HT',
+			'cost_price' => $p_data['product']['wholesale_price'],
+			'default_vat_code' => NULL,
+			'tva_tx' => static::_pd_map_tva_taux($p_data),
+			'recuperableonly' => '0',
+			'localtax1_tx' => '0',
+			'localtax1_type' => '0',
+			'localtax2_tx' => '0',
+			'localtax2_type' => '0',
+			'fk_user_author' => static::D_K_USER,
+			'fk_user_modif' => static::D_K_USER,
+			'tosell' => 1,
+			'tobuy' => 1,
+			'onportal' => 0,
+			'tobatch' => static::_pd_map_batch($p_data), // @todo 1 si dlc
+			'fk_product_type' => 0, // ?
+			'duration' => NULL,
+			'seuil_stock_alerte' => NULL,
+			'url' => '',
+			'barcode' => static::_pd_map_barcode($p_data),
+			'fk_barcode_type' => static::_pd_map_barcode_type($p_data),
+			'accountancy_code_sell' => NULL,
+			'accountancy_code_sell_intra' => NULL,
+			'accountancy_code_sell_export' => NULL,
+			'accountancy_code_buy' => NULL,
+			'accountancy_code_buy_intra' => NULL,
+			'accountancy_code_buy_export' => NULL,
+			'partnumber' => NULL,
+			'net_measure' => NULL,
+			'net_measure_units' => NULL,
+			'weight' => $p_data['product']['weight'],
+			'weight_units' => NULL,
+			'length' => $p_data['product']['depth'],
+			'length_units' => NULL,
+			'width' => $p_data['product']['width'],
+			'width_units' => NULL,
+			'height' => $p_data['product']['height'],
+			'height_units' => NULL,
+			'surface' => NULL,
+			'surface_units' => NULL,
+			'volume' => NULL,
+			'volume_units' => NULL,
+			'stock' => $p_data['stock_available']['quantity'],
+			'pmp' => '0',
+			'fifo' => NULL,
+			'lifo' => NULL,
+			'fk_default_warehouse' => static::D_K_WAREHOUSE,
+			'canvas' => NULL,
+			'finished' => NULL,
+			'hidden' => '0',
+			'import_key' => NULL,
+			'model_pdf' => NULL,
+			'fk_price_expression' => NULL,
+			'desiredstock' => '0', // @todo
+			'fk_unit' => NULL,
+			'price_autogen' => '0',
+			'fk_project' => NULL,
+			'fk_state' => NULL,
+			'lifetime' => NULL,
+			'qc_frequency' => NULL,
+			'batch_mask' => NULL,
+		],
+		'product_extrafields' => [
+			//'rowid' => '',
+			'tms' => static::$datetime,
+			//'fk_object' => '',
+			'import_key' => NULL,
+			'longdescript' => $p_data['product_lang']['description'],
+		],
+	];
+
+	return $d_data_map;
+}
+
+public function pd_map_create_combi(&$o, &$p_data)
+{
+	if (empty($p_data['product_attribute']['reference']))
+		return;
+	
+	$attributes = [];
+	foreach($p_data['product_attribute_combination'] as $row)
+		$attributes[] = $row['id_attribute'];
+	$attribute_names = implode(', ', $attributes);
+	
+	$d_data_map = [
+		'product' => [
+			//'rowid' => '',
+			'ref' => $p_data['product_attribute']['reference'],
+			'entity' => 1,
+			'ref_ext' => NULL,
+			'datec' => max($p_data['product_attribute']['date_upd'], $p_data['product']['date_add']),
+			'tms' => max($p_data['product_attribute']['date_upd'], $p_data['product']['date_upd']),
+			'fk_parent' => 0, // @todo voir si combi !
+			'label' => $p_data['product_lang']['name'].' - '.$attribute_names,
+			'description' => $p_data['product_lang']['description_short'],
+			'note_public' => NULL,
+			'note' => NULL,
+			'customcode' => NULL,
+			'fk_country' => NULL,
+			'price' => $p_data['product']['price']+$p_data['product_attribute']['price'],
+			'price_ttc' => $p_data['product']['price']+$p_data['product_attribute']['price'],
+			'price_min' => '0',
+			'price_min_ttc' => '0',
+			'price_base_type' => 'HT',
+			'cost_price' => $p_data['product_attribute']['wholesale_price'],
+			'default_vat_code' => NULL,
+			'tva_tx' => static::_pd_map_tva_taux($p_data),
+			'recuperableonly' => '0',
+			'localtax1_tx' => '0',
+			'localtax1_type' => '0',
+			'localtax2_tx' => '0',
+			'localtax2_type' => '0',
+			'fk_user_author' => static::D_K_USER,
+			'fk_user_modif' => static::D_K_USER,
+			'tosell' => 1,
+			'tobuy' => 1,
+			'onportal' => 0,
+			'tobatch' => static::_pd_map_batch($p_data), // @todo 1 si dlc
+			'fk_product_type' => 0, // ?
+			'duration' => NULL,
+			'seuil_stock_alerte' => NULL,
+			'url' => '',
+			'barcode' => static::_pd_map_barcode($p_data),
+			'fk_barcode_type' => static::_pd_map_barcode_type($p_data),
+			'accountancy_code_sell' => NULL,
+			'accountancy_code_sell_intra' => NULL,
+			'accountancy_code_sell_export' => NULL,
+			'accountancy_code_buy' => NULL,
+			'accountancy_code_buy_intra' => NULL,
+			'accountancy_code_buy_export' => NULL,
+			'partnumber' => NULL,
+			'net_measure' => NULL,
+			'net_measure_units' => NULL,
+			'weight' => $p_data['product']['weight']+$p_data['product_attribute']['weight'],
+			'weight_units' => NULL,
+			'length' => $p_data['product']['depth'],
+			'length_units' => NULL,
+			'width' => $p_data['product']['width'],
+			'width_units' => NULL,
+			'height' => $p_data['product']['height'],
+			'height_units' => NULL,
+			'surface' => NULL,
+			'surface_units' => NULL,
+			'volume' => NULL,
+			'volume_units' => NULL,
+			'stock' => $p_data['stock_available']['quantity'],
+			'pmp' => '0',
+			'fifo' => NULL,
+			'lifo' => NULL,
+			'fk_default_warehouse' => static::D_K_WAREHOUSE,
+			'canvas' => NULL,
+			'finished' => NULL,
+			'hidden' => '0',
+			'import_key' => NULL,
+			'model_pdf' => NULL,
+			'fk_price_expression' => NULL,
+			'desiredstock' => '0', // @todo
+			'fk_unit' => NULL,
+			'price_autogen' => '0',
+			'fk_project' => NULL,
+			'fk_state' => NULL,
+			'lifetime' => NULL,
+			'qc_frequency' => NULL,
+			'batch_mask' => NULL,
+		],
+		'product_extrafields' => [
+			//'rowid' => '',
+			'tms' => static::$datetime,
+			//'fk_object' => '',
+			'import_key' => NULL,
+			'longdescript' => $p_data['product_lang']['description'],
+		],
+	];
+
+	return $d_data_map;
+}
+
+// -----------
+
+// Field MAPPING
+
+public static function _pd_map_batch(&$orig_data, &$dest_tree=[])
+{
+	// @todo vérif si besoin
+	return (PRODUCT_LOT || !empty($orig_data['products_dlc_dluo']) ?1 :0);
+}
+
+public static function _pd_map_tva_taux(&$orig_data, &$dest_tree=[])
+{
+	// @todo mapper les taxes proprement !!
+	switch($orig_data['product']['id_tax_rules_group']) {
+		case 1:
+			return 20;
+		case 2:
+			return 10;
+		case 3:
+			return 5.5;
+		case 4:
+			return 2.1;
+		case 5:
+			return 0;
+	}
+}
+
+public static function _pd_map_barcode(&$orig_data, &$dest_tree=[])
+{
+	if (isset($orig_data['product_attribute'])) {
+		if ($orig_data['product_attribute']['ean13'])
+			return $orig_data['product_attribute']['ean13'];
+		if ($orig_data['product_attribute']['upc'])
+			return $orig_data['product_attribute']['upc'];
+		if ($orig_data['product_attribute']['isbn'])
+			return $orig_data['product_attribute']['isbn'];
+	}
+	else {
+		if ($orig_data['product']['ean13'])
+			return $orig_data['product']['ean13'];
+		if ($orig_data['product']['upc'])
+			return $orig_data['product']['upc'];
+		if ($orig_data['product']['isbn'])
+			return $orig_data['product']['isbn'];
+	}
+}
+public static function _pd_map_barcode_type(&$orig_data, &$dest_tree=[])
+{
+	if (isset($orig_data['product_attribute'])) {
+		if ($orig_data['product_attribute']['ean13'])
+			return 2;
+		if ($orig_data['product_attribute']['upc'])
+			return 3;
+		if ($orig_data['product_attribute']['isbn'])
+			return 4;
+	}
+	else {
+		if ($orig_data['product']['ean13'])
+			return 2;
+		if ($orig_data['product']['upc'])
+			return 3;
+		if ($orig_data['product']['isbn'])
+			return 4;
+	}
+}
+
+}
+
+sync_product_pd::__init();

+ 45 - 0
src/sync/product_lot/common.inc.php

@@ -0,0 +1,45 @@
+<?php
+
+// Order
+
+class sync_product_lot extends sync
+{
+
+const MODEL_NAME='product_lot';
+const MODEL_ID = 9;
+const MAP_ID = 7;
+const D_TABLE_MAIN = 'product_lot';
+const P_TABLE_MAIN = 'products_dlc_dluo';
+const P_TABLE_MAIN_ID = 'id';
+
+function d_data($otype, $oid)
+{
+	$data = [];
+	// Lot
+	$data['product_lot'] = DB::d_get_row('product_lot', ['rowid'=>$oid]);
+	$product_id = $data['product_lot']['fk_product'];
+	$batch = $data['product_lot']['batch'];
+	$data['product_lot_extrafields'] = DB::d_get_row('product_lot_extrafields', ['fk_object'=>$oid]);
+	
+	// Stock
+	$data['product_stock'] = DB::d_get_row('product_stock', ['fk_product'=>$product_id, 'fk_entrepot'=>static::D_K_WAREHOUSE]);
+	$stock_id = $data['product_stock']['rowid'];
+	
+	// Batch
+	$data['product_batch'] = DB::d_get_row('product_batch', ['fk_product_stock'=>$stock_id, 'batch'=>$batch]);
+
+	return $data;
+}
+
+function p_data($otype, $oid)
+{
+	$data = [];
+	$data['products_dlc_dluo'] = DB::p_get_row('products_dlc_dluo', ['id'=>$oid]);
+	
+	if (!empty($data['products_dlc_dluo']))
+		$data['stock_available'] = DB::p_get_row('stock_available', ['id_product'=>$data['products_dlc_dluo']['id_product'], 'id_product_attribute'=>$data['products_dlc_dluo']['id_combinaison'], 'id_shop'=>static::P_ID_SHOP]);
+	
+	return $data;
+}
+
+}

+ 138 - 0
src/sync/product_lot/dp.inc.php

@@ -0,0 +1,138 @@
+<?php
+
+class sync_product_lot_dp extends sync_product_lot
+{
+
+// -- ACTIONS --
+
+public function execute_osync($otype, $oid)
+{
+	return $this->dp_execute_osync($otype, $oid);
+}
+
+public function execute_create($otype, $oid)
+{
+	return $this->create($otype, $oid);
+}
+
+// -- SUB ACTIONS --
+
+public function create($otype, $oid)
+{
+	return $this->dp_create($otype, $oid);
+}
+
+public function osync($o)
+{
+	return $this->dp_osync($o);
+}
+
+public function dp_insert_more(&$o, &$p_data_map, &$d_data)
+{
+	if (!empty($d_data['product_stock'])) {
+		static::_action('stock', 'dp', 'osync', 'product_stock', $d_data['product_stock']['rowid']);
+	}
+}
+public function dp_update_more(&$o, &$p_data_map, &$p_data_map_create, &$p_data, &$d_data)
+{
+	// Décalage is_current_stock
+	if (!empty($p_data['products_dlc_dluo'])) if ($p_data_map['products_dlc_dluo']['stock']==0 && $p_data['products_dlc_dluo']['stock']>0 && $p_data['products_dlc_dluo']['is_current_stock']==1) {
+		$sql = 'UPDATE ps_products_dlc_dluo
+			SET is_current_stock=0
+			WHERE id='.$o['p_oid'];
+		DB::p_update($sql);
+
+		$sql = 'UPDATE ps_products_dlc_dluo
+			SET is_current_stock=is_current_stock-1
+			WHERE id_product='.$p_data['products_dlc_dluo']['id_product'].' AND id_combinaison='.$p_data['products_dlc_dluo']['id_combinaison'].' AND is_current_stock>1';
+		DB::p_update($sql);
+	}
+
+	if (!empty($d_data['product_stock'])) {
+		 static::_action('stock', 'dp', 'osync', 'product_stock', $d_data['product_stock']['rowid']);
+	}
+}
+
+// -- MAPPING --
+
+public function map_update(&$o, &$d_data, &$p_data=[])
+{
+	$p_data_map = [
+		'products_dlc_dluo' => [
+			//'id' => '',
+			//'id_product' => '',
+			//'id_combinaison' => '',
+			//'id_warehouse' => '',
+			//'is_current_stock' => '',
+			//'numero_lot' => '',
+			'dlc' => !empty($d_data['product_lot']['sellby']) ?$d_data['product_lot']['sellby'] :'0000-00-00',
+			'dluo' => !empty($d_data['product_lot']['eatby']) ?$d_data['product_lot']['eatby'] :'0000-00-00',
+			'stock' => (isset($d_data['product_batch']) ?$d_data['product_batch']['qty'] :0),
+			//'entry_date' => '',
+			//'alert1' => '',
+			//'alert2' => '',
+		],
+	];
+
+	return $p_data_map;
+}
+
+public function map_create(&$o, &$d_data)
+{
+	if (empty($product = static::_p_map_fk_product($d_data)))
+		return;
+	//var_dump($product);
+
+	// Position actuelle !
+	$rows = DB::p_get_rows('products_dlc_dluo', ['id_product'=>$product['id_product'], 'id_combinaison'=>$product['id_product_attribute']]);
+	$position = 1;
+	if (is_array($rows)) {
+		// @todo analyser et vérifier afin qu'on ait bien les bonnes valeurs...
+		foreach($rows as $row)
+			if ($row['is_current_stock']>0 && ($position <= $row['is_current_stock']))
+				$position = $row['is_current_stock']+1;
+	}
+	//var_dump($position); var_dump($rows); die();
+
+	$p_data_map = [
+		'products_dlc_dluo' => [
+			//'id' => '',
+			'id_product' => $product['id_product'],
+			'id_combinaison' => $product['id_product_attribute'],
+			'id_warehouse' => static::P_ID_WAREHOUSE,
+			'is_current_stock' => $position,
+			'numero_lot' => $d_data['product_lot']['batch'],
+			//'dlc' => $d_data['product_lot']['sellby'],
+			//'dluo' => $d_data['product_lot']['eatby'],
+			//'stock' => $d_data['product_batch']['qty'],
+			'entry_date' => substr($d_data['product_lot']['datec'], 0, 10),
+			'alert1' => static::P_DLC_ALERT1,
+			'alert2' => static::P_DLC_ALERT2,
+		],
+	];
+
+	return $p_data_map;
+}
+
+// -----------
+
+// Field MAPPING
+
+public static function _p_map_fk_product(&$orig_data, &$dest_tree=[])
+{
+	// Product type
+	$o = static::d_o_oid('product', 'product', $orig_data['product_lot']['fk_product']);
+	if (empty($o))
+		return;
+	if ($o['p_tref']=='product')
+		return ['id_product'=>$o['p_oid'], 'id_product_attribute'=>0];
+	elseif ($o['p_tref']=='product_attribute') {
+		$oid = $o['p_oid'];
+		$row = DB::p_get_row('product_attribute', ['id_product_attribute'=>$oid]);
+		return ['id_product'=>$row['id_product'], 'id_product_attribute'=>$oid];
+	}
+}
+
+}
+
+sync_product_lot_dp::__init();

+ 143 - 0
src/sync/product_lot/pd.inc.php

@@ -0,0 +1,143 @@
+<?php
+
+class sync_product_lot_pd extends sync_product_lot
+{
+
+// -- ACTIONS --
+
+public function execute_osync($otype, $oid)
+{
+	return $this->pd_execute_osync($otype, $oid);
+}
+
+public function execute_create($otype, $oid)
+{
+	return $this->create($otype, $oid);
+}
+
+// -- SUB ACTIONS --
+
+public function create($otype, $oid)
+{
+	return $this->pd_create($otype, $oid);
+}
+public function pd_insert_more(&$o, &$d_data_map, &$p_data)
+{
+	$id = $o['d_oid'];
+	
+	// product_batch
+	//$this->d_insert_row('product_lot_extrafields', ['fk_object'=>$id], $d_data_map); //@todo vérif identique !
+	//$d_data_map['product_lot_extrafields']['fk_object'] = $id;
+	//DB::d_insert_row('product_lot_extrafields', $d_data_map['product_lot_extrafields']);
+	
+	// product_batch
+	//DB::d_insert_row('product_batch', $d_data_map['product_batch']);
+}
+
+public function osync($o)
+{
+	return $this->pd_osync($o);
+}
+public function pd_update_more(&$o, &$d_data_map, &$d_data_map_create, &$d_data, &$p_data)
+{
+	$id = $o['d_oid'];
+	// @todo
+	
+	static::d_update_row('product_lot_extrafields', (!empty($d_data['product_lot_extrafields']) ?['rowid'=>$d_data['product_lot_extrafields']['rowid']] :['fk_object' => $id]), $d_data_map, $d_data_map_create, $d_data);
+	static::d_update_row('product_batch', (!empty($d_data['product_batch']) ?['rowid'=>$d_data['product_batch']['rowid']] :[]), $d_data_map, $d_data_map_create, $d_data);
+}
+
+// -- MAPPING --
+
+public function map_update(&$o, &$p_data, &$d_data=[])
+{
+	// @todo actuellement désactivé à vérifier...
+	if (false) {
+		return [
+			'product_lot' => [
+			],
+			'product_lot_extrafields' => [
+			],
+			'product_batch' => [
+			],
+		];
+	}
+	
+	// Stock
+	$fk_product_stock = static::_pd_map_fk('stock', 'stock_available', $p_data['stock_available']['id_stock_available']);
+	
+	$p_data_map = [
+		'product_lot' => [
+		],
+		'product_lot_extrafields' => [
+		],
+		'product_batch' => [
+			'qty' => $p_data['products_dlc_dluo']['stock'],
+		],
+	];
+
+	return $p_data_map;
+}
+
+public function map_create(&$o, &$p_data)
+{
+	$eatby = $p_data['products_dlc_dluo']['dluo']!='0000-00-00' ?$p_data['products_dlc_dluo']['dluo'] :NULL;
+	$sellby = $p_data['products_dlc_dluo']['dlc']!='0000-00-00' ?$p_data['products_dlc_dluo']['dlc'] :NULL;
+	$batch = $p_data['products_dlc_dluo']['numero_lot'];
+	$fk_product = static::_pd_map_fk_product($p_data);
+	
+	// Stock
+	$fk_product_stock = static::_pd_map_fk('stock', 'stock_available', $p_data['stock_available']['id_stock_available']);
+	
+	$d_data_map = [
+		'product_lot' => [
+			//'rowid' => '',
+			'entity' => 1,
+			'fk_product' => $fk_product,
+			'batch' => $batch,
+			'eatby' => $eatby,
+			'sellby' => $sellby,
+			'datec' => $p_data['products_dlc_dluo']['entry_date'].' 00:00:00',
+			'tms' => static::$datetime,
+			'fk_user_creat' => static::D_K_USER,
+			'fk_user_modif' => static::D_K_USER,
+			'import_key' => NULL,
+			'eol_date' => NULL,
+			'manufacturing_date' => NULL,
+			'scrapping_date' => NULL,
+		],
+		'product_lot_extrafields' => [
+		],
+		'product_batch' => [
+			//'rowid' => '',
+			'tms' => static::$datetime,
+			'fk_product_stock' => $fk_product_stock,
+			'eatby' => $eatby,
+			'sellby' => $sellby,
+			'batch' => $batch,
+			'qty' => $p_data['products_dlc_dluo']['stock'],
+			'import_key' => NULL,
+		],
+	];
+
+	return $d_data_map;
+}
+
+// -----------
+
+// Field MAPPING
+
+public static function _pd_map_fk_product(&$orig_data, &$dest_tree=[])
+{
+	// Product type
+	if ($orig_data['products_dlc_dluo']['id_combinaison']) {
+		return static::_pd_map_fk('product', 'product_attribute', $orig_data['products_dlc_dluo']['id_combinaison']);
+	}
+	else {
+		return static::_pd_map_fk('product', 'product', $orig_data['products_dlc_dluo']['id_product']);
+	}
+}
+
+}
+
+sync_product_lot_pd::__init();

+ 49 - 0
src/sync/shipping/common.inc.php

@@ -0,0 +1,49 @@
+<?php
+
+// Order
+
+class sync_shipping extends sync
+{
+
+const MODEL_NAME = 'shipping';
+const MODEL_ID = 23;
+const MAP_ID = 14;
+const D_TABLE_MAIN = 'expedition';
+const P_TABLE_MAIN = 'order_slip';
+const P_TABLE_MAIN_ID = 'id_order_slip';
+
+function d_data($otype, $oid)
+{
+	$data = [];
+
+	// shipping
+	$data['expedition'] = DB::d_get_row('expedition', ['rowid'=>$oid]);
+
+	return $data;
+}
+
+function p_data($otype, $oid)
+{
+	$data = [];
+
+	// order_slip
+	$data['order_slip'] = DB::p_get_row('order_slip', ['id_order_slip'=>$oid]);
+	$id_order = $data['order_slip']['id_order'];
+
+	// order
+	$data['orders'] = DB::p_get_row('orders', ['id_order'=>$id_order]);
+	// order_detail
+	$data['order_detail'] = DB::p_get_rows('order_detail', ['id_order'=>$id_order]);
+
+	// order_carrier
+	$data['order_carrier'] = DB::p_get_rows('order_carrier', ['id_order'=>$id_order]);
+
+	// order_slip_detail
+	$data['order_slip_detail'] = DB::p_get_rows('order_slip_detail', ['id_order_slip'=>$oid]);
+	// order_slip_detail_tax
+	//$data['order_slip_detail_tax'] = DB::p_get_rows('order_slip_detail_tax', ['id_order_slip'=>$oid]);
+
+	return $data;
+}
+
+}

+ 49 - 0
src/sync/shipping_detail/common.inc.php

@@ -0,0 +1,49 @@
+<?php
+
+// Shipping line
+
+class sync_shipping_detail extends sync
+{
+
+const MODEL_NAME = 'shipping_detail';
+const MODEL_ID = 24;
+const MAP_ID = 15;
+const D_TABLE_MAIN = 'expeditiondet';
+const P_TABLE_MAIN = 'order_slip_detail';
+const P_TABLE_MAIN_ID = 'id_order_slip_detail';
+
+function d_data($otype, $oid)
+{
+	$data = [];
+
+	// shipping
+	$data['expeditiondet'] = DB::d_get_row('expeditiondet', ['rowid'=>$oid]);
+
+	return $data;
+}
+
+function p_data($otype, $oid)
+{
+	$data = [];
+
+	// order_slip
+	$data['order_slip_detail'] = DB::p_get_row('order_slip_detail', ['id_order_slip_detail'=>$oid]);
+	$id_order = $data['order_slip']['id_order'];
+
+	// order
+	$data['orders'] = DB::p_get_row('orders', ['id_order'=>$id_order]);
+	// order_detail
+	$data['order_detail'] = DB::p_get_rows('order_detail', ['id_order'=>$id_order]);
+
+	// order_carrier
+	$data['order_carrier'] = DB::p_get_rows('order_carrier', ['id_order'=>$id_order]);
+
+	// order_slip_detail
+	$data['order_slip_detail'] = DB::p_get_rows('order_slip_detail', ['id_order_slip'=>$oid]);
+	// order_slip_detail_tax
+	//$data['order_slip_detail_tax'] = DB::p_get_rows('order_slip_detail_tax', ['id_order_slip'=>$oid]);
+
+	return $data;
+}
+
+}

+ 61 - 0
src/sync/stock/common.inc.php

@@ -0,0 +1,61 @@
+<?php
+
+// Order
+
+class sync_stock extends sync
+{
+
+const MODEL_NAME = 'stock';
+const MODEL_ID = 4;
+const MAP_ID = 12;
+const D_TABLE_MAIN = 'product_stock';
+const P_TABLE_MAIN = 'stock_available';
+const P_TABLE_MAIN_ID = 'id_stock_available';
+
+function d_data($otype, $oid)
+{
+	$data = [];
+
+	// product_stock
+	$data['product_stock'] = DB::d_get_row('product_stock', ['rowid'=>$oid]);
+	
+	if (!empty($data['product_stock'])) {
+		// product
+		$data['product'] = DB::d_get_row('product', ['rowid'=>$data['product_stock']['fk_product']]);
+	}
+	
+	// product_batch (stock par lot)
+	$data['product_batch'] = DB::d_get_rows('product_batch', ['fk_product_stock'=>$oid]);
+	
+	var_dump($data);
+	
+	return $data;
+}
+
+function p_data($otype, $oid)
+{
+	$data = [];
+
+	// stock_available
+	$data['stock_available'] = DB::p_get_row('stock_available', ['id_stock_available'=>$oid]);
+	$p_id = $data['stock_available']['id_product'];
+	var_dump($p_id);
+
+	// product
+	$data['product'] = DB::p_get_row('product', ['id_product'=>$p_id]);
+
+	// product_shop
+	$data['product_shop'] = DB::p_get_row('product_shop', ['id_product'=>$p_id, 'id_shop'=>static::P_ID_SHOP]);
+
+	if ($pa_id=$data['stock_available']['id_product_attribute']) {
+		// product_attribute
+		$data['product_attribute'] = DB::p_get_row('product_attribute', ['id_product_attribute'=>$pa_id]);
+
+		// product_attribute_shop
+		$data['product_attribute_shop'] = DB::p_get_row('product_attribute_shop', ['id_product_attribute'=>$pa_id, 'id_shop'=>static::P_ID_SHOP]);
+	}
+
+	return $data;
+}
+
+}

+ 138 - 0
src/sync/stock/dp.inc.php

@@ -0,0 +1,138 @@
+<?php
+
+class sync_stock_dp extends sync_stock
+{
+
+// -- ACTIONS --
+
+public function execute_osync($otype, $oid)
+{
+	return $this->dp_execute_osync($otype, $oid);
+}
+
+public function execute_create($otype, $oid)
+{
+	return $this->create($otype, $oid);
+}
+
+// -- SUB ACTIONS --
+
+public function create($otype, $oid)
+{
+	return $this->dp_create($otype, $oid);
+}
+
+public function osync($o)
+{
+	return $this->dp_osync($o);
+}
+
+// -- MAPPING --
+
+public function d_reserved_qty(&$o, &$d_data)
+{
+	$qty = 0;
+
+	// @todo : soucis ici, il faudrait balancer un message d'erreur...
+	if (empty($d_data['product_stock']['fk_product']))
+		return 0;
+
+	$fk_product = $d_data['product_stock']['fk_product'];
+
+	// Commandes brouilons et validées
+	// c.fk_statut IN (0, 1, 2) => brouillons, validés, envoi en cours
+	$sql = 'SELECT COUNT(DISTINCT c.fk_soc) as nb_customers, COUNT(DISTINCT c.rowid) as nb, COUNT(cd.rowid) as nb_rows, SUM(cd.qty) as qty
+		FROM llx_commandedet as cd
+		INNER JOIN llx_commande as c ON c.rowid = cd.fk_commande
+		WHERE cd.fk_product = '.$fk_product.' AND c.fk_statut in (0, 1, 2)';
+	$q = DB::d_select($sql);
+	if (!empty($cmd_row = $q->fetch_assoc()))
+		$qty += $cmd_row['qty'];
+	var_dump($cmd_row);
+
+	// Expéditions effectuées sur les commandes en cours
+	// e.fk_statut IN (1, 2) => validated et closed
+	$sql = 'SELECT COUNT(DISTINCT e.fk_soc) as nb_customers, COUNT(DISTINCT e.rowid) as nb, COUNT(ed.rowid) as nb_rows, SUM(ed.qty) as qty
+		FROM llx_expeditiondet as ed
+		INNER JOIN llx_commandedet as cd ON cd.rowid=ed.fk_origin_line
+		INNER JOIN llx_commande as c ON c.rowid = cd.fk_commande
+		INNER JOIN llx_expedition as e ON e.rowid = ed.fk_expedition
+		WHERE cd.fk_product = '.$fk_product.' AND c.fk_statut IN (0, 1, 2) AND e.fk_statut IN (1, 2)';
+	$q = DB::d_select($sql);
+	if (!empty($exp_row = $q->fetch_assoc()))
+		$qty -= $exp_row['qty'];
+	var_dump($exp_row);
+
+	//var_dump($row);
+
+	return $qty;
+}
+
+public function map_update(&$o, &$d_data, &$p_data=[])
+{
+	$reserved_qty = $this->d_reserved_qty($o, $d_data);
+
+	$p_data_map = [
+		'stock_available' => [
+			'quantity' => $d_data['product_stock']['reel'] - $reserved_qty, // @todo
+			'physical_quantity' => $d_data['product_stock']['reel'],
+			'reserved_quantity' => $reserved_qty,
+		],
+	];
+
+	return $p_data_map;
+}
+
+public function map_create(&$o, &$d_data)
+{
+	$product = static::_d_map_product($d_data);
+	var_dump($product);
+	
+	$p_data_map = [
+		'stock_available' => [
+			//'id_stock_available' => '',
+			'id_product' => $product['id_product'],
+			'id_product_attribute' => $product['id_product_attribute'],
+			'id_shop' => static::P_ID_SHOP,
+			'id_shop_group' => 0,
+			'quantity' => $d_data['product_stock']['reel'], // @todo
+			'physical_quantity' => $d_data['product_stock']['reel'],
+			'reserved_quantity' => 0,
+			'depends_on_stock' => 0,
+			'out_of_stock' => static::P_OUT_OF_STOCK_DEFAULT,
+			'location' => '',
+		],
+	];
+
+	return $p_data_map;
+}
+
+// -----------
+
+// Field MAPPING
+
+public static function _d_map_product(&$orig_data, &$dest_tree=[])
+{
+	$product = [];
+	// Déjà mappé
+	if ($o = static::d_o_oid('product', 'product', $orig_data['product_stock']['fk_product'])) {
+		// Combi
+		if ($o['p_tref']=='product_attribute') {
+			$row = DB::p_get_row('product_attribute', ['id_product_attribute'=>$o['p_oid']]);
+			return ['id_product'=>$row['id_product'], 'id_product_attribute'=>$o['p_oid']];
+		}
+		// Simple
+		else {
+			return ['id_product'=>$o['p_oid'], 'id_product_attribute'=>0];
+		}
+	}
+	// Création simple
+	else {
+		$o = static::_action('product', 'dp', 'create', 'product', $orig_data['product_stock']['fk_product']);
+		return ['id_product'=>$o['p_oid'], 'id_product_attribute'=>0];
+	}
+}
+
+}
+
+sync_stock_dp::__init();

+ 108 - 0
src/sync/stock/pd.inc.php

@@ -0,0 +1,108 @@
+<?php
+
+class sync_stock_pd extends sync_stock
+{
+
+// -- ACTIONS --
+
+public function execute_osync($otype, $oid)
+{
+	return $this->pd_execute_osync($otype, $oid);
+}
+
+public function execute_create($otype, $oid)
+{
+	return $this->create($otype, $oid);
+}
+
+// -- SUB ACTIONS --
+
+public function create($otype, $oid)
+{
+	return $this->pd_create($otype, $oid);
+}
+
+public function osync($o)
+{
+	return $this->pd_osync($o);
+}
+
+// @todo pd_insert_more
+
+public function pd_update_more(&$o, &$d_data_map, &$d_data_map_create, &$d_data, &$p_data)
+{
+	$id = $o['d_oid'];
+	// @todo
+	
+	if (!empty($d_data['product']))
+		static::d_update_row('product', ['rowid'=>$d_data['product']['rowid']], $d_data_map, $d_data_map_create, $d_data);
+}
+
+// -- MAPPING --
+
+public function map_update(&$o, &$p_data, &$d_data=[])
+{
+	if (true) {
+		return [
+			'product_stock' => [
+			],
+			'product' => [
+			],
+		];
+	}
+
+	$d_data_map = [
+		'product_stock' => [
+			'reel' => $p_data['stock_available']['physical_quantity'],
+		],
+		'product' => [
+			'stock' => $p_data['stock_available']['physical_quantity'],
+		],
+	];
+
+	return $d_data_map;
+}
+
+public function map_create(&$o, &$p_data)
+{
+	$fk_product = static::_pd_map_fk_product($p_data);
+	var_dump($fk_product); //die();
+	if (empty($fk_product))
+		return;
+	
+	//$p_data['stock_available']['quantity']
+	//$p_data['stock_available']['physical_quantity']
+	//$p_data['stock_available']['reserved_quantity']
+	
+	$d_data_map = [
+		'product_stock' => [
+			//'rowid' => '',
+			'tms' => static::$datetime,
+			'fk_product' => $fk_product,
+			'fk_entrepot' => static::D_K_WAREHOUSE,
+			'reel' => $p_data['stock_available']['physical_quantity'],
+			'import_key' => NULL,
+		],
+	];
+
+	return $d_data_map;
+}
+
+// -----------
+
+// Field MAPPING
+
+public static function _pd_map_fk_product(&$orig_data, &$dest_tree=[])
+{
+	// Product type
+	if ($orig_data['stock_available']['id_product_attribute']) {
+		return static::_pd_map_fk('product', 'product_attribute', $orig_data['stock_available']['id_product_attribute']);
+	}
+	else {
+		return static::_pd_map_fk('product', 'product', $orig_data['stock_available']['id_product']);
+	}
+}
+
+}
+
+sync_stock_pd::__init();

+ 43 - 0
src/sync/supplier/common.inc.php

@@ -0,0 +1,43 @@
+<?php
+
+// Order
+
+class sync_supplier extends sync
+{
+
+const MODEL_NAME = 'supplier';
+const MODEL_ID = 8;
+const MAP_ID = 3;
+const D_TABLE_MAIN = 'societe';
+const D_TABLE_MAIN_REF = 'code_fournisseur';
+const P_TABLE_MAIN = 'supplier';
+const P_TABLE_MAIN_ID = 'id_supplier';
+
+protected static $nb;
+const D_CODE = 'SU';
+
+function d_data($otype, $oid)
+{
+	$data = [];
+	$data['societe'] = DB::d_get_row('societe', ['rowid'=>$oid]);
+	
+	// adress
+	$data['socpeople'] = DB::d_get_rows('socpeople', ['fk_soc'=>$oid]);
+
+	return $data;
+}
+
+function p_data($otype, $oid)
+{
+	$data = [];
+	$data['supplier'] = DB::p_get_row('supplier', ['id_supplier'=>$oid]);
+	$data['supplier_lang'] = DB::p_get_row('supplier_lang', ['id_supplier'=>$oid, 'id_lang'=>static::P_ID_LANG]);
+	$data['supplier_shop'] = DB::p_get_row('supplier_shop', ['id_supplier'=>$oid, 'id_shop'=>static::P_ID_SHOP]);
+	
+	// address
+	$data['address'] = DB::p_get_row('address', ['id_supplier'=>$oid]);
+	
+	return $data;
+}
+
+}

+ 78 - 0
src/sync/supplier/dp.inc.php

@@ -0,0 +1,78 @@
+<?php
+
+class sync_supplier_dp extends sync_supplier
+{
+
+// -- ACTIONS --
+
+public function execute_osync($otype, $oid)
+{
+	return $this->dp_execute_osync($otype, $oid);
+}
+
+public function execute_create($otype, $oid)
+{
+	return $this->create($otype, $oid);
+}
+
+// -- SUB ACTIONS --
+
+public function create($otype, $oid)
+{
+	return $this->dp_create($otype, $oid);
+}
+
+public function osync($o)
+{
+	return $this->dp_osync($o);
+}
+
+// -- MAPPING --
+
+public function map_update(&$o, &$d_data, &$p_data=[])
+{
+	$p_data_map = [
+		'supplier' => [
+			'name' => $d_data['societe']['nom'],
+			'date_upd' => $d_data['societe']['tms'],
+		],
+	];
+
+	return $p_data_map;
+}
+
+public function map_create(&$d_data)
+{
+	$p_data_map = [
+		'supplier' => [
+			//'id_supplier' => '',
+			'name' => $d_data['societe']['nom'],
+			'date_add' => $d_data['societe']['datec'],
+			'date_upd' => $d_data['societe']['tms'],
+			'active' => 1,
+		],
+		'supplier_lang' => [
+			//'id_supplier' => '',
+			'id_lang' => static::P_ID_LANG,
+			'description' => '',
+			'meta_title' => '',
+			'meta_keywords' => '',
+			'meta_description' => '',
+		],
+		'supplier_lang' => [
+			//'id_supplier' => '',
+			'id_shop' => static::P_ID_SHOP,
+		],
+	];
+
+	return $p_data_map;
+}
+
+// -----------
+
+// Field MAPPING
+
+
+}
+
+sync_supplier_dp::__init();

+ 192 - 0
src/sync/supplier/pd.inc.php

@@ -0,0 +1,192 @@
+<?php
+
+class sync_supplier_pd extends sync_supplier
+{
+
+// -- ACTIONS --
+
+public function execute_osync($otype, $oid)
+{
+	return $this->pd_execute_osync($otype, $oid);
+}
+
+public function execute_create($otype, $oid)
+{
+	return $this->create($otype, $oid);
+}
+
+// -- SUB ACTIONS --
+
+public function create($otype, $oid)
+{
+	return $this->pd_create($otype, $oid);
+}
+public function pd_insert_more(&$o, &$d_data_map, &$p_data)
+{
+	$id = $o['d_oid'];
+
+	if (!empty($p_data['address']))
+		static::_action('address', 'pd', 'osync', 'address', $p_data['address']['id_address']);
+}
+
+public function osync($o)
+{
+	return $this->pd_osync($o);
+}
+public function pd_update_more(&$o, &$d_data_map, &$d_data_map_create, &$d_data, &$p_data)
+{
+	$id = $o['d_oid'];
+	
+	if (!empty($p_data['address']))
+		static::_action('address', 'pd', 'osync', 'address', $p_data['address']['id_address']);
+}
+
+// -- MAPPING --
+
+public function map_update(&$o, &$p_data, &$d_data=[])
+{
+	$d_data_map = [
+		'societe' => [
+			'nom' => $p_data['supplier']['name'],
+			//'address' => (!empty($p_data['address']) ?$p_data['address']['address1'].($p_data['address']['address2'] ?"\r\n".$p_data['address']['address2'] :'') :''),
+			//'zip' => (!empty($p_data['address']) ?$p_data['address']['postcode'] :NULL),
+			//'town' => (!empty($p_data['address']) ?$p_data['address']['city'] :NULL),
+			//'fk_pays' => static::_pd_map_fk_pays($p_data),
+			////'phone' => (!empty($p_data['address']) ?$p_data['address']['phone'] :NULL),
+			'fk_user_modif' => static::D_K_USER,
+			'tms' => $p_data['supplier']['date_upd'],
+		],
+	];
+
+	return $d_data_map;
+}
+
+public function map_create(&$o, &$p_data)
+{
+	$d_data_map = [
+		'societe' => [
+			//'rowid' => NULL,
+			'nom' => $p_data['supplier']['name'],
+			'name_alias' => NULL,
+			'entity' => 1,
+			'ref_ext' => NULL,
+			'ref_int' => NULL,
+			'statut' => 1, // activé, sinon inutile...
+			'parent' => NULL,
+			'status' => 1,
+			'code_client' => NULL,
+			'code_fournisseur' => static::d_ref($p_data['supplier']['date_add']),
+			'code_compta' => NULL,
+			'code_compta_fournisseur' => NULL,
+			'address' => (!empty($p_data['address']) ?$p_data['address']['address1'].($p_data['address']['address2'] ?"\r\n".$p_data['address']['address2'] :'') :''),
+			'zip' => (!empty($p_data['address']) ?$p_data['address']['postcode'] :NULL),
+			'town' => (!empty($p_data['address']) ?$p_data['address']['city'] :NULL),
+			'fk_departement' => NULL,
+			'fk_pays' => static::_pd_map_fk_pays($p_data),
+			'fk_account' => NULL,
+			'phone' => (!empty($p_data['address']) ?$p_data['address']['phone'] :NULL),
+			'fax' => NULL,
+			'url' => NULL,
+			'email' => NULL,
+			'socialnetworks' => NULL,
+			'skype' => NULL,
+			'twitter' => NULL,
+			'facebook' => NULL,
+			'linkedin' => NULL,
+			'instagram' => NULL,
+			'snapchat' => NULL,
+			'googleplus' => NULL,
+			'youtube' => NULL,
+			'whatsapp' => NULL,
+			'fk_effectif' => NULL,
+			'fk_typent' => 0,
+			'fk_forme_juridique' => NULL,
+			'fk_currency' => NULL,
+			'siren' => NULL,
+			'siret' => NULL,
+			'ape' => NULL,
+			'idprof4' => '',
+			'idprof5' => '',
+			'idprof6' => '',
+			'tva_intra' => '',
+			'capital' => NULL,
+			'fk_stcomm' => 0,
+			'note_private' => NULL,
+			'note_public' => NULL,
+			'model_pdf' => NULL,
+			'prefix_comm' => NULL,
+			'client' => 0,
+			'fournisseur' => 1,
+			'supplier_account' => NULL,
+			'fk_prospectlevel' => '',
+			'fk_incoterms' => 0,
+			'location_incoterms' => NULL,
+			'customer_bad' => 0,
+			'customer_rate' => 0,
+			'supplier_rate' => 0,
+			'remise_client' => 0,
+			'remise_supplier' => 0,
+			'mode_reglement' => NULL,
+			'cond_reglement' => NULL,
+			'mode_reglement_supplier' => NULL,
+			'cond_reglement_supplier' => NULL,
+			'fk_shipping_method' => NULL,
+			'tva_assuj' => 1,
+			'localtax1_assuj' => NULL,
+			'localtax1_value' => 0,
+			'localtax2_assuj' => NULL,
+			'localtax2_value' => 0,
+			'barcode' => NULL,
+			'fk_barcode_type' => 0,
+			'price_level' => NULL,
+			'outstanding_limit' => NULL,
+			'order_min_amount' => NULL,
+			'supplier_order_min_amount' => NULL,
+			'default_lang' => NULL,
+			'logo' => NULL,
+			'logo_squarred' => NULL,
+			'canvas' => NULL,
+			'webservices_url' => NULL,
+			'webservices_key' => NULL,
+			'tms' => $p_data['supplier']['date_upd'],
+			'datec' => $p_data['supplier']['date_add'],
+			'fk_user_creat' => static::D_K_USER,
+			'fk_user_modif' => static::D_K_USER,
+			'fk_multicurrency' => 0,
+			'multicurrency_code' => '',
+			'import_key' => NULL,
+			'transport_mode' => NULL,
+			'transport_mode_supplier' => NULL,
+			'fk_warehouse' => NULL,
+			'accountancy_code_sell' => NULL,
+			'accountancy_code_buy' => NULL,
+		],
+		//'commandedet' => [],
+	];
+
+	return $d_data_map;
+}
+
+// -----------
+
+// Field MAPPING
+
+public static function _p_map_code_client(&$orig_data)
+{
+	return NULL; // @todo récupérer la prochaine valeur !
+}
+
+public static function _pd_map_fk_pays(&$orig_data)
+{
+	static::_country_load();
+
+	if (!empty($orig_data['address']['id_country'])) {
+		$id_country = $orig_data['address']['id_country'];
+		if (isset(static::$_pd_country[$id_country]))
+			return static::$_pd_country[$id_country];
+	}
+}
+
+}
+
+sync_supplier_pd::__init();

+ 46 - 0
src/sync/supplier_price/common.inc.php

@@ -0,0 +1,46 @@
+<?php
+
+// Order
+
+class sync_supplier_price extends sync
+{
+
+const MODEL_NAME = 'supplier_price';
+const MODEL_ID = 22;
+const MAP_ID = 11;
+const D_TABLE_MAIN = 'product_fournisseur_price';
+const P_TABLE_MAIN = 'product_supplier';
+const P_TABLE_MAIN_ID = 'id_product_supplier';
+
+function d_data($otype, $oid)
+{
+	$data = [];
+	$data['product_fournisseur_price'] = DB::d_get_row('product_fournisseur_price', ['rowid'=>$oid]);
+	$data['product_fournisseur_price_extrafields'] = DB::d_get_row('product_fournisseur_price_extrafields', ['fk_object'=>$oid]);
+
+	// product
+	if (!empty($data['product_fournisseur_price']['fk_product']))
+		$data['product'] = DB::d_get_row('product', ['rowid'=>$data['product_fournisseur_price']['fk_product']]);
+
+	// log
+	$data['product_fournisseur_price_log'] = DB::d_get_rows('product_fournisseur_price_log', ['fk_product_fournisseur'=>$oid]);
+	
+	return $data;
+}
+
+function p_data($otype, $oid)
+{
+	$data = [];
+	$data['product_supplier'] = DB::p_get_row('product_supplier', ['id_product_supplier'=>$oid]);
+
+	// product
+	if (!empty($data['product_supplier']['id_product']))
+		$data['product'] = DB::p_get_row('product', ['id_product'=>$data['product_supplier']['id_product']]);
+	// product_attribute
+	if (!empty($data['product_supplier']['id_product_attribute']))
+		$data['product_attribute'] = DB::p_get_row('product_attribute', ['id_product_attribute'=>$data['product_supplier']['id_product_attribute']]);
+	
+	return $data;
+}
+
+}

+ 97 - 0
src/sync/supplier_price/dp.inc.php

@@ -0,0 +1,97 @@
+<?php
+
+class sync_supplier_price_dp extends sync_supplier_price
+{
+
+// -- ACTIONS --
+
+public function execute_osync($otype, $oid)
+{
+	return $this->dp_execute_osync($otype, $oid);
+}
+
+public function execute_create($otype, $oid)
+{
+	return $this->create($otype, $oid);
+}
+
+// -- SUB ACTIONS --
+
+public function create($otype, $oid)
+{
+	return $this->dp_create($otype, $oid);
+}
+
+public function osync($o)
+{
+	return $this->dp_osync($o);
+}
+
+// -- MAPPING --
+
+public function map_update(&$o, &$d_data, &$p_data=[])
+{
+	$p_data_map = [
+		'product_supplier' => [
+			//'id_product_supplier' => NULL,
+			'product_supplier_reference' => $d_data['product_fournisseur_price']['ref_fourn'],
+			'product_supplier_price_te' => $d_data['product_fournisseur_price']['unitprice'],
+		],
+	];
+
+	return $p_data_map;
+}
+
+public function map_create(&$o, &$d_data)
+{
+	$product = static::_dp_map_product($d_data);
+
+	$p_data_map = [
+		'product_supplier' => [
+			//'id_product_supplier' => NULL,
+			'id_product' => $product['id_product'],
+			'id_product_attribute' => $product['id_product_attribute'],
+			'id_supplier' => static::_dp_map_fk_supplier($d_data),
+			'product_supplier_reference' => $d_data['product_fournisseur_price']['ref_fourn'],
+			'product_supplier_price_te' => $d_data['product_fournisseur_price']['unitprice'],
+			'id_currency' => static::P_ID_CURRENCY,
+		],
+	];
+
+	return $p_data_map;
+}
+
+// -----------
+
+// Field MAPPING
+
+public static function _dp_map_product(&$orig_data, &$dest_tree=[])
+{
+	$product = [];
+	// Déjà mappé
+	if ($o = static::d_o_oid('product', 'product', $orig_data['product_fournisseur_price']['fk_product'])) {
+		// Combi
+		if ($o['p_tref']=='product_attribute') {
+			$row = DB::p_get_row('product_attribute', ['id_product_attribute'=>$o['p_oid']]);
+			return ['id_product'=>$row['id_product'], 'id_product_attribute'=>$o['p_oid']];
+		}
+		// Simple
+		else {
+			return ['id_product'=>$o['p_oid'], 'id_product_attribute'=>0];
+		}
+	}
+	// Création simple
+	else {
+		$o = static::_action('product', 'dp', 'create', 'product', $orig_data['product_fournisseur_price']['fk_product']);
+		return ['id_product'=>$o['p_oid'], 'id_product_attribute'=>0];
+	}
+}
+
+public static function _dp_map_fk_supplier(&$orig_data, &$dest_tree=[])
+{
+	return static::_dp_map_fk('supplier', 'societe', $orig_data['product_fournisseur_price']['fk_soc']);
+}
+
+}
+
+sync_supplier_price_dp::__init();

+ 174 - 0
src/sync/supplier_price/pd.inc.php

@@ -0,0 +1,174 @@
+<?php
+
+class sync_supplier_price_pd extends sync_supplier_price
+{
+
+// -- ACTIONS --
+
+public function execute_osync($otype, $oid)
+{
+	return $this->pd_execute_osync($otype, $oid);
+}
+
+public function execute_create($otype, $oid)
+{
+	return $this->create($otype, $oid);
+}
+
+// -- SUB ACTIONS --
+
+public function create($otype, $oid)
+{
+	return $this->pd_create($otype, $oid);
+}
+
+public function osync($o)
+{
+	return $this->pd_osync($o);
+}
+
+public function pd_update_more(&$o, &$d_data_map, &$d_data_map_create, &$d_data, &$p_data)
+{
+	var_dump($d_data_map);
+}
+
+// -- MAPPING --
+
+public function map_update(&$o, &$p_data, &$d_data=[])
+{
+	if (!empty($d_data['product_fournisseur_price']) && $d_data['product_fournisseur_price']['quantity'] !=1 ) {
+		return;
+		//@todo calcul unit_price
+		$d_data_map = [
+			'product_fournisseur_price' => [
+				'ref_fourn' => static::_pd_ref_fourn($p_data),
+				'price' => $p_data['product_supplier']['product_supplier_price_te'],
+				'multicurrency_price' => $p_data['product_supplier']['product_supplier_price_te'],
+				//'quantity' => 1,
+				'unitprice' => $p_data['product_supplier']['product_supplier_price_te'],
+				//'packaging' => 1,
+				'multicurrency_unitprice' => $p_data['product_supplier']['product_supplier_price_te'],
+			],
+		];
+	}
+	else {
+		$d_data_map = [
+			'product_fournisseur_price' => [
+				'ref_fourn' => static::_pd_ref_fourn($p_data),
+				'price' => $p_data['product_supplier']['product_supplier_price_te'],
+				'multicurrency_price' => $p_data['product_supplier']['product_supplier_price_te'],
+				'quantity' => 1,
+				'unitprice' => $p_data['product_supplier']['product_supplier_price_te'],
+				'packaging' => 1,
+				'multicurrency_unitprice' => $p_data['product_supplier']['product_supplier_price_te'],
+			],
+		];
+	}
+
+	return $d_data_map;
+}
+
+public function map_create(&$o, &$p_data)
+{
+	$d_data_map = [
+		'product_fournisseur_price' => [
+			//'rowid' => NULL,
+			'entity' => 1,
+			'datec' => static::$datetime,
+			'tms' => static::$datetime,
+			'fk_product' => static::_pd_map_fk_product($p_data),
+			'fk_soc' => static::_pd_map_fk_supplier($p_data),
+			'ref_fourn' => static::_pd_ref_fourn($p_data),
+			'desc_fourn' => '',
+			'fk_availability' => 0, //@todo
+			'price' => $p_data['product_supplier']['product_supplier_price_te'],
+			'quantity' => 1,
+			'remise_percent' => 0,
+			'remise' => 0,
+			'unitprice' => $p_data['product_supplier']['product_supplier_price_te'],
+			'charges' => 0,
+			'default_vat_code' => NULL,
+			'barcode' => NULL, // @todo barcode
+			'fk_barcode_type' => NULL, // @todo barcode
+			'tva_tx' => static::_dp_map_tva_taux($p_data),
+			'localtax1_tx' => 0,
+			'localtax1_type' => 0,
+			'localtax2_tx' => 0,
+			'localtax2_type' => 0,
+			'info_bits' => 0,
+			'fk_user' => static::D_K_USER,
+			'fk_supplier_price_expression' => NULL,
+			'import_key' => NULL,
+			'delivery_time_days' => NULL,
+			'supplier_reputation' => NULL,
+			'fk_multicurrency' => NULL,
+			'multicurrency_code' => '',
+			'multicurrency_tx' => 1,
+			'multicurrency_unitprice' => $p_data['product_supplier']['product_supplier_price_te'],
+			'multicurrency_price' => $p_data['product_supplier']['product_supplier_price_te'],
+			'packaging' => 1,
+		],
+	];
+
+	return $d_data_map;
+}
+
+// -----------
+
+// Field MAPPING
+
+public static function _pd_map_fk_product(&$orig_data, &$dest_tree=[])
+{
+	// Product type
+	if ($orig_data['product_supplier']['id_product_attribute'])
+		return static::_pd_map_fk('product', 'product_attribute', $orig_data['product_supplier']['id_product_attribute']);
+	else
+		return static::_pd_map_fk('product', 'product', $orig_data['product_supplier']['id_product']);
+}
+
+public static function _pd_map_fk_supplier(&$orig_data, &$dest_tree=[])
+{
+	return static::_pd_map_fk('supplier', 'supplier', $orig_data['product_supplier']['id_supplier']);
+}
+
+public static function _dp_map_tva_taux(&$orig_data, &$dest_tree=[])
+{
+	// @todo mapper les taxes proprement !!
+	switch($orig_data['product']['id_tax_rules_group']) {
+		case 1:
+			return 20;
+		case 2:
+			return 10;
+		case 3:
+			return 5.5;
+		case 4:
+			return 2.1;
+		case 5:
+			return 0;
+	}
+}
+
+public static function _pd_ref_fourn(&$orig_data, &$dest_tree=[])
+{
+	return (
+		!empty($orig_data['product_supplier']['product_supplier_reference'])
+		?$orig_data['product_supplier']['product_supplier_reference']
+		:(
+			!empty($orig_data['product_attribute'])
+			?(
+				!empty($orig_data['product_attribute']['ean13'])
+				?$orig_data['product_attribute']['ean13']
+				:$orig_data['product_attribute']['reference']
+			)
+			:(
+				!empty($orig_data['product']['ean13'])
+				?$orig_data['product']['ean13']
+				:$orig_data['product']['reference']
+			)
+		)
+	);
+}
+
+}
+
+sync_supplier_price_pd::__init();

+ 687 - 0
src/sync/sync.inc.php

@@ -0,0 +1,687 @@
+<?php
+
+// -- SYNC CLASS --
+
+class sync
+{
+
+protected static $_instance = [];
+
+// Prestashop
+
+// Tables
+const P_TABLE_ID = [
+	'address' => 'id_address',
+	'customer' => 'id_customer',
+	'manufacturer' => 'id_manufacturer',
+	'orders' => 'id_order',
+	'order_cart_rule' => 'id_order_cart_rule',
+	'order_detail' => 'id_order_detail',
+	'order_invoice' => 'id_order_invoice',
+	'order_payment' => 'id_order_payment',
+	'order_slip' => 'id_order_slip',
+	'order_slip_detail' => 'id_order_slip_detail',
+	'product' => 'id_product',
+	'product_attribute' => 'id_product_attribute',
+	'product_supplier' => 'id_product_supplier',
+	'products_dlc_dluo' => 'id',
+	'stock_available' => 'id_stock_available',
+	'supplier' => 'id_supplier',
+];
+
+// Général
+const P_FOLDER_ROOT = '/home/siteadm/calicote/public/calicote';
+const P_ID_LANG = 1;
+const P_ID_SHOP = 1;
+const P_ID_SHOP_GROUP = 1;
+const P_ID_TAX_RULES_GROUP = 3;
+// Product
+const P_ID_CATEGORY_DEFAULT = 2;
+const P_MINIMAL_QUANTITY = 1;
+const P_LOW_STOCK_THRESHOLD = 5;
+const P_LOW_STOCK_ALERT = 1;
+const P_UNITY = 'Le KG';
+const P_PACK_STOCK_TYPE = 3;
+// Product Lot
+const P_DLC_ALERT1 = 30;
+const P_DLC_ALERT2 = 60;
+// Stock
+const P_ID_WAREHOUSE = 0;
+const P_OUT_OF_STOCK_DEFAULT = 2;
+// Customer
+const P_ID_GROUP_DEFAULT = 3;
+// Order
+const P_ID_CURRENCY = 1;
+// Cart rule
+const P_K_FREESHIPPING_CART_RULE = 18;
+const P_K_DISCOUNT_CART_RULE = 17;
+
+// Dolibarr
+
+// Général
+const D_FOLDER_ROOT = '/home/siteadm/calicote/public/calicote-erp';
+const D_K_USER = 2;
+// Stock
+const D_K_WAREHOUSE = 1;
+// Product Shipping Price (PrestaShipping)
+const D_K_SHIPPING_PRODUCT = 1;
+const D_SHIPPING_PRODUCT_NAME = 'Frais de Transport';
+const D_SHIPPING_PRODUCT_TVA = 20; // Pas certain... mais assez sûr !
+// Product Discount Price
+const D_K_DISCOUNT_PRODUCT = 2;
+const D_DISCOUNT_PRODUCT_NAME = 'Remise exceptionnelle';
+const D_DISCOUNT_PRODUCT_TVA = 20; // Pas certain...
+// Banque par défaut etransactions
+const D_K_BANK_ETRANSACTION = 1;
+
+protected static $timestamp;
+protected static $datetime;
+protected static $date;
+
+public static function __init()
+{
+	$classname = get_called_class();
+	static::$_instance[$classname] = new static();
+	if (empty(static::$timestamp)) {
+		static::$timestamp = time();
+		static::$datetime = date('Y-m-d H:i:s', static::$timestamp);
+		static::$date = substr(static::$datetime, 0, 10);
+	}
+}
+
+protected static $_p_country = [];
+protected static $_d_country = [];
+protected static $_pd_country = [];
+protected static $_dp_country = [];
+
+protected static function _country_load()
+{
+	if (!empty(static::$_p_country))
+		return;
+
+	// Presta
+	$sql = 'SELECT * FROM ps_country';
+	$q = DB::p_select($sql);
+	while($row=$q->fetch_assoc())
+		static::$_p_country[$row['iso_code']] = $row['id_country'];
+	//var_dump(static::$_p_country);
+
+	// Doli
+	$sql = 'SELECT * FROM llx_c_country';
+	$q = DB::d_select($sql);
+	while($row=$q->fetch_assoc()) 
+		static::$_d_country[$row['code']] = $row['rowid'];
+	//var_dump(static::$_d_country);
+
+	// Presta => Doli
+	foreach(static::$_p_country as $iso=>$id)
+		if (isset(static::$_d_country[$iso]))
+			static::$_pd_country[$id] = static::$_d_country[$iso];
+	//var_dump(static::$_pd_country);
+
+	// Doli => Presta
+	foreach(static::$_d_country as $iso=>$id)
+		if (isset(static::$_p_country[$iso]))
+			static::$_dp_country[$id] = static::$_p_country[$iso];
+	//var_dump(static::$_dp_country);
+
+}
+
+public static function _class($type, $sens)
+{
+	// Load common model class
+	try {
+		require_once(SYNC_PATH.'/'.$type.'/common.inc.php');
+	} catch(Throwable $e) {
+		var_dump($e);
+		sync_error("Could not load common sync class for type ".$type);
+	} catch(Exception $e) {
+		var_dump($e);
+		sync_error("Could not load common sync class for type ".$type);
+	}
+	
+	// Load specific direction model class
+	try {
+		require_once(SYNC_PATH.'/'.$type.'/'.$sens.'.inc.php');
+	} catch(Throwable $e) {
+		var_dump($e);
+		sync_error("Could not load controller sync class for type ".$type.', sens '.$sens);
+	} catch(Exception $e) {
+		var_dump($e);
+		sync_error("Could not load controller sync class for type ".$type.', sens '.$sens);
+	}
+	
+	return $classname = 'sync_'.$type.'_'.$sens;
+}
+
+public static function _action($type, $sens, $action, $otype, $oid)
+{
+	$classname = static::_class($type, $sens);
+	
+	// Initialise controller and execute action
+	try {
+		$controller = $classname::_instance();
+		$method = 'execute_'.$action;
+		$r = $controller->$method($otype, $oid);
+	} catch(Throwable $e) {
+		var_dump($e);
+		sync_error('Error executing controller '.$classname.'::'.$method.'('.$otype.','.$oid.')');
+	} catch(Exception $e) {
+		var_dump($e);
+		sync_error('Error executing controller '.$classname.'::'.$method.'('.$otype.','.$oid.')');
+	}
+	
+	if (!empty($r))
+		return $r;
+}
+
+public static function _instance()
+{
+	$classname = get_called_class();
+	return static::$_instance[$classname];
+}
+public static function _getInstance()
+{
+	return static::_instance();
+}
+protected function __construct()
+{
+
+}
+
+
+// -- OBJECT MAPPING RELATION MANAGEMENT --
+
+public function o_create($o)
+{
+	//var_dump($o);
+	if (empty($o) || empty($o['d_tref']) || empty($o['d_oid']) || empty($o['p_tref']) || empty($o['p_oid']))
+		return;
+
+	// Insert main object
+	$sql = 'INSERT INTO `_objects` (`mid`, `map_id`) VALUES ('.$o['model_id'].', '.$o['map_id'].')';
+	if (!($id = DB::o_insert($sql)))
+		return;
+	// Insert relations
+	$sql = 'INSERT INTO `_objects_d` (`id`, `tid`, `oid`) SELECT '.$id.', `id`, '.$o['d_oid'].' FROM `_d_tables` WHERE `ref`="'.$o['d_tref'].'"';
+	DB::o_insert($sql);
+	$sql = 'INSERT INTO `_objects_p` (`id`, `tid`, `oid`) SELECT '.$id.', `id`, '.$o['p_oid'].' FROM `_p_tables` WHERE `ref`="'.$o['p_tref'].'"';
+	DB::o_insert($sql);
+	return $id;
+}
+
+public static function o_oid($sens, $type, $otype, $oid)
+{
+	$sql = 'SELECT o.id,
+			od.tid d_tid, td.ref d_tref, od.oid d_oid,
+			op.tid p_tid, tp.ref p_tref, op.oid p_oid
+		FROM _objects o
+		INNER JOIN _map ma ON ma.id=o.map_id
+		INNER JOIN _models mo ON mo.id=ma.model_id
+		INNER JOIN _objects_d od ON od.id=o.id
+		INNER JOIN _d_tables td ON td.id=od.tid
+		INNER JOIN _objects_p op ON op.id=o.id
+		INNER JOIN _p_tables tp ON tp.id=op.tid
+		WHERE mo.ref="'.$type.'"
+		  AND '.($sens=='dp' ?'td' :'tp').'.ref="'.$otype.'"
+		  AND '.($sens=='dp' ?'od' :'op').'.oid="'.$oid.'"';
+
+	$q = DB::o_query($sql);
+	return $q->fetch_assoc();
+}
+public static function d_o_oid($type, $otype, $oid)
+{
+	return static::o_oid('dp', $type, $otype, $oid);
+}
+public static function p_o_oid($type, $otype, $oid)
+{
+	return static::o_oid('pd', $type, $otype, $oid);
+}
+
+
+// -- HELPERS --
+
+// @todo mettre ailleurs, c'est un helper bien plus général
+public static function _array_merge_recursive(&$map, $tmap)
+{
+	if (is_array($tmap)) foreach($tmap as $i=>$j) {
+		if (!isset($map[$i]))
+			$map[$i] = $j;
+		elseif (!is_array($map[$i]) || !is_array($j))
+			$map[$i] = $j;
+		else
+			static::_array_merge_recursive($map[$i], $j);
+	}
+}
+
+/**
+ * Text to slug
+ * @return string
+ */
+public static function _slugify($text, string $divider='-')
+{
+	// Remove HTML entities
+	$text = html_entity_decode($text);
+
+	// replace non letter or digits by divider
+	$text = preg_replace('~[^\pL\d]+~u', $divider, $text);
+
+	// transliterate
+	$text = iconv('utf-8', 'us-ascii//TRANSLIT', $text);
+
+	// remove unwanted characters
+	$text = preg_replace('~[^-\w]+~', '', $text);
+
+	// trim
+	$text = trim($text, $divider);
+
+	// remove duplicate divider
+	$text = preg_replace('~-+~', $divider, $text);
+
+	// lowercase
+	$text = strtolower($text);
+
+	if (empty($text)) {
+		return 'n-a-'.rand(1, 1000000000);
+	}
+
+	return $text;
+}
+
+// TABLE REF MAP
+
+//protected static $nb;
+
+public static function _d_ref_nb($tablename, $fieldname, $str)
+{
+	$sql = 'SELECT rowid, MAX('.$fieldname.')
+		FROM llx_'.$tablename.'
+		WHERE '.$fieldname.' REGEXP "'.$str.'[0-9]{4}-[0-9]{6}"';
+	$q = DB::d_select($sql);
+	if ($q->num_rows) {
+		list($id, $ref) = $q->fetch_row();
+		$nb = (int)substr($ref, 7);
+		//var_dump($id); var_dump($ref); var_dump($date);
+	}
+	else {
+		$nb = 0;
+	}
+
+	var_dump($nb);
+	return $nb;
+}
+public static function d_ref_nb()
+{
+	static::$nb = static::_d_ref_nb(static::D_TABLE_MAIN, static::D_TABLE_MAIN_REF, static::D_CODE);
+	return static::$nb;
+}
+
+public static function _d_ref($date, $nb, $str)
+{
+	$nb++;
+	for ($i=strlen($nb); $i<6; $i++)
+		$nb = '0'.$nb;
+	$ref = $str.substr($date, 2, 2).substr($date, 5, 2).'-'.$nb;
+	
+	return $ref;
+}
+public static function d_ref($date)
+{
+	$nb = static::d_ref_nb();
+	return static::_d_ref($date, $nb, static::D_CODE);
+}
+
+
+// -- MAP FK LINKS --
+
+public static function _dp_map_fk($modelname, $tablename, $id)
+{
+	if (!($o = static::d_o_oid($modelname, $tablename, $id))) {
+		$o = static::_action($modelname, 'dp', 'create', $tablename, $id);
+	}
+	var_dump($o);
+	if (empty($o))
+		return;
+	return $o['p_oid'];
+}
+public static function _pd_map_fk($modelname, $tablename, $id)
+{
+	if (!($o = static::p_o_oid($modelname, $tablename, $id))) {
+		$o = static::_action($modelname, 'pd', 'create', $tablename, $id);
+	}
+	var_dump($o);
+	if (empty($o))
+		return;
+	return $o['d_oid'];
+}
+
+
+// -- PD COMMON METHODS --
+
+
+// EXECUTE_OSYNC
+public function pd_execute_osync($otype, $oid)
+{
+	// Vérif lien existant
+	// Récupère lien
+	$o = $this->p_o_oid(static::MODEL_NAME, $otype, $oid);
+	var_dump($o);
+
+	//die();
+
+	// Création
+	if (empty($o)) {
+		return $this->execute_create($otype, $oid);
+	}
+
+	return $this->osync($o);
+}
+public function dp_execute_osync($otype, $oid)
+{
+	// Vérif lien existant
+	// Récupère lien
+	$o = $this->d_o_oid(static::MODEL_NAME, $otype, $oid);
+	var_dump($o);
+
+	//die();
+
+	// Création
+	if (empty($o)) {
+		return $this->execute_create($otype, $oid);
+	}
+	
+	return $this->osync($o);
+}
+
+// CREATE
+public function pd_create($otype, $oid)
+{
+	$o = [
+		'model_id'=>static::MODEL_ID,
+		'map_id'=>static::MAP_ID,
+		'p_tref'=>$otype,
+		'p_oid'=>$oid,
+	];
+
+	// Récupère données source
+	$p_data = $this->p_data($otype, $oid);
+	var_dump($p_data);
+	if (empty($p_data[$otype])) {
+		sync_error("ERROR ".__METHOD__."($otype, $oid) : Source data does not exists");
+		return;
+	}
+	
+	// Construit nouvel objet
+	if (!($d_data_map = $d_data_map_create = $this->pd_map_create($o, $p_data))) {
+		return;
+		sync_error("ERROR ".__METHOD__."($otype, $oid) : map_create()");
+	}
+	$d_data_map_update = $this->pd_map_update($o, $p_data);
+	static::_array_merge_recursive($d_data_map, $d_data_map_update);
+	var_dump($d_data_map);
+	
+	echo '<p>CREATION OBJETS</p>';
+	
+	// insert main row
+	if (!($id = DB::d_insert_row($o['d_tref'], $d_data_map[$o['d_tref']]))) {
+		sync_error("ERROR ".__METHOD__."($otype, $oid) : DB::d_insert_row");
+		return;
+	}
+
+	// Create Mapping Object
+	$o['d_oid'] = $id;
+	if (!($this->o_create($o))) {
+		// @todo : delete useless inserted data
+		sync_error("ERROR ".__METHOD__."($otype, $oid) : o_create()");
+		return;
+	}
+	
+	$this->pd_insert_more($o, $d_data_map, $p_data);
+	$d_data = [];
+	$this->pd_update_more($o, $d_data_map_update, $d_data_map_create, $d_data, $p_data);
+
+	// Sync notif ts
+	$date = date('Y-m-d H:i:s');
+	$sql = 'UPDATE `_sync`
+		SET `sync_ts`=NOW()
+		WHERE `type`="'.static::MODEL_NAME.'" AND `t_name`="'.$otype.'" AND `t_oid`="'.$oid.'" AND `action` IN ("create", "osync")
+			AND `sync_ts` IS NULL AND `ts` <= "'.$date.'"';
+	DB::p_update($sql);
+	
+	return $o;
+}
+public function pd_insert_more(&$o, &$d_data_map, &$p_data)
+{
+}
+public function pd_map_create(&$o, &$p_data)
+{
+	$o['d_tref'] = static::D_TABLE_MAIN;
+	
+	return $this->map_create($o, $p_data);
+}
+public function dp_create($otype, $oid)
+{
+	$o = [
+		'model_id'=>static::MODEL_ID,
+		'map_id'=>static::MAP_ID,
+		'd_tref'=>$otype,
+		'd_oid'=>$oid,
+	];
+	var_dump($o);
+
+	// Récupère données source
+	$d_data = $this->d_data($otype, $oid);
+	var_dump($d_data);
+	if (empty($d_data[$otype])) {
+		sync_error("ERROR ".__METHOD__."($otype, $oid) : Source data does not exists");
+		return;
+	}
+	
+	// Construit nouvel objet
+	if (!($p_data_map_create = $p_data_map = $this->dp_map_create($o, $d_data)))
+		sync_error("ERROR ".__METHOD__."($otype, $oid) : map_create()");
+	$p_data_map_update = $this->dp_map_update($o, $d_data);
+	static::_array_merge_recursive($p_data_map, $p_data_map_update);
+	var_dump($p_data_map);
+
+	//die();
+
+	echo '<p>CREATION OBJETS</p>';
+
+	// insert main row
+	if (!($id = DB::p_insert_row($o['p_tref'], $p_data_map[$o['p_tref']]))) {
+		sync_error("ERROR ".__METHOD__."($otype, $oid) : DB::d_insert_row()");
+		return;
+	}
+	
+	// Create Mapping Object
+	$o['p_oid'] = $id;
+	if (!($this->o_create($o))) {
+		// @todo : delete useless inserted data
+		sync_error("ERROR ".__METHOD__."($otype, $oid) : o_create()");
+		return;
+	}
+	
+	$this->dp_insert_more($o, $p_data_map, $d_data);
+	$p_data = [];
+	$this->dp_update_more($o, $p_data_map_update, $p_data_map_create, $p_data, $d_data);
+	
+	// Sync notif ts
+	$date = date('Y-m-d H:i:s');
+	$sql = 'UPDATE `_sync`
+		SET `sync_ts`=NOW()
+		WHERE `type`="'.static::MODEL_NAME.'" AND `t_name`="'.$o['d_tref'].'" AND `t_oid`="'.$o['d_oid'].'" AND `action` IN ("create", "osync")
+			AND `sync_ts` IS NULL AND `ts` <= "'.$date.'"';
+	DB::d_update($sql);
+
+	return $o;
+}
+public function dp_insert_more(&$o, &$p_data_map, &$d_data)
+{
+}
+public function dp_map_create(&$o, &$d_data)
+{
+	$o['p_tref'] = static::P_TABLE_MAIN;
+	
+	return $this->map_create($o, $d_data);
+}
+
+// OSYNC
+public function pd_osync($o)
+{
+	var_dump($o);
+	// Récupère données source
+	$p_data = $this->p_data($o['p_tref'], $o['p_oid']);
+	var_dump($p_data);
+	if (empty($p_data[$o['p_tref']])) {
+		sync_error("ERROR ".__METHOD__."($o[p_tref], $o[p_oid]) : Source data does not exists");
+		return;
+	}
+	
+	// Récupère données actuelles en comparaison
+	$d_data = $this->d_data($o['d_tref'], $o['d_oid']);
+	var_dump($d_data);
+	
+	//die();
+
+	// Construit update
+	$d_data_map = $this->pd_map_update($o, $p_data, $d_data);
+	var_dump($d_data_map);
+	
+	// Construit create
+	$d_data_map_create = $this->pd_map_create($o, $p_data);
+	
+	// Update base
+	static::d_update_row($o['d_tref'], ['rowid'=>$o['d_oid']], $d_data_map, $d_data_map_create, $d_data);
+	
+	// Update more
+	$this->pd_update_more($o, $d_data_map, $d_data_map_create, $d_data, $p_data);
+
+	// Sync notif ts
+	$date = date('Y-m-d H:i:s');
+	$sql = 'UPDATE `_sync`
+		SET `sync_ts`=NOW()
+		WHERE `type`="'.static::MODEL_NAME.'" AND `t_name`="'.$o['p_tref'].'" AND `t_oid`="'.$o['p_oid'].'" AND `action` IN ("create", "osync")
+			AND `sync_ts` IS NULL AND `ts` <= "'.$date.'"';
+	//trigger_error($sql);
+	DB::p_update($sql);
+	
+	return $o;
+}
+public function pd_update_more(&$o, &$d_data_map, &$d_data_map_create, &$d_data, &$p_data)
+{
+}
+public function pd_map_update(&$o, &$p_data, &$d_data=[])
+{
+	return $this->map_update($o, $p_data, $d_data);
+}
+
+public function dp_osync($o)
+{
+
+	// Récupère données source
+	$d_data = $this->d_data($o['d_tref'], $o['d_oid']);
+	var_dump($d_data);
+	if (empty($d_data[static::D_TABLE_MAIN])) {
+		sync_error("ERROR ".__METHOD__."($o[d_tref], $o[d_oid]) : Source data does not exists");
+		return;
+	}
+
+	// Récupère données actuelles en comparaison
+	$p_data = $this->p_data($o['p_tref'], $o['p_oid']);
+	var_dump($p_data);
+	
+	// Construit update
+	$p_data_map = $this->dp_map_update($o, $d_data, $p_data);
+	var_dump($p_data_map);
+	
+	// Construit create
+	$p_data_map_create = $this->dp_map_create($o, $d_data);
+
+	// Update base
+	static::p_update_row($o['p_tref'], [static::P_TABLE_ID[$o['p_tref']]=>$o['p_oid']], $p_data_map, $p_data_map_create, $p_data);
+
+	// Update more
+	$this->dp_update_more($o, $p_data_map, $p_data_map_create, $p_data, $d_data);
+	
+	// Sync notif ts
+	$date = date('Y-m-d H:i:s');
+	$sql = 'UPDATE `_sync`
+		SET `sync_ts`=NOW()
+		WHERE `type`="'.static::MODEL_NAME.'" AND `t_name`="'.$o['d_tref'].'" AND `t_oid`="'.$o['d_oid'].'" AND `action` IN ("create", "osync")
+			AND `sync_ts` IS NULL AND `ts` <= "'.$date.'"';
+	//var_dump(get_called_class()); var_dump($sql);
+	DB::d_update($sql);
+	
+	return $o;
+}
+public function dp_update_more(&$o, &$p_data_map, &$p_data_map_create, &$p_data, &$d_data)
+{
+	//
+}
+public function dp_map_update(&$o, &$d_data, &$p_data=[])
+{
+	return $this->map_update($o, $d_data, $p_data);
+}
+
+// DELETE
+
+
+public function pd_delete($otype, $oid)
+{
+	$o = static::p_o_oid(static::MODEL_NAME, $otype, $oid);
+	if (!empty($o)) {
+		DB::d_delete_row($o['d_tref'], ['rowid'=>$o['d_oid']]);
+		//DB::p_delete_row($o['p_tref']], [static::P_TABLE_ID[$o['p_tref']]]=>$o['p_oid']]);
+		$sql = 'DELETE FROM _objects WHERE id='.$o['id'];
+		DB::o_delete($sql);
+		return true;
+	}
+}
+
+public function dp_delete($otype, $oid)
+{
+	$o = static::d_o_oid(static::MODEL_NAME, $otype, $oid);
+	if (!empty($o)) {
+		//DB::d_delete_row($o['d_tref']], ['rowid'=>$o['d_oid']]);
+		DB::p_delete_row($o['p_tref'], [static::P_TABLE_ID[$o['p_tref']]=>$o['p_oid']]);
+		$sql = 'DELETE FROM _objects WHERE id='.$o['id'];
+		DB::o_delete($sql);
+		return true;
+	}
+}
+
+// DB UPDATE ROW
+
+public static function p_update_row($tablename, $params, $data_map, $data_map_create=[], $data=[])
+{
+	// Déjà => Update
+	if (isset($data[$tablename]))
+		return DB::p_update_row($tablename, (isset($data_map[$tablename]) ?$data_map[$tablename] :[]), $params, $data[$tablename]);
+	// Pas => Insert
+	else
+		return DB::p_insert_row($tablename, array_merge((isset($data_map_create[$tablename]) ?$data_map_create[$tablename] :[]), (isset($data_map[$tablename]) ?$data_map[$tablename] :[]), $params));
+}
+public static function p_insert_row($tablename, $params, $data_map, $data=[])
+{
+	// Pas déjà => Insert
+	if (!isset($data[$tablename]))
+		return DB::p_insert_row($tablename, array_merge((isset($data_map[$tablename]) ?$data_map[$tablename] :[]), $params));
+}
+public static function d_update_row($tablename, $params, $data_map, $data_map_create=[], $data=[])
+{
+	// Déjà => Update
+	if (isset($data[$tablename]))
+		return DB::d_update_row($tablename, (isset($data_map[$tablename]) ?$data_map[$tablename] :[]), $params, $data[$tablename]);
+	// Pas => Insert
+	else
+		return DB::d_insert_row($tablename, array_merge((isset($data_map_create[$tablename]) ?$data_map_create[$tablename] :[]), (isset($data_map[$tablename]) ?$data_map[$tablename] :[]), $params));
+}
+public static function d_insert_row($tablename, $params, $data_map, $data=[])
+{
+	// Pas déjà => Insert
+	if (!isset($data[$tablename]))
+		return DB::d_insert_row($tablename, array_merge((isset($data_map[$tablename]) ?$data_map[$tablename] :[]), $params));
+}
+
+}

+ 0 - 0
app/config/.gitkeep → src/sync/user/common.inc.php


+ 0 - 0
src/sync/user/dp.inc.php


+ 0 - 0
src/sync/user/pd.inc.php


+ 48 - 0
src/update/product.inc.php

@@ -0,0 +1,48 @@
+<?php
+
+die();
+
+$file = file('../tmp/product.csv');
+$h = $row = explode(',', array_shift($file));
+var_dump($h);
+foreach($file as $l) {
+	$row = explode(',', $l);
+	var_dump($row);
+	
+	// product_attribute
+	if ($row[2]) {
+		$sql = 'UPDATE ps_product_attribute
+			SET wholesale_price='.$row[7].'
+			WHERE id_product_attribute='.$row[2];
+		if (isset($_GET['go']))
+			DB::p_update($sql);
+		else
+			echo '<p>'.$sql.'</p>';
+	}
+	// product
+	else {
+		$sql = 'UPDATE ps_product
+			SET wholesale_price='.$row[7].'
+			WHERE id_product='.$row[1];
+		if (isset($_GET['go']))
+			DB::p_update($sql);
+		else
+			echo '<p>'.$sql.'</p>';
+		
+		$sql = 'UPDATE ps_product_shop
+			SET wholesale_price='.$row[7].'
+			WHERE id_product='.$row[1];
+		if (isset($_GET['go']))
+			DB::p_update($sql);
+		else
+			echo '<p>'.$sql.'</p>';
+	}
+	
+	$sql = 'UPDATE ps_product_supplier
+		SET product_supplier_price_te='.$row[5].'
+		WHERE id_product_supplier='.$row[0];
+	if (isset($_GET['go']))
+		DB::p_update($sql);
+	else
+		echo '<p>'.$sql.'</p>';
+}

+ 116 - 0
tmp/product.csv

@@ -0,0 +1,116 @@
+id_product_supplier,id_product,id_product_attribute,id_supplier,product_supplier_reference,product_supplier_price_te,id_currency,wholesale_price,name,active,id_category_default
+1205,318,0,1,ECR01,112.000000,1,112.000000,"Enrouleur de bâche à bulles piscine ECO largeur maxi 4,50m",1,78
+1982,452,0,8,PMFCP-750S,301.500000,1,301.500000,Pompe filtration piscine DERCYA vitesse variable,1,5
+2029,453,0,14,127108K,1180.000000,1,1180.000000,Kit de chauffage solaire Heliocol pour piscine,1,53
+2059,458,0,14,MAGEN-HEAT-KIT,270.000000,1,270.000000,Chauffage solaire pour piscine hors-sol Heat Kit,1,53
+2111,481,0,1,WR000239,603.170000,1,603.170000,Robot piscine électrique Zodiac RC 4402,0,83
+2234,500,0,1,WR000327,624.180000,1,654.180000,Robot piscine électrique Zodiac RE 440 IQ Voyager,0,83
+1977,67,940,3,PSPVV,558.690000,1,558.690000,Pompe filtration piscine HAYWARD SUPER PUMP VSTD vitesse variable,1,5
+56,68,108,3,51316622,226.490000,1,226.490000,Pompe filtration piscine HAYWARD MAX FLO,1,4
+57,68,109,3,51316789,228.120000,1,228.120000,Pompe filtration piscine HAYWARD MAX FLO,1,4
+58,68,111,3,51316797,318.100000,1,318.100000,Pompe filtration piscine HAYWARD MAX FLO,1,4
+59,68,112,3,51316800,233.850000,1,233.850000,Pompe filtration piscine HAYWARD MAX FLO,1,4
+60,68,113,3,51316819,322.810000,1,322.810000,Pompe filtration piscine HAYWARD MAX FLO,1,4
+61,68,114,3,51316827,242.900000,1,242.900000,Pompe filtration piscine HAYWARD MAX FLO,1,4
+62,68,115,3,51316835,351.310000,1,351.310000,Pompe filtration piscine HAYWARD MAX FLO,1,4
+2102,69,855,7,FM15,550.000000,1,550.000000,Filtre à cartouches piscine Hayward Swimclear,1,9
+2003,365,730,5,30815,149.000000,1,149.000000,Traitement UV pour piscine PURIQ,1,38
+2004,365,731,5,30816,180.000000,1,180.000000,Traitement UV pour piscine PURIQ,1,38
+2124,426,991,11,BUDUO4C/SO,5.008954,1,5.008954,Bâche à bulles piscine sur mesure 500 microns EnergyGuard Geobubble,1,56
+2181,496,1046,4,B04C0077,162.940000,1,162.940000,Coffret électrique pour piscine multifonctions filtration+projecteur,0,83
+2178,496,1048,4,B04C0073,125.280000,1,125.280000,Coffret électrique pour piscine multifonctions filtration+projecteur,0,83
+2189,497,1050,11,LAM350BL,96.980000,1,96.980000,Lames de remplacement en PVC pour volet de piscine,1,61
+2190,497,1051,11,LAM4BL,108.340000,1,108.340000,Lames de remplacement en PVC pour volet de piscine,1,61
+2191,497,1052,11,LAM450BL,119.700000,1,119.700000,Lames de remplacement en PVC pour volet de piscine,1,61
+2192,497,1053,11,LAM5BL,131.060000,1,131.060000,Lames de remplacement en PVC pour volet de piscine,1,61
+2193,497,1054,11,LAM550BL,142.420000,1,142.420000,Lames de remplacement en PVC pour volet de piscine,1,61
+2194,497,1055,11,LAM6BL,153.780000,1,153.780000,Lames de remplacement en PVC pour volet de piscine,1,61
+2199,497,1060,11,LAM3SA,85.620000,1,85.620000,Lames de remplacement en PVC pour volet de piscine,1,61
+2200,497,1061,11,LAM350SA,96.980000,1,96.980000,Lames de remplacement en PVC pour volet de piscine,1,61
+2201,497,1062,11,LAM4SA,108.340000,1,108.340000,Lames de remplacement en PVC pour volet de piscine,1,61
+2202,497,1063,11,LAM450SA,119.700000,1,119.700000,Lames de remplacement en PVC pour volet de piscine,1,61
+2203,497,1064,11,LAM5SA,131.060000,1,131.060000,Lames de remplacement en PVC pour volet de piscine,1,61
+2204,497,1065,11,LAM550SA,142.420000,1,142.420000,Lames de remplacement en PVC pour volet de piscine,1,61
+2205,497,1066,11,LAM6SA,153.780000,1,153.780000,Lames de remplacement en PVC pour volet de piscine,1,61
+2206,497,1067,11,LAM650SA,157.160000,1,157.160000,Lames de remplacement en PVC pour volet de piscine,1,61
+2207,497,1068,11,LAM7SA,168.180000,1,168.180000,Lames de remplacement en PVC pour volet de piscine,1,61
+2208,497,1069,11,LAM750SA,174.890000,1,174.890000,Lames de remplacement en PVC pour volet de piscine,1,61
+2209,497,1070,11,LAM8SA,186.870000,1,186.870000,Lames de remplacement en PVC pour volet de piscine,1,61
+2210,497,1071,11,LAM3GR,85.620000,1,85.620000,Lames de remplacement en PVC pour volet de piscine,1,61
+2211,497,1082,11,LAM350GR,96.980000,1,96.980000,Lames de remplacement en PVC pour volet de piscine,1,61
+2212,497,1083,11,LAM4GR,108.340000,1,108.340000,Lames de remplacement en PVC pour volet de piscine,1,61
+2213,497,1084,11,LAM450GR,119.700000,1,119.700000,Lames de remplacement en PVC pour volet de piscine,1,61
+2214,497,1085,11,LAM5GR,131.060000,1,131.060000,Lames de remplacement en PVC pour volet de piscine,1,61
+2215,497,1086,11,LAM550GR,142.420000,1,142.420000,Lames de remplacement en PVC pour volet de piscine,1,61
+2216,497,1087,11,LAM6GR,153.780000,1,153.780000,Lames de remplacement en PVC pour volet de piscine,1,61
+2217,497,1088,11,LAM650GR,157.160000,1,157.160000,Lames de remplacement en PVC pour volet de piscine,1,61
+2218,497,1089,11,LAM7GR,168.180000,1,168.180000,Lames de remplacement en PVC pour volet de piscine,1,61
+2219,497,1090,11,LAM750GR,174.890000,1,174.890000,Lames de remplacement en PVC pour volet de piscine,1,61
+2220,497,1091,11,LAM8GR,186.870000,1,186.870000,Lames de remplacement en PVC pour volet de piscine,1,61
+2232,498,1099,1,VSP11FL,304.850000,1,304.850000,Pompe filtration piscine à vitesse variable E-Flow VS,1,5
+2233,498,1100,1,VSP22FL,355.660000,1,355.660000,Pompe filtration piscine à vitesse variable E-Flow VS,1,5
+2240,505,1101,11,LAMPOL3TRANSLU,149.490000,1,149.490000,Lames de remplacement en Polycarbonate pour volet de piscine,1,61
+2241,505,1102,11,LAMPOL350TRANSLU,170.530000,1,170.530000,Lames de remplacement en Polycarbonate pour volet de piscine,1,61
+2242,505,1103,11,LAMPOL4TRANSLU,191.580000,1,191.580000,Lames de remplacement en Polycarbonate pour volet de piscine,1,61
+2243,505,1104,11,LAMPOL450TRANSLU,212.630000,1,212.630000,Lames de remplacement en Polycarbonate pour volet de piscine,1,61
+2244,505,1105,11,LAMPOL5TRANSLU,233.670000,1,233.670000,Lames de remplacement en Polycarbonate pour volet de piscine,1,61
+2245,505,1106,11,LAMPOL550TRANSLU,254.720000,1,254.720000,Lames de remplacement en Polycarbonate pour volet de piscine,1,61
+2246,505,1107,11,LAMPOL6TRANSLU,275.770000,1,275.770000,Lames de remplacement en Polycarbonate pour volet de piscine,1,61
+2250,505,1111,11,LAMPOL8TRANSLU,386.940000,1,386.940000,Lames de remplacement en Polycarbonate pour volet de piscine,1,61
+2251,505,1112,11,LAMPOL3GN,149.490000,1,149.490000,Lames de remplacement en Polycarbonate pour volet de piscine,1,61
+2252,505,1113,11,LAMPOL3BLEUTRAN,149.490000,1,149.490000,Lames de remplacement en Polycarbonate pour volet de piscine,1,61
+2253,505,1114,11,LAMPOL3TRANSNOIR,149.490000,1,149.490000,Lames de remplacement en Polycarbonate pour volet de piscine,1,61
+2255,505,1115,11,LAMPOL350BLEUTRAN,170.530000,1,170.530000,Lames de remplacement en Polycarbonate pour volet de piscine,1,61
+2256,505,1116,11,LAMPOL350TRANSNOIR,170.530000,1,170.530000,Lames de remplacement en Polycarbonate pour volet de piscine,1,61
+2254,505,1117,11,LAMPOL350GN,170.530000,1,170.530000,Lames de remplacement en Polycarbonate pour volet de piscine,1,61
+2258,505,1118,11,LAMPOL4BLEUTRAN,191.580000,1,191.580000,Lames de remplacement en Polycarbonate pour volet de piscine,1,61
+2257,505,1119,11,LAMPOL4GN,191.580000,1,191.580000,Lames de remplacement en Polycarbonate pour volet de piscine,1,61
+2259,505,1120,11,LAMPOL4TRANSNOIR,191.580000,1,191.580000,Lames de remplacement en Polycarbonate pour volet de piscine,1,61
+2261,505,1121,11,LAMPOL450BLEUTRAN,212.630000,1,212.630000,Lames de remplacement en Polycarbonate pour volet de piscine,1,61
+2260,505,1122,11,LAMPOL450GN,212.630000,1,212.630000,Lames de remplacement en Polycarbonate pour volet de piscine,1,61
+2262,505,1123,11,LAMPOL450TRANSNOIR,212.630000,1,212.630000,Lames de remplacement en Polycarbonate pour volet de piscine,1,61
+2264,505,1124,11,LAMPOL5BLEUTRAN,233.670000,1,233.670000,Lames de remplacement en Polycarbonate pour volet de piscine,1,61
+2265,505,1125,11,LAMPOL5TRANSNOIR,233.670000,1,233.670000,Lames de remplacement en Polycarbonate pour volet de piscine,1,61
+2263,505,1126,11,LAMPOL5GN,233.670000,1,233.670000,Lames de remplacement en Polycarbonate pour volet de piscine,1,61
+2266,505,1127,11,LAMPOL550GN,254.720000,1,254.720000,Lames de remplacement en Polycarbonate pour volet de piscine,1,61
+2268,505,1128,11,LAMPOL550TRANSNOIR,254.720000,1,254.720000,Lames de remplacement en Polycarbonate pour volet de piscine,1,61
+2267,505,1129,11,LAMPOL550BLEUTRAN,254.720000,1,254.720000,Lames de remplacement en Polycarbonate pour volet de piscine,1,61
+2269,505,1130,11,LAMPOL6GN,275.770000,1,275.770000,Lames de remplacement en Polycarbonate pour volet de piscine,1,61
+2271,505,1131,11,LAMPOL6TRANSNOIR,275.770000,1,275.770000,Lames de remplacement en Polycarbonate pour volet de piscine,1,61
+2270,505,1132,11,LAMPOL6BLEUTRAN,275.770000,1,275.770000,Lames de remplacement en Polycarbonate pour volet de piscine,1,61
+2282,505,1142,11,LAMPOL8BLEUTRAN,386.940000,1,386.940000,Lames de remplacement en Polycarbonate pour volet de piscine,1,61
+2281,505,1143,11,LAMPOL8GN,386.940000,1,386.940000,Lames de remplacement en Polycarbonate pour volet de piscine,1,61
+2283,505,1144,11,LAMPOL8TRANSNOIR,386.940000,1,386.940000,Lames de remplacement en Polycarbonate pour volet de piscine,1,61
+2287,508,1145,11,VRSIL80S-BL,831.010000,1,831.010000,Structure fixe volet Silver Roll,0,83
+2289,508,1146,11,VRSIL80S-GR,831.010000,1,831.010000,Structure fixe volet Silver Roll,0,83
+2290,508,1147,11,VRSIL80S-GA,831.010000,1,831.010000,Structure fixe volet Silver Roll,0,83
+2288,508,1148,11,VRSIL80S-SA,831.010000,1,831.010000,Structure fixe volet Silver Roll,0,83
+2296,508,1153,11,VRSIL120S-SA,1023.430000,1,1023.430000,Structure fixe volet Silver Roll,0,83
+2297,508,1154,11,VRSIL120S-GR,1023.430000,1,1023.430000,Structure fixe volet Silver Roll,0,83
+2298,508,1155,11,VRSIL120S-GA,1023.430000,1,1023.430000,Structure fixe volet Silver Roll,0,83
+2295,508,1156,11,VRSIL120S-BL,1023.430000,1,1023.430000,Structure fixe volet Silver Roll,0,83
+2299,508,1157,11,VRSIL200-BL,1203.650000,1,1203.650000,Structure fixe volet Silver Roll,0,83
+2302,508,1158,11,VRSIL200S-GA,1203.650000,1,1203.650000,Structure fixe volet Silver Roll,0,83
+2300,508,1159,11,VRSIL200S-SA,1203.650000,1,1203.650000,Structure fixe volet Silver Roll,0,83
+2301,508,1160,11,VRSIL200S-GR,1203.650000,1,1203.650000,Structure fixe volet Silver Roll,0,83
+2303,508,1161,11,VRSIL120BD-BL,1351.370000,1,1351.370000,Structure fixe volet Silver Roll,0,83
+2305,508,1162,11,VRSIL120BD-GR,1351.370000,1,1351.370000,Structure fixe volet Silver Roll,0,83
+2306,508,1163,11,VRSIL120BD-GA,1351.370000,1,1351.370000,Structure fixe volet Silver Roll,0,83
+2304,508,1164,11,VRSIL120BD-SA,1351.370000,1,1351.370000,Structure fixe volet Silver Roll,0,83
+2308,509,1165,11,VRMOUV80S-BL,1416.550000,0,1416.550000,Structure déplacable Mouv and Roll,0,83
+2309,509,1166,11,VRMOUV80S-SA,1416.550000,0,1416.550000,Structure déplacable Mouv and Roll,0,83
+2310,509,1167,11,VRMOUV80S-GR,1416.550000,0,1416.550000,Structure déplacable Mouv and Roll,0,83
+2311,509,1168,11,VRMOUV80S-GA,1416.550000,0,1416.550000,Structure déplacable Mouv and Roll,0,83
+2312,509,1169,11,VRMOUV200S-BL,1665.290000,0,1665.290000,Structure déplacable Mouv and Roll,0,83
+2313,509,1170,11,VRMOUV200S-SA,1665.290000,0,1665.290000,Structure déplacable Mouv and Roll,0,83
+2314,509,1171,11,VRMOUV200S-GR,1665.290000,0,1665.290000,Structure déplacable Mouv and Roll,0,83
+2315,509,1172,11,VRMOUV200S-GA,1665.290000,0,1665.290000,Structure déplacable Mouv and Roll,0,83
+2316,509,1173,11,VRMOUV120BD-BL,1857.030000,0,1857.030000,Structure déplacable Mouv and Roll,0,83
+2317,509,1174,11,VRMOUV120BD-SA,1857.030000,0,1857.030000,Structure déplacable Mouv and Roll,0,83
+2318,509,1175,11,VRMOUV120BD-GR,1857.030000,0,1857.030000,Structure déplacable Mouv and Roll,0,83
+2319,509,1176,11,VRMOUV120BD-GA,1857.030000,0,1857.030000,Structure déplacable Mouv and Roll,0,83
+2470,535,1301,1,74139,427.450000,1,427.450000,Pompe à chaleur piscine AQUASPHERE FSP,1,51
+2471,535,1302,1,74140,520.150000,1,520.150000,Pompe à chaleur piscine AQUASPHERE FSP,1,51
+2606,572,1391,11,VRSUB4,1769.710000,0,1769.710000,Structure fixe volet immergé Subwater,0,83
+2608,572,1392,11,VRSUB6,1854.700000,0,1854.700000,Structure fixe volet immergé Subwater,0,83
+2607,572,1393,11,VRSUB5,1929.460000,0,1929.460000,Structure fixe volet immergé Subwater,0,83

+ 1 - 0
web/.htaccess

@@ -1,5 +1,6 @@
 RewriteEngine on
 
+RewriteRule ^sync\.php$ sync.php [L,QSA]
 RewriteRule ^([a-z_-]+)(/)?$ $1.php [L,QSA]
 RewriteRule ^([a-z_-]+)/([a-z_-]+)(/)?$ $1.php?action=$2 [L,QSA]
 

+ 14 - 0
web/address.php

@@ -0,0 +1,14 @@
+<?php
+
+require_once "bootstrap.inc.php";
+//echo 'bootstrap loaded';
+
+require_once CONTROLLER_PATH."/synchro_address.php";
+
+$controller = new Controller\synchro_address();
+
+$controller->execute();
+
+$controller->display();
+
+//echo '<hr />'; echo '<h2>Debug</h2>'; $logger->display();

+ 2 - 1
web/bootstrap.inc.php

@@ -4,7 +4,8 @@ define('WEB_PATH', realpath(dirname(__FILE__)));
 define('ROOT_PATH', realpath(WEB_PATH.'/..'));
 define('APP_PATH', ROOT_PATH.'/app');
 define('SRC_PATH', ROOT_PATH.'/src');
+define('CFG_PATH', ROOT_PATH.'/config');
 
-require_once APP_PATH.'/config/config.inc.php';
+require_once CFG_PATH.'/config.inc.php';
 require_once SRC_PATH.'/bootstrap.inc.php';
 

+ 18 - 0
web/clean.php

@@ -0,0 +1,18 @@
+<?php
+
+require_once "bootstrap.inc.php";
+require_once('../src/sync/common.inc.php');
+
+$options = ['product', 'product_attribute', 'product_lot', 'product_pack', 'supplier_price', 'stock'];
+
+echo '<p>';
+foreach($options as $option)
+	echo '<a href="?t='.$option.'">'.$option.'</a> | ';
+echo '</p>';
+
+if (empty($_GET['t']))
+	die('param t required');
+
+if (in_array($_GET['t'], $options))
+	require_once('../src/clean/'.$_GET['t'].'.inc.php');
+

+ 7 - 1
web/index.php

@@ -2,4 +2,10 @@
 
 require_once "bootstrap.inc.php";
 
-echo 'YO MAN';
+if (isset($_GET['yoman'])) {
+	echo 'YO MAN';
+
+	echo '<p>DB</p>'; var_dump($db);
+	echo '<p>DB_D</p>'; var_dump($db_d);
+	echo '<p>DB_P</p>'; var_dump($db_p);
+}

+ 18 - 0
web/install.php

@@ -0,0 +1,18 @@
+<?php
+
+require_once "bootstrap.inc.php";
+require_once('../src/sync/common.inc.php');
+
+$options = ['supplier', 'product', 'img', 'product_lot', 'supplier_price', 'order', 'customer', 'address'];
+
+echo '<p>';
+foreach($options as $option)
+	echo '<a href="?t='.$option.'">'.$option.'</a> | ';
+echo '</p>';
+
+if (empty($_GET['t']))
+	die('param t required');
+	
+if (in_array($_GET['t'], $options))
+	require_once('../src/install/'.$_GET['t'].'.inc.php');
+

+ 0 - 0
web/phpinfo.php → web/phpinfo_lemathou.php


+ 18 - 0
web/repair.php

@@ -0,0 +1,18 @@
+<?php
+
+require_once "bootstrap.inc.php";
+require_once('../src/sync/common.inc.php');
+
+$options = ['supplier', 'product', 'img', 'product_lot', 'supplier_price', 'order', 'customer', 'address'];
+
+echo '<p>';
+foreach($options as $option)
+	echo '<a href="?t='.$option.'">'.$option.'</a> | ';
+echo '</p>';
+
+if (empty($_GET['t']))
+	die('param t required');
+	
+if (in_array($_GET['t'], $options))
+	require_once('../src/repair/'.$_GET['t'].'.inc.php');
+

+ 14 - 0
web/stock.php

@@ -0,0 +1,14 @@
+<?php
+
+require_once "bootstrap.inc.php";
+//echo 'bootstrap loaded';
+
+require_once CONTROLLER_PATH."/synchro_stock.php";
+
+$controller = new Controller\synchro_stock();
+
+$controller->execute();
+
+$controller->display();
+
+//echo '<hr />'; echo '<h2>Debug</h2>'; $logger->display();

+ 14 - 0
web/supplier_price.php

@@ -0,0 +1,14 @@
+<?php
+
+require_once "bootstrap.inc.php";
+//echo 'bootstrap loaded';
+
+require_once CONTROLLER_PATH."/synchro_supplier_price.php";
+
+$controller = new Controller\synchro_supplier_price();
+
+$controller->execute();
+
+$controller->display();
+
+//echo '<hr />'; echo '<h2>Debug</h2>'; $logger->display();

+ 24 - 0
web/sync.php

@@ -0,0 +1,24 @@
+<?php
+
+require_once "bootstrap.inc.php";
+require_once('../src/sync/common.inc.php');
+
+foreach($_GET as $i=>&$j) {
+	if (!isset($_POST[$i]))
+		$_POST[$i] = $j;
+}
+
+if (empty($_POST['password']) || $_POST['password']!==WS_PASSWORD)
+	sync_error("Auth failed");
+if (empty($_POST['type']) || !is_string($type=$_POST['type']))
+	sync_error("Bad param : type");
+if (empty($_POST['sens']) || !is_string($sens=$_POST['sens']) || !in_array($_POST['sens'], ['pd', 'dp']))
+	sync_error("Bad param : sens");
+if (empty($_POST['action']) || !is_string($action=$_POST['action']) || !in_array($_POST['action'], ['create', 'osync', 'delete']))
+	sync_error("Bad param : action");
+if (empty($_POST['otype']) || !is_string($otype=$_POST['otype']))
+	sync_error("Bad param : otype");
+if (empty($_POST['oid']) || !is_numeric($oid=$_POST['oid']))
+	sync_error("Bad param : oid");
+
+sync::_action($type, $sens, $action, $otype, $oid);

+ 18 - 0
web/update.php

@@ -0,0 +1,18 @@
+<?php
+
+require_once "bootstrap.inc.php";
+require_once('../src/sync/common.inc.php');
+
+$options = ['supplier', 'product', 'img', 'product_lot', 'supplier_price', 'order', 'customer', 'address'];
+
+echo '<p>';
+foreach($options as $option)
+	echo '<a href="?t='.$option.'">'.$option.'</a> | ';
+echo '</p>';
+
+if (empty($_GET['t']))
+	die('param t required');
+	
+if (in_array($_GET['t'], $options))
+	require_once('../src/update/'.$_GET['t'].'.inc.php');
+

Some files were not shown because too many files changed in this diff