dolibarr  18.0.6
import_csv.modules.php
Go to the documentation of this file.
1 <?php
2 /* Copyright (C) 2006-2012 Laurent Destailleur <eldy@users.sourceforge.net>
3  * Copyright (C) 2009-2012 Regis Houssin <regis.houssin@inodbox.com>
4  * Copyright (C) 2012 Christophe Battarel <christophe.battarel@altairis.fr>
5  * Copyright (C) 2012-2016 Juanjo Menent <jmenent@2byte.es>
6  *
7  * This program is free software; you can redistribute it and/or modify
8  * it under the terms of the GNU General Public License as published by
9  * the Free Software Foundation; either version 3 of the License, or
10  * (at your option) any later version.
11  *
12  * This program is distributed in the hope that it will be useful,
13  * but WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15  * GNU General Public License for more details.
16  *
17  * You should have received a copy of the GNU General Public License
18  * along with this program. If not, see <https://www.gnu.org/licenses/>.
19  * or see https://www.gnu.org/
20  */
21 
28 require_once DOL_DOCUMENT_ROOT.'/core/modules/import/modules_import.php';
29 
30 
34 class ImportCsv extends ModeleImports
35 {
39  public $db;
40 
41  public $datatoimport;
42 
46  public $error = '';
47 
51  public $errors = array();
52 
56  public $id;
57 
61  public $label;
62 
63  public $extension; // Extension of files imported by driver
64 
69  public $version = 'dolibarr';
70 
71  public $label_lib; // Label of external lib used by driver
72 
73  public $version_lib; // Version of external lib used by driver
74 
75  public $separator;
76 
77  public $file; // Path of file
78 
79  public $handle; // Handle fichier
80 
81  public $cacheconvert = array(); // Array to cache list of value found after a convertion
82 
83  public $cachefieldtable = array(); // Array to cache list of value found into fields@tables
84 
85  public $nbinsert = 0; // # of insert done during the import
86 
87  public $nbupdate = 0; // # of update done during the import
88 
89  public $charset = '';
90 
91 
98  public function __construct($db, $datatoimport)
99  {
100  global $conf, $langs;
101 
102  parent::__construct();
103  $this->db = $db;
104 
105  $this->separator = (GETPOST('separator') ?GETPOST('separator') : (empty($conf->global->IMPORT_CSV_SEPARATOR_TO_USE) ? ',' : $conf->global->IMPORT_CSV_SEPARATOR_TO_USE));
106  $this->enclosure = '"';
107  $this->escape = '"';
108 
109  $this->id = 'csv'; // Same value then xxx in file name export_xxx.modules.php
110  $this->label = 'Csv'; // Label of driver
111  $this->desc = $langs->trans("CSVFormatDesc", $this->separator, $this->enclosure, $this->escape);
112  $this->extension = 'csv'; // Extension for generated file by this driver
113  $this->picto = 'mime/other'; // Picto
114  $this->version = '1.34'; // Driver version
115 
116  // If driver use an external library, put its name here
117  $this->label_lib = 'Dolibarr';
118  $this->version_lib = DOL_VERSION;
119 
120  $this->datatoimport = $datatoimport;
121  if (preg_match('/^societe_/', $datatoimport)) {
122  $this->thirdpartyobject = new Societe($this->db);
123  }
124  }
125 
126 
127  // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
134  public function write_header_example($outputlangs)
135  {
136  // phpcs:enable
137  return '';
138  }
139 
140  // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
148  public function write_title_example($outputlangs, $headerlinefields)
149  {
150  // phpcs:enable
151  $s = join($this->separator, array_map('cleansep', $headerlinefields));
152  return $s."\n";
153  }
154 
155  // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
163  public function write_record_example($outputlangs, $contentlinevalues)
164  {
165  // phpcs:enable
166  $s = join($this->separator, array_map('cleansep', $contentlinevalues));
167  return $s."\n";
168  }
169 
170  // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
177  public function write_footer_example($outputlangs)
178  {
179  // phpcs:enable
180  return '';
181  }
182 
183 
184  // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
191  public function import_open_file($file)
192  {
193  // phpcs:enable
194  global $langs;
195  $ret = 1;
196 
197  dol_syslog(get_class($this)."::open_file file=".$file);
198 
199  ini_set('auto_detect_line_endings', 1); // For MAC compatibility
200 
201  $this->handle = fopen(dol_osencode($file), "r");
202  if (!$this->handle) {
203  $langs->load("errors");
204  $this->error = $langs->trans("ErrorFailToOpenFile", $file);
205  $ret = -1;
206  } else {
207  $this->file = $file;
208  }
209 
210  return $ret;
211  }
212 
213 
214  // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
221  public function import_get_nb_of_lines($file)
222  {
223  // phpcs:enable
224  return dol_count_nb_of_line($file);
225  }
226 
227 
228  // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
234  public function import_read_header()
235  {
236  // phpcs:enable
237  return 0;
238  }
239 
240 
241  // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
247  public function import_read_record()
248  {
249  // phpcs:enable
250  global $conf;
251 
252  $arrayres = fgetcsv($this->handle, 100000, $this->separator, $this->enclosure, $this->escape);
253 
254  // End of file
255  if ($arrayres === false) {
256  return false;
257  }
258 
259  //var_dump($this->handle);
260  //var_dump($arrayres);exit;
261  $newarrayres = array();
262  if ($arrayres && is_array($arrayres)) {
263  foreach ($arrayres as $key => $val) {
264  if (!empty($conf->global->IMPORT_CSV_FORCE_CHARSET)) { // Forced charset
265  if (strtolower($conf->global->IMPORT_CSV_FORCE_CHARSET) == 'utf8') {
266  $newarrayres[$key]['val'] = $val;
267  $newarrayres[$key]['type'] = (dol_strlen($val) ? 1 : -1); // If empty we considere it's null
268  } else {
269  $newarrayres[$key]['val'] = utf8_encode($val);
270  $newarrayres[$key]['type'] = (dol_strlen($val) ? 1 : -1); // If empty we considere it's null
271  }
272  } else // Autodetect format (UTF8 or ISO)
273  {
274  if (utf8_check($val)) {
275  $newarrayres[$key]['val'] = $val;
276  $newarrayres[$key]['type'] = (dol_strlen($val) ? 1 : -1); // If empty we considere it's null
277  } else {
278  $newarrayres[$key]['val'] = utf8_encode($val);
279  $newarrayres[$key]['type'] = (dol_strlen($val) ? 1 : -1); // If empty we considere it's null
280  }
281  }
282  }
283 
284  $this->col = count($newarrayres);
285  }
286 
287  return $newarrayres;
288  }
289 
290  // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
296  public function import_close_file()
297  {
298  // phpcs:enable
299  fclose($this->handle);
300  return 0;
301  }
302 
303 
304  // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
316  public function import_insert($arrayrecord, $array_match_file_to_database, $objimport, $maxfields, $importid, $updatekeys)
317  {
318  // phpcs:enable
319  global $langs, $conf, $user;
320  global $thirdparty_static; // Specific to thirdparty import
321  global $tablewithentity_cache; // Cache to avoid to call desc at each rows on tables
322 
323  $error = 0;
324  $warning = 0;
325  $this->errors = array();
326  $this->warnings = array();
327 
328  //dol_syslog("import_csv.modules maxfields=".$maxfields." importid=".$importid);
329 
330  //var_dump($array_match_file_to_database);
331  //var_dump($arrayrecord); exit;
332 
333  $array_match_database_to_file = array_flip($array_match_file_to_database);
334  $sort_array_match_file_to_database = $array_match_file_to_database;
335  ksort($sort_array_match_file_to_database);
336 
337  //var_dump($sort_array_match_file_to_database);
338 
339  if (count($arrayrecord) == 0 || (count($arrayrecord) == 1 && empty($arrayrecord[0]['val']))) {
340  //print 'W';
341  $this->warnings[$warning]['lib'] = $langs->trans('EmptyLine');
342  $this->warnings[$warning]['type'] = 'EMPTY';
343  $warning++;
344  } else {
345  $last_insert_id_array = array(); // store the last inserted auto_increment id for each table, so that dependent tables can be inserted with the appropriate id (eg: extrafields fk_object will be set with the last inserted object's id)
346  $updatedone = false;
347  $insertdone = false;
348  // For each table to insert, me make a separate insert
349  foreach ($objimport->array_import_tables[0] as $alias => $tablename) {
350  // Build sql request
351  $sql = '';
352  $listfields = array();
353  $listvalues = array();
354  $i = 0;
355  $errorforthistable = 0;
356 
357  // Define $tablewithentity_cache[$tablename] if not already defined
358  if (!isset($tablewithentity_cache[$tablename])) { // keep this test with "isset"
359  dol_syslog("Check if table ".$tablename." has an entity field");
360  $resql = $this->db->DDLDescTable($tablename, 'entity');
361  if ($resql) {
362  $obj = $this->db->fetch_object($resql);
363  if ($obj) {
364  $tablewithentity_cache[$tablename] = 1; // table contains entity field
365  } else {
366  $tablewithentity_cache[$tablename] = 0; // table does not contain entity field
367  }
368  } else {
369  dol_print_error($this->db);
370  }
371  } else {
372  //dol_syslog("Table ".$tablename." check for entity into cache is ".$tablewithentity_cache[$tablename]);
373  }
374 
375  // Define array to convert fields ('c.ref', ...) into column index (1, ...)
376  $arrayfield = array();
377  foreach ($sort_array_match_file_to_database as $key => $val) {
378  $arrayfield[$val] = ($key - 1);
379  }
380 
381  // $arrayrecord start at key 0
382  // $sort_array_match_file_to_database start at key 1
383 
384  // Loop on each fields in the match array: $key = 1..n, $val=alias of field (s.nom)
385  foreach ($sort_array_match_file_to_database as $key => $val) {
386  $fieldalias = preg_replace('/\..*$/i', '', $val);
387  $fieldname = preg_replace('/^.*\./i', '', $val);
388 
389  if ($alias != $fieldalias) {
390  continue; // Not a field of current table
391  }
392 
393  if ($key <= $maxfields) {
394  // Set $newval with value to insert and set $listvalues with sql request part for insert
395  $newval = '';
396  if ($arrayrecord[($key - 1)]['type'] > 0) {
397  $newval = $arrayrecord[($key - 1)]['val']; // If type of field into input file is not empty string (so defined into input file), we get value
398  }
399 
400  //var_dump($newval);var_dump($val);
401  //var_dump($objimport->array_import_convertvalue[0][$val]);
402 
403  // Make some tests on $newval
404 
405  // Is it a required field ?
406  if (preg_match('/\*/', $objimport->array_import_fields[0][$val]) && ((string) $newval == '')) {
407  $this->errors[$error]['lib'] = $langs->trans('ErrorMissingMandatoryValue', $key);
408  $this->errors[$error]['type'] = 'NOTNULL';
409  $errorforthistable++;
410  $error++;
411  } else {
412  // Test format only if field is not a missing mandatory field (field may be a value or empty but not mandatory)
413  // We convert field if required
414  if (!empty($objimport->array_import_convertvalue[0][$val])) {
415  //print 'Must convert '.$newval.' with rule '.join(',',$objimport->array_import_convertvalue[0][$val]).'. ';
416  if ($objimport->array_import_convertvalue[0][$val]['rule'] == 'fetchidfromcodeid'
417  || $objimport->array_import_convertvalue[0][$val]['rule'] == 'fetchidfromref'
418  || $objimport->array_import_convertvalue[0][$val]['rule'] == 'fetchidfromcodeorlabel'
419  ) {
420  // New val can be an id or ref. If it start with id: it is forced to id, if it start with ref: it is forced to ref. It not, we try to guess.
421  $isidorref = 'id';
422  if (!is_numeric($newval) && $newval != '' && !preg_match('/^id:/i', $newval)) {
423  $isidorref = 'ref';
424  }
425 
426  $newval = preg_replace('/^(id|ref):/i', '', $newval); // Remove id: or ref: that was used to force if field is id or ref
427  //print 'Newval is now "'.$newval.'" and is type '.$isidorref."<br>\n";
428 
429  if ($isidorref == 'ref') { // If value into input import file is a ref, we apply the function defined into descriptor
430  $file = (empty($objimport->array_import_convertvalue[0][$val]['classfile']) ? $objimport->array_import_convertvalue[0][$val]['file'] : $objimport->array_import_convertvalue[0][$val]['classfile']);
431  $class = $objimport->array_import_convertvalue[0][$val]['class'];
432  $method = $objimport->array_import_convertvalue[0][$val]['method'];
433  if ($this->cacheconvert[$file.'_'.$class.'_'.$method.'_'][$newval] != '') {
434  $newval = $this->cacheconvert[$file.'_'.$class.'_'.$method.'_'][$newval];
435  } else {
436  $resultload = dol_include_once($file);
437  if (empty($resultload)) {
438  dol_print_error('', 'Error trying to call file='.$file.', class='.$class.', method='.$method);
439  break;
440  }
441  $classinstance = new $class($this->db);
442  if ($class == 'CGenericDic') {
443  $classinstance->element = $objimport->array_import_convertvalue[0][$val]['element'];
444  $classinstance->table_element = $objimport->array_import_convertvalue[0][$val]['table_element'];
445  }
446 
447  // Try the fetch from code or ref
448  $param_array = array('', $newval);
449  if ($class == 'AccountingAccount') {
450  //var_dump($arrayrecord[0]['val']);
451  /*include_once DOL_DOCUMENT_ROOT.'/accountancy/class/accountancysystem.class.php';
452  $tmpchartofaccount = new AccountancySystem($this->db);
453  $tmpchartofaccount->fetch(getDolGlobalInt('CHARTOFACCOUNTS'));
454  //var_dump($tmpchartofaccount->ref.' - '.$arrayrecord[0]['val']);
455  if ((! (getDolGlobalInt('CHARTOFACCOUNTS') > 0)) || $tmpchartofaccount->ref != $arrayrecord[0]['val'])
456  {
457  $this->errors[$error]['lib']=$langs->trans('ErrorImportOfChartLimitedToCurrentChart', $tmpchartofaccount->ref);
458  $this->errors[$error]['type']='RESTRICTONCURRENCTCHART';
459  $errorforthistable++;
460  $error++;
461  }*/
462  $param_array = array('', $newval, 0, $arrayrecord[0]['val']); // Param to fetch parent from account, in chart.
463  }
464 
465  $result = call_user_func_array(array($classinstance, $method), $param_array);
466 
467  // If duplicate record found
468  if (!($classinstance->id != '') && $result == -2) {
469  $this->errors[$error]['lib'] = $langs->trans('ErrorMultipleRecordFoundFromRef', $newval);
470  $this->errors[$error]['type'] = 'FOREIGNKEY';
471  $errorforthistable++;
472  $error++;
473  }
474 
475  // If not found, try the fetch from label
476  if (!($classinstance->id != '') && $objimport->array_import_convertvalue[0][$val]['rule'] == 'fetchidfromcodeorlabel') {
477  $param_array = array('', '', $newval);
478  call_user_func_array(array($classinstance, $method), $param_array);
479  }
480  $this->cacheconvert[$file.'_'.$class.'_'.$method.'_'][$newval] = $classinstance->id;
481 
482  //print 'We have made a '.$class.'->'.$method.' to get id from code '.$newval.'. ';
483  if ($classinstance->id != '') { // id may be 0, it is a found value
484  $newval = $classinstance->id;
485  } elseif (! $error) {
486  if (!empty($objimport->array_import_convertvalue[0][$val]['dict'])) {
487  $this->errors[$error]['lib'] = $langs->trans('ErrorFieldValueNotIn', num2Alpha($key - 1), $newval, 'code', $langs->transnoentitiesnoconv($objimport->array_import_convertvalue[0][$val]['dict']));
488  } elseif (!empty($objimport->array_import_convertvalue[0][$val]['element'])) {
489  $this->errors[$error]['lib'] = $langs->trans('ErrorFieldRefNotIn', num2Alpha($key - 1), $newval, $langs->transnoentitiesnoconv($objimport->array_import_convertvalue[0][$val]['element']));
490  } else {
491  $this->errors[$error]['lib'] = 'ErrorBadDefinitionOfImportProfile';
492  }
493  $this->errors[$error]['type'] = 'FOREIGNKEY';
494  $errorforthistable++;
495  $error++;
496  }
497  }
498  }
499  } elseif ($objimport->array_import_convertvalue[0][$val]['rule'] == 'fetchidfromcodeandlabel') {
500  $isidorref = 'id';
501  if (!is_numeric($newval) && $newval != '' && !preg_match('/^id:/i', $newval)) {
502  $isidorref = 'ref';
503  }
504  $newval = preg_replace('/^(id|ref):/i', '', $newval);
505 
506  if ($isidorref == 'ref') {
507  $file = (empty($objimport->array_import_convertvalue[0][$val]['classfile']) ? $objimport->array_import_convertvalue[0][$val]['file'] : $objimport->array_import_convertvalue[0][$val]['classfile']);
508  $class = $objimport->array_import_convertvalue[0][$val]['class'];
509  $method = $objimport->array_import_convertvalue[0][$val]['method'];
510  $codefromfield = $objimport->array_import_convertvalue[0][$val]['codefromfield'];
511  $code = $arrayrecord[$arrayfield[$codefromfield]]['val'];
512  if ($this->cacheconvert[$file.'_'.$class.'_'.$method.'_'.$code][$newval] != '') {
513  $newval = $this->cacheconvert[$file.'_'.$class.'_'.$method.'_'.$code][$newval];
514  } else {
515  $resultload = dol_include_once($file);
516  if (empty($resultload)) {
517  dol_print_error('', 'Error trying to call file='.$file.', class='.$class.', method='.$method.', code='.$code);
518  break;
519  }
520  $classinstance = new $class($this->db);
521  // Try the fetch from code and ref
522  $param_array = array('', $newval, $code);
523  call_user_func_array(array($classinstance, $method), $param_array);
524  $this->cacheconvert[$file.'_'.$class.'_'.$method.'_'.$code][$newval] = $classinstance->id;
525  if ($classinstance->id > 0) { // we found record
526  $newval = $classinstance->id;
527  } else {
528  if (!empty($objimport->array_import_convertvalue[0][$val]['dict'])) {
529  $this->errors[$error]['lib'] = $langs->trans('ErrorFieldValueNotIn', num2Alpha($key - 1), $newval, 'scale', $langs->transnoentitiesnoconv($objimport->array_import_convertvalue[0][$val]['dict']));
530  } else {
531  $this->errors[$error]['lib'] = 'ErrorFieldValueNotIn';
532  }
533  $this->errors[$error]['type'] = 'FOREIGNKEY';
534  $errorforthistable++;
535  $error++;
536  }
537  }
538  }
539  } elseif ($objimport->array_import_convertvalue[0][$val]['rule'] == 'zeroifnull') {
540  if (empty($newval)) {
541  $newval = '0';
542  }
543  } elseif ($objimport->array_import_convertvalue[0][$val]['rule'] == 'fetchidfromcodeunits' || $objimport->array_import_convertvalue[0][$val]['rule'] == 'fetchscalefromcodeunits') {
544  $file = (empty($objimport->array_import_convertvalue[0][$val]['classfile']) ? $objimport->array_import_convertvalue[0][$val]['file'] : $objimport->array_import_convertvalue[0][$val]['classfile']);
545  $class = $objimport->array_import_convertvalue[0][$val]['class'];
546  $method = $objimport->array_import_convertvalue[0][$val]['method'];
547  $units = $objimport->array_import_convertvalue[0][$val]['units'];
548  if ($this->cacheconvert[$file.'_'.$class.'_'.$method.'_'.$units][$newval] != '') {
549  $newval = $this->cacheconvert[$file.'_'.$class.'_'.$method.'_'.$units][$newval];
550  } else {
551  $resultload = dol_include_once($file);
552  if (empty($resultload)) {
553  dol_print_error('', 'Error trying to call file='.$file.', class='.$class.', method='.$method.', units='.$units);
554  break;
555  }
556  $classinstance = new $class($this->db);
557  // Try the fetch from code or ref
558  call_user_func_array(array($classinstance, $method), array('', '', $newval, $units));
559  $scaleorid = (($objimport->array_import_convertvalue[0][$val]['rule'] == 'fetchidfromcodeunits') ? $classinstance->id : $classinstance->scale);
560  $this->cacheconvert[$file.'_'.$class.'_'.$method.'_'.$units][$newval] = $scaleorid;
561  //print 'We have made a '.$class.'->'.$method." to get a value from key '".$newval."' and we got '".$scaleorid."'.";exit;
562  if ($classinstance->id > 0) { // we found record
563  $newval = $scaleorid ? $scaleorid : 0;
564  } else {
565  if (!empty($objimport->array_import_convertvalue[0][$val]['dict'])) {
566  $this->errors[$error]['lib'] = $langs->trans('ErrorFieldValueNotIn', num2Alpha($key - 1), $newval, 'scale', $langs->transnoentitiesnoconv($objimport->array_import_convertvalue[0][$val]['dict']));
567  } else {
568  $this->errors[$error]['lib'] = 'ErrorFieldValueNotIn';
569  }
570  $this->errors[$error]['type'] = 'FOREIGNKEY';
571  $errorforthistable++;
572  $error++;
573  }
574  }
575  } elseif ($objimport->array_import_convertvalue[0][$val]['rule'] == 'getcustomercodeifauto') {
576  if (strtolower($newval) == 'auto') {
577  $this->thirdpartyobject->get_codeclient(0, 0);
578  $newval = $this->thirdpartyobject->code_client;
579  //print 'code_client='.$newval;
580  }
581  if (empty($newval)) {
582  $arrayrecord[($key - 1)]['type'] = -1; // If we get empty value, we will use "null"
583  }
584  } elseif ($objimport->array_import_convertvalue[0][$val]['rule'] == 'getsuppliercodeifauto') {
585  if (strtolower($newval) == 'auto') {
586  $this->thirdpartyobject->get_codefournisseur(0, 1);
587  $newval = $this->thirdpartyobject->code_fournisseur;
588  //print 'code_fournisseur='.$newval;
589  }
590  if (empty($newval)) {
591  $arrayrecord[($key - 1)]['type'] = -1; // If we get empty value, we will use "null"
592  }
593  } elseif ($objimport->array_import_convertvalue[0][$val]['rule'] == 'getcustomeraccountancycodeifauto') {
594  if (strtolower($newval) == 'auto') {
595  $this->thirdpartyobject->get_codecompta('customer');
596  $newval = $this->thirdpartyobject->code_compta;
597  //print 'code_compta='.$newval;
598  }
599  if (empty($newval)) {
600  $arrayrecord[($key - 1)]['type'] = -1; // If we get empty value, we will use "null"
601  }
602  } elseif ($objimport->array_import_convertvalue[0][$val]['rule'] == 'getsupplieraccountancycodeifauto') {
603  if (strtolower($newval) == 'auto') {
604  $this->thirdpartyobject->get_codecompta('supplier');
605  $newval = $this->thirdpartyobject->code_compta_fournisseur;
606  if (empty($newval)) {
607  $arrayrecord[($key - 1)]['type'] = -1; // If we get empty value, we will use "null"
608  }
609  //print 'code_compta_fournisseur='.$newval;
610  }
611  if (empty($newval)) {
612  $arrayrecord[($key - 1)]['type'] = -1; // If we get empty value, we will use "null"
613  }
614  } elseif ($objimport->array_import_convertvalue[0][$val]['rule'] == 'getrefifauto') {
615  if (strtolower($newval) == 'auto') {
616  $defaultref = '';
617 
618  $classModForNumber = $objimport->array_import_convertvalue[0][$val]['class'];
619  $pathModForNumber = $objimport->array_import_convertvalue[0][$val]['path'];
620 
621  if (!empty($classModForNumber) && !empty($pathModForNumber) && is_readable(DOL_DOCUMENT_ROOT.$pathModForNumber)) {
622  require_once DOL_DOCUMENT_ROOT.$pathModForNumber;
623  $modForNumber = new $classModForNumber;
624 
625  $tmpobject = null;
626  // Set the object with the date property when we can
627  if (!empty($objimport->array_import_convertvalue[0][$val]['classobject'])) {
628  $pathForObject = $objimport->array_import_convertvalue[0][$val]['pathobject'];
629  require_once DOL_DOCUMENT_ROOT.$pathForObject;
630  $tmpclassobject = $objimport->array_import_convertvalue[0][$val]['classobject'];
631  $tmpobject = new $tmpclassobject($this->db);
632  foreach ($arrayfield as $tmpkey => $tmpval) { // $arrayfield is array('c.ref'=>0, ...)
633  if (in_array($tmpkey, array('t.date', 'c.date_commande'))) {
634  $tmpobject->date = dol_stringtotime($arrayrecord[$arrayfield[$tmpkey]]['val'], 1);
635  }
636  }
637  }
638 
639  $defaultref = $modForNumber->getNextValue(null, $tmpobject);
640  }
641  if (is_numeric($defaultref) && $defaultref <= 0) { // If error
642  $defaultref = '';
643  }
644  $newval = $defaultref;
645  }
646  } elseif ($objimport->array_import_convertvalue[0][$val]['rule'] == 'compute') {
647  $file = (empty($objimport->array_import_convertvalue[0][$val]['classfile']) ? $objimport->array_import_convertvalue[0][$val]['file'] : $objimport->array_import_convertvalue[0][$val]['classfile']);
648  $class = $objimport->array_import_convertvalue[0][$val]['class'];
649  $method = $objimport->array_import_convertvalue[0][$val]['method'];
650  $resultload = dol_include_once($file);
651  if (empty($resultload)) {
652  dol_print_error('', 'Error trying to call file='.$file.', class='.$class.', method='.$method);
653  break;
654  }
655  $classinstance = new $class($this->db);
656  $res = call_user_func_array(array($classinstance, $method), array(&$arrayrecord, $arrayfield, ($key - 1)));
657  if (empty($classinstance->error) && empty($classinstance->errors)) {
658  $newval = $res; // We get new value computed.
659  } else {
660  $this->errors[$error]['type'] = 'CLASSERROR';
661  $this->errors[$error]['lib'] = implode(
662  "\n",
663  array_merge([$classinstance->error], $classinstance->errors)
664  );
665  $errorforthistable++;
666  $error++;
667  }
668  } elseif ($objimport->array_import_convertvalue[0][$val]['rule'] == 'numeric') {
669  $newval = price2num($newval);
670  } elseif ($objimport->array_import_convertvalue[0][$val]['rule'] == 'accountingaccount') {
671  if (empty($conf->global->ACCOUNTING_MANAGE_ZERO)) {
672  $newval = rtrim(trim($newval), "0");
673  } else {
674  $newval = trim($newval);
675  }
676  }
677 
678  //print 'Val to use as insert is '.$newval.'<br>';
679  }
680 
681  // Test regexp
682  if (!empty($objimport->array_import_regex[0][$val]) && ($newval != '')) {
683  // If test is "Must exist in a field@table or field@table:..."
684  $reg = array();
685  if (preg_match('/^(.+)@([^:]+)(:.+)?$/', $objimport->array_import_regex[0][$val], $reg)) {
686  $field = $reg[1];
687  $table = $reg[2];
688  $filter = !empty($reg[3]) ?substr($reg[3], 1) : '';
689 
690  $cachekey = $field.'@'.$table;
691  if (!empty($filter)) {
692  $cachekey .= ':'.$filter;
693  }
694 
695  // Load content of field@table into cache array
696  if (!is_array($this->cachefieldtable[$cachekey])) { // If content of field@table not already loaded into cache
697  $sql = "SELECT ".$field." as aliasfield FROM ".$table;
698  if (!empty($filter)) {
699  $sql .= ' WHERE '.$filter;
700  }
701 
702  $resql = $this->db->query($sql);
703  if ($resql) {
704  $num = $this->db->num_rows($resql);
705  $i = 0;
706  while ($i < $num) {
707  $obj = $this->db->fetch_object($resql);
708  if ($obj) {
709  $this->cachefieldtable[$cachekey][] = $obj->aliasfield;
710  }
711  $i++;
712  }
713  } else {
714  dol_print_error($this->db);
715  }
716  }
717 
718  // Now we check cache is not empty (should not) and key is into cache
719  if (!is_array($this->cachefieldtable[$cachekey]) || !in_array($newval, $this->cachefieldtable[$cachekey])) {
720  $tableforerror = $table;
721  if (!empty($filter)) {
722  $tableforerror .= ':'.$filter;
723  }
724  $this->errors[$error]['lib'] = $langs->transnoentitiesnoconv('ErrorFieldValueNotIn', num2Alpha($key - 1), $newval, $field, $tableforerror);
725  $this->errors[$error]['type'] = 'FOREIGNKEY';
726  $errorforthistable++;
727  $error++;
728  }
729  } elseif (!preg_match('/'.$objimport->array_import_regex[0][$val].'/i', $newval)) {
730  // If test is just a static regex
731  //if ($key == 19) print "xxx".$newval."zzz".$objimport->array_import_regex[0][$val]."<br>";
732  $this->errors[$error]['lib'] = $langs->transnoentitiesnoconv('ErrorWrongValueForField', num2Alpha($key - 1), $newval, $objimport->array_import_regex[0][$val]);
733  $this->errors[$error]['type'] = 'REGEX';
734  $errorforthistable++;
735  $error++;
736  }
737  }
738 
739  // Check HTML injection
740  $inj = testSqlAndScriptInject($newval, 0);
741  if ($inj) {
742  $this->errors[$error]['lib'] = $langs->transnoentitiesnoconv('ErrorHtmlInjectionForField', num2Alpha($key - 1), dol_trunc($newval, 100));
743  $this->errors[$error]['type'] = 'HTMLINJECTION';
744  $errorforthistable++;
745  $error++;
746  }
747 
748  // Other tests
749  // ...
750  }
751 
752  // Define $listfields and $listvalues to build SQL request
753  if (isModEnabled("socialnetworks") && strpos($fieldname, "socialnetworks") !== false) {
754  if (!in_array("socialnetworks", $listfields)) {
755  $listfields[] = "socialnetworks";
756  $socialkey = array_search("socialnetworks", $listfields); // Return position of 'socialnetworks' key in array
757  $listvalues[$socialkey] = '';
758  }
759  //var_dump($newval); var_dump($arrayrecord[($key - 1)]['type']);
760  if (!empty($newval) && $arrayrecord[($key - 1)]['type'] > 0) {
761  $socialkey = array_search("socialnetworks", $listfields); // Return position of 'socialnetworks' key in array
762  //var_dump('sk='.$socialkey); // socialkey=19
763  $socialnetwork = explode("_", $fieldname)[1];
764  if (empty($listvalues[$socialkey]) || $listvalues[$socialkey] == "null") {
765  $json = new stdClass();
766  $json->$socialnetwork = $newval;
767  $listvalues[$socialkey] = json_encode($json);
768  } else {
769  $jsondata = $listvalues[$socialkey];
770  $json = json_decode($jsondata);
771  $json->$socialnetwork = $newval;
772  $listvalues[$socialkey] = json_encode($json);
773  }
774  }
775  } else {
776  $listfields[] = $fieldname;
777  // Note: arrayrecord (and 'type') is filled with ->import_read_record called by import.php page before calling import_insert
778  if (empty($newval) && $arrayrecord[($key - 1)]['type'] < 0) {
779  $listvalues[] = ($newval == '0' ? $newval : "null");
780  } elseif (empty($newval) && $arrayrecord[($key - 1)]['type'] == 0) {
781  $listvalues[] = "''";
782  } else {
783  $listvalues[] = "'".$this->db->escape($newval)."'";
784  }
785  }
786  }
787  $i++;
788  }
789 
790  // We add hidden fields (but only if there is at least one field to add into table)
791  // We process here all the fields that were declared into the array $this->import_fieldshidden_array of the descriptor file.
792  // Previously we processed the ->import_fields_array.
793  if (!empty($listfields) && is_array($objimport->array_import_fieldshidden[0])) {
794  // Loop on each hidden fields to add them into listfields/listvalues
795  foreach ($objimport->array_import_fieldshidden[0] as $tmpkey => $tmpval) {
796  if (!preg_match('/^'.preg_quote($alias, '/').'\./', $tmpkey)) {
797  continue; // Not a field of current table
798  }
799  if ($tmpval == 'user->id') {
800  $listfields[] = preg_replace('/^'.preg_quote($alias, '/').'\./', '', $tmpkey);
801  $listvalues[] = ((int) $user->id);
802  } elseif (preg_match('/^lastrowid-/', $tmpval)) {
803  $tmp = explode('-', $tmpval);
804  $lastinsertid = (isset($last_insert_id_array[$tmp[1]])) ? $last_insert_id_array[$tmp[1]] : 0;
805  $keyfield = preg_replace('/^'.preg_quote($alias, '/').'\./', '', $tmpkey);
806  $listfields[] = $keyfield;
807  $listvalues[] = $lastinsertid;
808  //print $tmpkey."-".$tmpval."-".$listfields."-".$listvalues."<br>";exit;
809  } elseif (preg_match('/^const-/', $tmpval)) {
810  $tmp = explode('-', $tmpval, 2);
811  $listfields[] = preg_replace('/^'.preg_quote($alias, '/').'\./', '', $tmpkey);
812  $listvalues[] = "'".$this->db->escape($tmp[1])."'";
813  } elseif (preg_match('/^rule-/', $tmpval)) {
814  $fieldname = $tmpkey;
815  if (!empty($objimport->array_import_convertvalue[0][$fieldname])) {
816  if ($objimport->array_import_convertvalue[0][$fieldname]['rule'] == 'compute') {
817  $file = (empty($objimport->array_import_convertvalue[0][$fieldname]['classfile']) ? $objimport->array_import_convertvalue[0][$fieldname]['file'] : $objimport->array_import_convertvalue[0][$fieldname]['classfile']);
818  $class = $objimport->array_import_convertvalue[0][$fieldname]['class'];
819  $method = $objimport->array_import_convertvalue[0][$fieldname]['method'];
820  $resultload = dol_include_once($file);
821  if (empty($resultload)) {
822  dol_print_error('', 'Error trying to call file=' . $file . ', class=' . $class . ', method=' . $method);
823  break;
824  }
825  $classinstance = new $class($this->db);
826  $res = call_user_func_array(array($classinstance, $method), array(&$arrayrecord, $arrayfield, ($key - 1)));
827  if (empty($classinstance->error) && empty($classinstance->errors)) {
828  $fieldArr = explode('.', $fieldname);
829  if (count($fieldArr) > 0) {
830  $fieldname = $fieldArr[1];
831  }
832  $listfields[] = $fieldname;
833  $listvalues[] = $res;
834  } else {
835  $this->errors[$error]['type'] = 'CLASSERROR';
836  $this->errors[$error]['lib'] = implode(
837  "\n",
838  array_merge([$classinstance->error], $classinstance->errors)
839  );
840  $errorforthistable++;
841  $error++;
842  }
843  }
844  }
845  } else {
846  $this->errors[$error]['lib'] = 'Bad value of profile setup '.$tmpval.' for array_import_fieldshidden';
847  $this->errors[$error]['type'] = 'Import profile setup';
848  $error++;
849  }
850  }
851  }
852  //print 'listfields='.$listfields.'<br>listvalues='.$listvalues.'<br>';
853 
854  // If no error for this $alias/$tablename, we have a complete $listfields and $listvalues that are defined
855  // so we can try to make the insert or update now.
856  if (!$errorforthistable) {
857  //print "$alias/$tablename/$listfields/$listvalues<br>";
858  if (!empty($listfields)) {
859  $updatedone = false;
860  $insertdone = false;
861 
862  $is_table_category_link = false;
863  $fname = 'rowid';
864  if (strpos($tablename, '_categorie_') !== false) {
865  $is_table_category_link = true;
866  $fname='*';
867  }
868 
869  if (!empty($updatekeys)) {
870  // We do SELECT to get the rowid, if we already have the rowid, it's to be used below for related tables (extrafields)
871 
872  if (empty($lastinsertid)) { // No insert done yet for a parent table
873  $sqlSelect = "SELECT ".$fname." FROM ".$tablename;
874  $data = array_combine($listfields, $listvalues);
875  $where = array(); // filters to forge SQL request
876  $filters = array(); // filters to forge output error message
877  foreach ($updatekeys as $key) {
878  $col = $objimport->array_import_updatekeys[0][$key];
879  $key = preg_replace('/^.*\./i', '', $key);
880  if (isModEnabled("socialnetworks") && strpos($key, "socialnetworks") !== false) {
881  $tmp = explode("_", $key);
882  $key = $tmp[0];
883  $socialnetwork = $tmp[1];
884  $jsondata = $data[$key];
885  $json = json_decode($jsondata);
886  $stringtosearch = json_encode($socialnetwork).':'.json_encode($json->$socialnetwork);
887  //var_dump($stringtosearch);
888  //var_dump($this->db->escape($stringtosearch)); // This provide a value for sql string (but not for a like)
889  $where[] = $key." LIKE '%".$this->db->escape($this->db->escapeforlike($stringtosearch))."%'";
890  $filters[] = $col." LIKE '%".$this->db->escape($this->db->escapeforlike($stringtosearch))."%'";
891  //var_dump($where[1]); // This provide a value for sql string inside a like
892  } else {
893  $where[] = $key.' = '.$data[$key];
894  $filters[] = $col.' = '.$data[$key];
895  }
896  }
897  if (!empty($tablewithentity_cache[$tablename])) {
898  $where[] = "entity IN (".getEntity($this->getElementFromTableWithPrefix($tablename)).")";
899  $filters[] = "entity IN (".getEntity($this->getElementFromTableWithPrefix($tablename)).")";
900  }
901  $sqlSelect .= " WHERE ".implode(' AND ', $where);
902 
903  $resql = $this->db->query($sqlSelect);
904  if ($resql) {
905  $num_rows = $this->db->num_rows($resql);
906  if ($num_rows == 1) {
907  $res = $this->db->fetch_object($resql);
908  $lastinsertid = $res->rowid;
909  if ($is_table_category_link) $lastinsertid = 'linktable'; // used to apply update on tables like llx_categorie_product and avoid being blocked for all file content if at least one entry already exists
910  $last_insert_id_array[$tablename] = $lastinsertid;
911  } elseif ($num_rows > 1) {
912  $this->errors[$error]['lib'] = $langs->trans('MultipleRecordFoundWithTheseFilters', implode(', ', $filters));
913  $this->errors[$error]['type'] = 'SQL';
914  $error++;
915  } else {
916  // No record found with filters, insert will be tried below
917  }
918  } else {
919  //print 'E';
920  $this->errors[$error]['lib'] = $this->db->lasterror();
921  $this->errors[$error]['type'] = 'SQL';
922  $error++;
923  }
924  } else {
925  // We have a last INSERT ID (got by previous pass), so we check if we have a row referencing this foreign key.
926  // This is required when updating table with some extrafields. When inserting a record in parent table, we can make
927  // a direct insert into subtable extrafields, but when me wake an update, the insertid is defined and the child record
928  // may already exists. So we rescan the extrafield table to know if record exists or not for the rowid.
929  // Note: For extrafield tablename, we have in importfieldshidden_array an enty 'extra.fk_object'=>'lastrowid-tableparent' so $keyfield is 'fk_object'
930  $sqlSelect = "SELECT rowid FROM ".$tablename;
931 
932  if (empty($keyfield)) {
933  $keyfield = 'rowid';
934  }
935  $sqlSelect .= " WHERE ".$keyfield." = ".((int) $lastinsertid);
936 
937  if (!empty($tablewithentity_cache[$tablename])) {
938  $sqlSelect .= " AND entity IN (".getEntity($this->getElementFromTableWithPrefix($tablename)).")";
939  }
940 
941  $resql = $this->db->query($sqlSelect);
942  if ($resql) {
943  $res = $this->db->fetch_object($resql);
944  if ($this->db->num_rows($resql) == 1) {
945  // We have a row referencing this last foreign key, continue with UPDATE.
946  } else {
947  // No record found referencing this last foreign key,
948  // force $lastinsertid to 0 so we INSERT below.
949  $lastinsertid = 0;
950  }
951  } else {
952  //print 'E';
953  $this->errors[$error]['lib'] = $this->db->lasterror();
954  $this->errors[$error]['type'] = 'SQL';
955  $error++;
956  }
957  }
958 
959  if (!empty($lastinsertid)) {
960  // We db escape social network field because he isn't in field creation
961  if (in_array("socialnetworks", $listfields)) {
962  $socialkey = array_search("socialnetworks", $listfields);
963  $tmpsql = $listvalues[$socialkey];
964  $listvalues[$socialkey] = "'".$this->db->escape($tmpsql)."'";
965  }
966 
967  // Build SQL UPDATE request
968  $sqlstart = "UPDATE ".$tablename;
969 
970  $data = array_combine($listfields, $listvalues);
971  $set = array();
972  foreach ($data as $key => $val) {
973  $set[] = $key." = ".$val;
974  }
975  $sqlstart .= " SET ".implode(', ', $set);
976 
977  if (empty($keyfield)) {
978  $keyfield = 'rowid';
979  }
980  $sqlend = " WHERE ".$keyfield." = ".((int) $lastinsertid);
981 
982  if ($is_table_category_link) {
983  $sqlend = " WHERE " . implode(' AND ', $where);
984  }
985 
986  if (!empty($tablewithentity_cache[$tablename])) {
987  $sqlend .= " AND entity IN (".getEntity($this->getElementFromTableWithPrefix($tablename)).")";
988  }
989 
990  $sql = $sqlstart.$sqlend;
991 
992  // Run update request
993  $resql = $this->db->query($sql);
994  if ($resql) {
995  // No error, update has been done. $this->db->db->affected_rows can be 0 if data hasn't changed
996  $updatedone = true;
997  } else {
998  //print 'E';
999  $this->errors[$error]['lib'] = $this->db->lasterror();
1000  $this->errors[$error]['type'] = 'SQL';
1001  $error++;
1002  }
1003  }
1004  }
1005 
1006  // Update not done, we do insert
1007  if (!$error && !$updatedone) {
1008  // We db escape social network field because he isn't in field creation
1009  if (in_array("socialnetworks", $listfields)) {
1010  $socialkey = array_search("socialnetworks", $listfields);
1011  $tmpsql = $listvalues[$socialkey];
1012  $listvalues[$socialkey] = "'".$this->db->escape($tmpsql)."'";
1013  }
1014 
1015  // Build SQL INSERT request
1016  $sqlstart = "INSERT INTO ".$tablename."(".implode(", ", $listfields).", import_key";
1017  $sqlend = ") VALUES(".implode(', ', $listvalues).", '".$this->db->escape($importid)."'";
1018  if (!empty($tablewithentity_cache[$tablename])) {
1019  $sqlstart .= ", entity";
1020  $sqlend .= ", ".$conf->entity;
1021  }
1022  if (!empty($objimport->array_import_tables_creator[0][$alias])) {
1023  $sqlstart .= ", ".$objimport->array_import_tables_creator[0][$alias];
1024  $sqlend .= ", ".$user->id;
1025  }
1026  $sql = $sqlstart.$sqlend.")";
1027  //dol_syslog("import_csv.modules", LOG_DEBUG);
1028 
1029  // Run insert request
1030  if ($sql) {
1031  $resql = $this->db->query($sql);
1032  if ($resql) {
1033  if (!$is_table_category_link) {
1034  $last_insert_id_array[$tablename] = $this->db->last_insert_id($tablename); // store the last inserted auto_increment id for each table, so that child tables can be inserted with the appropriate id. This must be done just after the INSERT request, else we risk losing the id (because another sql query will be issued somewhere in Dolibarr).
1035  }
1036  $insertdone = true;
1037  } else {
1038  //print 'E';
1039  $this->errors[$error]['lib'] = $this->db->lasterror();
1040  $this->errors[$error]['type'] = 'SQL';
1041  $error++;
1042  }
1043  }
1044  }
1045  }
1046  /*else
1047  {
1048  dol_print_error('','ErrorFieldListEmptyFor '.$alias."/".$tablename);
1049  }*/
1050  }
1051 
1052  if ($error) {
1053  break;
1054  }
1055  }
1056 
1057  if ($updatedone) {
1058  $this->nbupdate++;
1059  }
1060  if ($insertdone) {
1061  $this->nbinsert++;
1062  }
1063  }
1064 
1065  return 1;
1066  }
1067 }
1068 
1075 function cleansep($value)
1076 {
1077  return str_replace(array(',', ';'), '/', $value);
1078 }
Class to import CSV files.
write_header_example($outputlangs)
Output header of an example file for this format.
import_get_nb_of_lines($file)
Return nb of records.
import_insert($arrayrecord, $array_match_file_to_database, $objimport, $maxfields, $importid, $updatekeys)
Insert a record into database.
__construct($db, $datatoimport)
Constructor.
import_read_record()
Return array of next record in input file.
write_record_example($outputlangs, $contentlinevalues)
Output record of an example file for this format.
write_footer_example($outputlangs)
Output footer of an example file for this format.
import_close_file()
Close file handle.
import_read_header()
Input header line from file.
write_title_example($outputlangs, $headerlinefields)
Output title line of an example file for this format.
import_open_file($file)
Open input file.
Parent class for import file readers.
getElementFromTableWithPrefix($tableNameWithPrefix)
Get element from table name with prefix.
Class to manage third parties objects (customers, suppliers, prospects...)
if(isModEnabled('facture') && $user->hasRight('facture', 'lire')) if((isModEnabled('fournisseur') &&empty($conf->global->MAIN_USE_NEW_SUPPLIERMOD) && $user->hasRight("fournisseur", "facture", "lire"))||(isModEnabled('supplier_invoice') && $user->hasRight("supplier_invoice", "lire"))) if(isModEnabled('don') && $user->hasRight('don', 'lire')) if(isModEnabled('tax') &&!empty($user->rights->tax->charges->lire)) if(isModEnabled('facture') &&isModEnabled('commande') && $user->hasRight("commande", "lire") &&empty($conf->global->WORKFLOW_DISABLE_CREATE_INVOICE_FROM_ORDER)) $sql
Social contributions to pay.
Definition: index.php:746
dol_stringtotime($string, $gm=1)
Convert a string date into a GM Timestamps date Warning: YYYY-MM-DDTHH:MM:SS+02:00 (RFC3339) is not s...
Definition: date.lib.php:409
dol_count_nb_of_line($file)
Count number of lines in a file.
Definition: files.lib.php:556
dol_osencode($str)
Return a string encoded into OS filesystem encoding.
price2num($amount, $rounding='', $option=0)
Function that return a number with universal decimal format (decimal separator is '.
dol_print_error($db='', $error='', $errors=null)
Displays error message system with all the information to facilitate the diagnosis and the escalation...
dol_strlen($string, $stringencoding='UTF-8')
Make a strlen call.
if(!function_exists('dol_getprefix')) dol_include_once($relpath, $classname='')
Make an include_once using default root and alternate root if it fails.
num2Alpha($n)
Return a numeric value into an Excel like column number.
GETPOST($paramname, $check='alphanohtml', $method=0, $filter=null, $options=null, $noreplace=0)
Return value of a param into GET or POST supervariable.
dol_trunc($string, $size=40, $trunc='right', $stringencoding='UTF-8', $nodot=0, $display=0)
Truncate a string to a particular length adding '…' if string larger than length.
isModEnabled($module)
Is Dolibarr module enabled.
utf8_check($str)
Check if a string is in UTF8.
dol_syslog($message, $level=LOG_INFO, $ident=0, $suffixinfilename='', $restricttologhandler='', $logcontext=null)
Write log message into outputs.
cleansep($value)
Clean a string from separator.
testSqlAndScriptInject($val, $type)
Security: WAF layer for SQL Injection and XSS Injection (scripts) protection (Filters on GET,...
Definition: main.inc.php:89