dolibarr  16.0.1
import_xlsx.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 use PhpOffice\PhpSpreadsheet\Reader\Xlsx;
29 use PhpOffice\PhpSpreadsheet\Spreadsheet;
30 use PhpOffice\PhpSpreadsheet\Style\Alignment;
31 
32 require_once DOL_DOCUMENT_ROOT . '/core/modules/import/modules_import.php';
33 
34 
39 {
43  public $db;
44 
45  public $datatoimport;
46 
50  public $error = '';
51 
55  public $errors = array();
56 
60  public $id;
61 
65  public $label;
66 
67  public $extension; // Extension of files imported by driver
68 
73  public $version = 'dolibarr';
74 
75  public $label_lib; // Label of external lib used by driver
76 
77  public $version_lib; // Version of external lib used by driver
78 
79  public $separator;
80 
81  public $file; // Path of file
82 
83  public $handle; // Handle fichier
84 
85  public $cacheconvert = array(); // Array to cache list of value found after a convertion
86 
87  public $cachefieldtable = array(); // Array to cache list of value found into fields@tables
88 
89  public $nbinsert = 0; // # of insert done during the import
90 
91  public $nbupdate = 0; // # of update done during the import
92 
93  public $workbook; // temporary import file
94 
95  public $record; // current record
96 
97  public $headers;
98 
99 
106  public function __construct($db, $datatoimport)
107  {
108  global $conf, $langs;
109  $this->db = $db;
110 
111  // this is used as an extension from the example file code, so we have to put xlsx here !!!
112  $this->id = 'xlsx'; // Same value as xxx in file name export_xxx.modules.php
113  $this->label = 'Excel 2007'; // Label of driver
114  $this->desc = $langs->trans("Excel2007FormatDesc");
115  $this->extension = 'xlsx'; // Extension for generated file by this driver
116  $this->picto = 'mime/xls'; // Picto (This is not used by the example file code as Mime type, too bad ...)
117  $this->version = '1.0'; // Driver version
118 
119  // If driver use an external library, put its name here
120  require_once DOL_DOCUMENT_ROOT.'/includes/phpoffice/phpspreadsheet/src/autoloader.php';
121  require_once DOL_DOCUMENT_ROOT.'/includes/Psr/autoloader.php';
122  require_once PHPEXCELNEW_PATH.'Spreadsheet.php';
123  $this->workbook = new Spreadsheet();
124 
125  // If driver use an external library, put its name here
126  if (!class_exists('ZipArchive')) { // For Excel2007
127  $langs->load("errors");
128  $this->error = $langs->trans('ErrorPHPNeedModule', 'zip');
129  return -1;
130  }
131  $this->label_lib = 'PhpSpreadSheet';
132  $this->version_lib = '1.8.0';
133 
134  $this->datatoimport = $datatoimport;
135  if (preg_match('/^societe_/', $datatoimport)) {
136  $this->thirpartyobject = new Societe($this->db);
137  }
138  }
139 
140 
141  // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
148  public function write_header_example($outputlangs)
149  {
150  // phpcs:enable
151  global $user, $conf, $langs, $file;
152  // create a temporary object, the final output will be generated in footer
153  $this->workbook->getProperties()->setCreator($user->getFullName($outputlangs) . ' - Dolibarr ' . DOL_VERSION);
154  $this->workbook->getProperties()->setTitle($outputlangs->trans("Import") . ' - ' . $file);
155  $this->workbook->getProperties()->setSubject($outputlangs->trans("Import") . ' - ' . $file);
156  $this->workbook->getProperties()->setDescription($outputlangs->trans("Import") . ' - ' . $file);
157 
158  $this->workbook->setActiveSheetIndex(0);
159  $this->workbook->getActiveSheet()->setTitle($outputlangs->trans("Sheet"));
160  $this->workbook->getActiveSheet()->getDefaultRowDimension()->setRowHeight(16);
161 
162  return '';
163  }
164 
165  // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
173  public function write_title_example($outputlangs, $headerlinefields)
174  {
175  // phpcs:enable
176  global $conf;
177  $this->workbook->getActiveSheet()->getStyle('1')->getFont()->setBold(true);
178  $this->workbook->getActiveSheet()->getStyle('1')->getAlignment()->setHorizontal(Alignment::HORIZONTAL_LEFT);
179 
180  $col = 1;
181  foreach ($headerlinefields as $field) {
182  $this->workbook->getActiveSheet()->SetCellValueByColumnAndRow($col, 1, $outputlangs->transnoentities($field));
183  // set autowidth
184  //$this->workbook->getActiveSheet()->getColumnDimension($this->column2Letter($col + 1))->setAutoSize(true);
185  $col++;
186  }
187 
188  return ''; // final output will be generated in footer
189  }
190 
191  // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
199  public function write_record_example($outputlangs, $contentlinevalues)
200  {
201  // phpcs:enable
202  $col = 1;
203  $row = 2;
204  foreach ($contentlinevalues as $cell) {
205  $this->workbook->getActiveSheet()->SetCellValueByColumnAndRow($col, $row, $cell);
206  $col++;
207  }
208 
209  return ''; // final output will be generated in footer
210  }
211 
212  // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
219  public function write_footer_example($outputlangs)
220  {
221  // phpcs:enable
222  // return the file content as a string
223  $tempfile = tempnam(sys_get_temp_dir(), 'dol');
224  $objWriter = new PhpOffice\PhpSpreadsheet\Writer\Xlsx($this->workbook);
225  $objWriter->save($tempfile);
226  $this->workbook->disconnectWorksheets();
227  unset($this->workbook);
228 
229  $content = file_get_contents($tempfile);
230  unlink($tempfile);
231  return $content;
232  }
233 
234 
235 
236  // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
243  public function import_open_file($file)
244  {
245  // phpcs:enable
246  global $langs;
247  $ret = 1;
248 
249  dol_syslog(get_class($this) . "::open_file file=" . $file);
250 
251  $reader = new Xlsx();
252  $this->workbook = $reader->load($file);
253  $this->record = 1;
254  $this->file = $file;
255 
256  return $ret;
257  }
258 
259 
260  // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
267  public function import_get_nb_of_lines($file)
268  {
269  // phpcs:enable
270  $reader = new Xlsx();
271  $this->workbook = $reader->load($file);
272 
273  $rowcount = $this->workbook->getActiveSheet()->getHighestDataRow();
274 
275  $this->workbook->disconnectWorksheets();
276  unset($this->workbook);
277 
278  return $rowcount;
279  }
280 
281 
282  // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
288  public function import_read_header()
289  {
290  // phpcs:enable
291  // This is not called by the import code !!!
292  $this->headers = array();
293  $xlsx = new Xlsx();
294  $info = $xlsx->listWorksheetinfo($this->file);
295  $countcolumns = $info[0]['totalColumns'];
296  for ($col = 1; $col <= $countcolumns; $col++) {
297  $this->headers[$col] = $this->workbook->getActiveSheet()->getCellByColumnAndRow($col, 1)->getValue();
298  }
299  return 0;
300  }
301 
302 
303  // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
309  public function import_read_record()
310  {
311  // phpcs:enable
312  global $conf;
313 
314  $rowcount = $this->workbook->getActiveSheet()->getHighestDataRow();
315  if ($this->record > $rowcount) {
316  return false;
317  }
318  $array = array();
319  $xlsx = new Xlsx();
320  $info = $xlsx->listWorksheetinfo($this->file);
321  $countcolumns = $info[0]['totalColumns'];
322  for ($col = 1; $col <= $countcolumns; $col++) {
323  $val = $this->workbook->getActiveSheet()->getCellByColumnAndRow($col, $this->record)->getValue();
324  $array[$col]['val'] = $val;
325  $array[$col]['type'] = (dol_strlen($val) ? 1 : -1); // If empty we consider it null
326  }
327  $this->record++;
328  return $array;
329  }
330 
331  // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
337  public function import_close_file()
338  {
339  // phpcs:enable
340  $this->workbook->disconnectWorksheets();
341  unset($this->workbook);
342  }
343 
344 
345  // What is this doing here ? it is common to all imports, is should be in the parent class
346  // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
358  public function import_insert($arrayrecord, $array_match_file_to_database, $objimport, $maxfields, $importid, $updatekeys)
359  {
360  // phpcs:enable
361  global $langs, $conf, $user;
362  global $thirdparty_static; // Specific to thirdparty import
363  global $tablewithentity_cache; // Cache to avoid to call desc at each rows on tables
364 
365  $error = 0;
366  $warning = 0;
367  $this->errors = array();
368  $this->warnings = array();
369 
370  //dol_syslog("import_csv.modules maxfields=".$maxfields." importid=".$importid);
371 
372  //var_dump($array_match_file_to_database);
373  //var_dump($arrayrecord); exit;
374 
375  $array_match_database_to_file = array_flip($array_match_file_to_database);
376  $sort_array_match_file_to_database = $array_match_file_to_database;
377  ksort($sort_array_match_file_to_database);
378 
379  //var_dump($sort_array_match_file_to_database);
380 
381  if (count($arrayrecord) == 0 || (count($arrayrecord) == 1 && empty($arrayrecord[1]['val']))) {
382  //print 'W';
383  $this->warnings[$warning]['lib'] = $langs->trans('EmptyLine');
384  $this->warnings[$warning]['type'] = 'EMPTY';
385  $warning++;
386  } else {
387  $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)
388  $updatedone = false;
389  $insertdone = false;
390  // For each table to insert, me make a separate insert
391  foreach ($objimport->array_import_tables[0] as $alias => $tablename) {
392  // Build sql request
393  $sql = '';
394  $listfields = array();
395  $listvalues = array();
396  $i = 0;
397  $errorforthistable = 0;
398 
399  // Define $tablewithentity_cache[$tablename] if not already defined
400  if (!isset($tablewithentity_cache[$tablename])) { // keep this test with "isset"
401  dol_syslog("Check if table " . $tablename . " has an entity field");
402  $resql = $this->db->DDLDescTable($tablename, 'entity');
403  if ($resql) {
404  $obj = $this->db->fetch_object($resql);
405  if ($obj) {
406  $tablewithentity_cache[$tablename] = 1; // table contains entity field
407  } else {
408  $tablewithentity_cache[$tablename] = 0; // table does not contains entity field
409  }
410  } else {
411  dol_print_error($this->db);
412  }
413  } else {
414  //dol_syslog("Table ".$tablename." check for entity into cache is ".$tablewithentity_cache[$tablename]);
415  }
416 
417  // Define array to convert fields ('c.ref', ...) into column index (1, ...)
418  $arrayfield = array();
419  foreach ($sort_array_match_file_to_database as $key => $val) {
420  $arrayfield[$val] = ($key);
421  }
422 
423  // $arrayrecord start at key 1
424  // $sort_array_match_file_to_database start at key 1
425 
426  // Loop on each fields in the match array: $key = 1..n, $val=alias of field (s.nom)
427  foreach ($sort_array_match_file_to_database as $key => $val) {
428  $fieldalias = preg_replace('/\..*$/i', '', $val);
429  $fieldname = preg_replace('/^.*\./i', '', $val);
430 
431  if ($alias != $fieldalias) {
432  continue; // Not a field of current table
433  }
434 
435  if ($key <= $maxfields) {
436  // Set $newval with value to insert and set $listvalues with sql request part for insert
437  $newval = '';
438  if ($arrayrecord[($key)]['type'] > 0) {
439  $newval = $arrayrecord[($key)]['val']; // If type of field into input file is not empty string (so defined into input file), we get value
440  }
441 
442  //var_dump($newval);var_dump($val);
443  //var_dump($objimport->array_import_convertvalue[0][$val]);
444 
445  // Make some tests on $newval
446 
447  // Is it a required field ?
448  if (preg_match('/\*/', $objimport->array_import_fields[0][$val]) && ((string) $newval == '')) {
449  $this->errors[$error]['lib'] = $langs->trans('ErrorMissingMandatoryValue', num2Alpha($key - 1));
450  $this->errors[$error]['type'] = 'NOTNULL';
451  $errorforthistable++;
452  $error++;
453  } else {
454  // Test format only if field is not a missing mandatory field (field may be a value or empty but not mandatory)
455  // We convert field if required
456  if (!empty($objimport->array_import_convertvalue[0][$val])) {
457  //print 'Must convert '.$newval.' with rule '.join(',',$objimport->array_import_convertvalue[0][$val]).'. ';
458  if ($objimport->array_import_convertvalue[0][$val]['rule'] == 'fetchidfromcodeid'
459  || $objimport->array_import_convertvalue[0][$val]['rule'] == 'fetchidfromref'
460  || $objimport->array_import_convertvalue[0][$val]['rule'] == 'fetchidfromcodeorlabel'
461  ) {
462  // 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.
463  $isidorref = 'id';
464  if (!is_numeric($newval) && $newval != '' && !preg_match('/^id:/i', $newval)) {
465  $isidorref = 'ref';
466  }
467  $newval = preg_replace('/^(id|ref):/i', '', $newval); // Remove id: or ref: that was used to force if field is id or ref
468  //print 'Newval is now "'.$newval.'" and is type '.$isidorref."<br>\n";
469 
470  if ($isidorref == 'ref') { // If value into input import file is a ref, we apply the function defined into descriptor
471  $file = (empty($objimport->array_import_convertvalue[0][$val]['classfile']) ? $objimport->array_import_convertvalue[0][$val]['file'] : $objimport->array_import_convertvalue[0][$val]['classfile']);
472  $class = $objimport->array_import_convertvalue[0][$val]['class'];
473  $method = $objimport->array_import_convertvalue[0][$val]['method'];
474  if ($this->cacheconvert[$file . '_' . $class . '_' . $method . '_'][$newval] != '') {
475  $newval = $this->cacheconvert[$file . '_' . $class . '_' . $method . '_'][$newval];
476  } else {
477  $resultload = dol_include_once($file);
478  if (empty($resultload)) {
479  dol_print_error('', 'Error trying to call file=' . $file . ', class=' . $class . ', method=' . $method);
480  break;
481  }
482  $classinstance = new $class($this->db);
483  if ($class == 'CGenericDic') {
484  $classinstance->element = $objimport->array_import_convertvalue[0][$val]['element'];
485  $classinstance->table_element = $objimport->array_import_convertvalue[0][$val]['table_element'];
486  }
487 
488  // Try the fetch from code or ref
489  $param_array = array('', $newval);
490  if ($class == 'AccountingAccount') {
491  //var_dump($arrayrecord[0]['val']);
492  /*include_once DOL_DOCUMENT_ROOT.'/accountancy/class/accountancysystem.class.php';
493  $tmpchartofaccount = new AccountancySystem($this->db);
494  $tmpchartofaccount->fetch($conf->global->CHARTOFACCOUNTS);
495  //var_dump($tmpchartofaccount->ref.' - '.$arrayrecord[0]['val']);
496  if ((! ($conf->global->CHARTOFACCOUNTS > 0)) || $tmpchartofaccount->ref != $arrayrecord[0]['val'])
497  {
498  $this->errors[$error]['lib']=$langs->trans('ErrorImportOfChartLimitedToCurrentChart', $tmpchartofaccount->ref);
499  $this->errors[$error]['type']='RESTRICTONCURRENCTCHART';
500  $errorforthistable++;
501  $error++;
502  }*/
503  $param_array = array('', $newval, 0, $arrayrecord[0]['val']); // Param to fetch parent from account, in chart.
504  }
505  call_user_func_array(array($classinstance, $method), $param_array);
506  // If not found, try the fetch from label
507  if (!($classinstance->id != '') && $objimport->array_import_convertvalue[0][$val]['rule'] == 'fetchidfromcodeorlabel') {
508  $param_array = array('', '', $newval);
509  call_user_func_array(array($classinstance, $method), $param_array);
510  }
511  $this->cacheconvert[$file . '_' . $class . '_' . $method . '_'][$newval] = $classinstance->id;
512  //print 'We have made a '.$class.'->'.$method.' to get id from code '.$newval.'. ';
513  if ($classinstance->id != '') { // id may be 0, it is a found value
514  $newval = $classinstance->id;
515  } else {
516  if (!empty($objimport->array_import_convertvalue[0][$val]['dict'])) {
517  $this->errors[$error]['lib'] = $langs->trans('ErrorFieldValueNotIn', $key, $newval, 'code', $langs->transnoentitiesnoconv($objimport->array_import_convertvalue[0][$val]['dict']));
518  } elseif (!empty($objimport->array_import_convertvalue[0][$val]['element'])) {
519  $this->errors[$error]['lib'] = $langs->trans('ErrorFieldRefNotIn', $key, $newval, $langs->transnoentitiesnoconv($objimport->array_import_convertvalue[0][$val]['element']));
520  } else {
521  $this->errors[$error]['lib'] = 'ErrorBadDefinitionOfImportProfile';
522  }
523  $this->errors[$error]['type'] = 'FOREIGNKEY';
524  $errorforthistable++;
525  $error++;
526  }
527  }
528  }
529  } elseif ($objimport->array_import_convertvalue[0][$val]['rule'] == 'fetchidfromcodeandlabel') {
530  $isidorref = 'id';
531  if (!is_numeric($newval) && $newval != '' && !preg_match('/^id:/i', $newval)) {
532  $isidorref = 'ref';
533  }
534  $newval = preg_replace('/^(id|ref):/i', '', $newval);
535 
536  if ($isidorref == 'ref') {
537  $file = (empty($objimport->array_import_convertvalue[0][$val]['classfile']) ? $objimport->array_import_convertvalue[0][$val]['file'] : $objimport->array_import_convertvalue[0][$val]['classfile']);
538  $class = $objimport->array_import_convertvalue[0][$val]['class'];
539  $method = $objimport->array_import_convertvalue[0][$val]['method'];
540  $codefromfield = $objimport->array_import_convertvalue[0][$val]['codefromfield'];
541  $code = $arrayrecord[$arrayfield[$codefromfield]]['val'];
542  if ($this->cacheconvert[$file . '_' . $class . '_' . $method . '_' . $code][$newval] != '') {
543  $newval = $this->cacheconvert[$file . '_' . $class . '_' . $method . '_' . $code][$newval];
544  } else {
545  $resultload = dol_include_once($file);
546  if (empty($resultload)) {
547  dol_print_error('', 'Error trying to call file=' . $file . ', class=' . $class . ', method=' . $method . ', code=' . $code);
548  break;
549  }
550  $classinstance = new $class($this->db);
551  // Try the fetch from code and ref
552  $param_array = array('', $newval, $code);
553  call_user_func_array(array($classinstance, $method), $param_array);
554  $this->cacheconvert[$file . '_' . $class . '_' . $method . '_' . $code][$newval] = $classinstance->id;
555  if ($classinstance->id > 0) { // we found record
556  $newval = $classinstance->id;
557  } else {
558  if (!empty($objimport->array_import_convertvalue[0][$val]['dict'])) {
559  $this->errors[$error]['lib'] = $langs->trans('ErrorFieldValueNotIn', $key, $newval, 'scale', $langs->transnoentitiesnoconv($objimport->array_import_convertvalue[0][$val]['dict']));
560  } else {
561  $this->errors[$error]['lib'] = 'ErrorFieldValueNotIn';
562  }
563  $this->errors[$error]['type'] = 'FOREIGNKEY';
564  $errorforthistable++;
565  $error++;
566  }
567  }
568  }
569  } elseif ($objimport->array_import_convertvalue[0][$val]['rule'] == 'zeroifnull') {
570  if (empty($newval)) {
571  $newval = '0';
572  }
573  } elseif ($objimport->array_import_convertvalue[0][$val]['rule'] == 'fetchidfromcodeunits' || $objimport->array_import_convertvalue[0][$val]['rule'] == 'fetchscalefromcodeunits') {
574  $file = (empty($objimport->array_import_convertvalue[0][$val]['classfile']) ? $objimport->array_import_convertvalue[0][$val]['file'] : $objimport->array_import_convertvalue[0][$val]['classfile']);
575  $class = $objimport->array_import_convertvalue[0][$val]['class'];
576  $method = $objimport->array_import_convertvalue[0][$val]['method'];
577  $units = $objimport->array_import_convertvalue[0][$val]['units'];
578  if ($this->cacheconvert[$file . '_' . $class . '_' . $method . '_' . $units][$newval] != '') {
579  $newval = $this->cacheconvert[$file . '_' . $class . '_' . $method . '_' . $units][$newval];
580  } else {
581  $resultload = dol_include_once($file);
582  if (empty($resultload)) {
583  dol_print_error('', 'Error trying to call file=' . $file . ', class=' . $class . ', method=' . $method . ', units=' . $units);
584  break;
585  }
586  $classinstance = new $class($this->db);
587  // Try the fetch from code or ref
588  call_user_func_array(array($classinstance, $method), array('', '', $newval, $units));
589  $scaleorid = (($objimport->array_import_convertvalue[0][$val]['rule'] == 'fetchidfromcodeunits') ? $classinstance->id : $classinstance->scale);
590  $this->cacheconvert[$file . '_' . $class . '_' . $method . '_' . $units][$newval] = $scaleorid;
591  //print 'We have made a '.$class.'->'.$method." to get a value from key '".$newval."' and we got '".$scaleorid."'.";exit;
592  if ($classinstance->id > 0) { // we found record
593  $newval = $scaleorid ? $scaleorid : 0;
594  } else {
595  if (!empty($objimport->array_import_convertvalue[0][$val]['dict'])) {
596  $this->errors[$error]['lib'] = $langs->trans('ErrorFieldValueNotIn', $key, $newval, 'scale', $langs->transnoentitiesnoconv($objimport->array_import_convertvalue[0][$val]['dict']));
597  } else {
598  $this->errors[$error]['lib'] = 'ErrorFieldValueNotIn';
599  }
600  $this->errors[$error]['type'] = 'FOREIGNKEY';
601  $errorforthistable++;
602  $error++;
603  }
604  }
605  } elseif ($objimport->array_import_convertvalue[0][$val]['rule'] == 'getcustomercodeifauto') {
606  if (strtolower($newval) == 'auto') {
607  $this->thirpartyobject->get_codeclient(0, 0);
608  $newval = $this->thirpartyobject->code_client;
609  //print 'code_client='.$newval;
610  }
611  if (empty($newval)) {
612  $arrayrecord[($key)]['type'] = -1; // If we get empty value, we will use "null"
613  }
614  } elseif ($objimport->array_import_convertvalue[0][$val]['rule'] == 'getsuppliercodeifauto') {
615  if (strtolower($newval) == 'auto') {
616  $newval = $this->thirpartyobject->get_codefournisseur(0, 1);
617  $newval = $this->thirpartyobject->code_fournisseur;
618  //print 'code_fournisseur='.$newval;
619  }
620  if (empty($newval)) {
621  $arrayrecord[($key)]['type'] = -1; // If we get empty value, we will use "null"
622  }
623  } elseif ($objimport->array_import_convertvalue[0][$val]['rule'] == 'getcustomeraccountancycodeifauto') {
624  if (strtolower($newval) == 'auto') {
625  $this->thirpartyobject->get_codecompta('customer');
626  $newval = $this->thirpartyobject->code_compta;
627  //print 'code_compta='.$newval;
628  }
629  if (empty($newval)) {
630  $arrayrecord[($key)]['type'] = -1; // If we get empty value, we will use "null"
631  }
632  } elseif ($objimport->array_import_convertvalue[0][$val]['rule'] == 'getsupplieraccountancycodeifauto') {
633  if (strtolower($newval) == 'auto') {
634  $this->thirpartyobject->get_codecompta('supplier');
635  $newval = $this->thirpartyobject->code_compta_fournisseur;
636  if (empty($newval)) {
637  $arrayrecord[($key)]['type'] = -1; // If we get empty value, we will use "null"
638  }
639  //print 'code_compta_fournisseur='.$newval;
640  }
641  if (empty($newval)) {
642  $arrayrecord[($key)]['type'] = -1; // If we get empty value, we will use "null"
643  }
644  } elseif ($objimport->array_import_convertvalue[0][$val]['rule'] == 'getrefifauto') {
645  if (strtolower($newval) == 'auto') {
646  $defaultref = '';
647 
648  $classModForNumber = $objimport->array_import_convertvalue[0][$val]['class'];
649  $pathModForNumber = $objimport->array_import_convertvalue[0][$val]['path'];
650 
651  if (!empty($classModForNumber) && !empty($pathModForNumber) && is_readable(DOL_DOCUMENT_ROOT.$pathModForNumber)) {
652  require_once DOL_DOCUMENT_ROOT.$pathModForNumber;
653  $modForNumber = new $classModForNumber;
654 
655  $tmpobject = null;
656  // Set the object with the date property when we can
657  if (!empty($objimport->array_import_convertvalue[0][$val]['classobject'])) {
658  $pathForObject = $objimport->array_import_convertvalue[0][$val]['pathobject'];
659  require_once DOL_DOCUMENT_ROOT.$pathForObject;
660  $tmpclassobject = $objimport->array_import_convertvalue[0][$val]['classobject'];
661  $tmpobject = new $tmpclassobject($this->db);
662  foreach ($arrayfield as $tmpkey => $tmpval) { // $arrayfield is array('c.ref'=>1, ...)
663  if (in_array($tmpkey, array('t.date', 'c.date_commande'))) {
664  $tmpobject->date = dol_stringtotime($arrayrecord[$arrayfield[$tmpkey]]['val'], 1);
665  }
666  }
667  }
668 
669  $defaultref = $modForNumber->getNextValue(null, $tmpobject);
670  }
671  if (is_numeric($defaultref) && $defaultref <= 0) { // If error
672  $defaultref = '';
673  }
674  $newval = $defaultref;
675  }
676  } elseif ($objimport->array_import_convertvalue[0][$val]['rule'] == 'compute') {
677  $file = (empty($objimport->array_import_convertvalue[0][$val]['classfile']) ? $objimport->array_import_convertvalue[0][$val]['file'] : $objimport->array_import_convertvalue[0][$val]['classfile']);
678  $class = $objimport->array_import_convertvalue[0][$val]['class'];
679  $method = $objimport->array_import_convertvalue[0][$val]['method'];
680  $resultload = dol_include_once($file);
681  if (empty($resultload)) {
682  dol_print_error('', 'Error trying to call file=' . $file . ', class=' . $class . ', method=' . $method);
683  break;
684  }
685  $classinstance = new $class($this->db);
686  $res = call_user_func_array(array($classinstance, $method), array(&$arrayrecord, $listfields, $key));
687  $newval = $res; // We get new value computed.
688  } elseif ($objimport->array_import_convertvalue[0][$val]['rule'] == 'numeric') {
689  $newval = price2num($newval);
690  } elseif ($objimport->array_import_convertvalue[0][$val]['rule'] == 'accountingaccount') {
691  if (empty($conf->global->ACCOUNTING_MANAGE_ZERO)) {
692  $newval = rtrim(trim($newval), "0");
693  } else {
694  $newval = trim($newval);
695  }
696  }
697 
698  //print 'Val to use as insert is '.$newval.'<br>';
699  }
700 
701  // Test regexp
702  if (!empty($objimport->array_import_regex[0][$val]) && ($newval != '')) {
703  // If test is "Must exist in a field@table or field@table:..."
704  $reg = array();
705  if (preg_match('/^(.+)@([^:]+)(:.+)?$/', $objimport->array_import_regex[0][$val], $reg)) {
706  $field = $reg[1];
707  $table = $reg[2];
708  $filter = !empty($reg[3]) ? substr($reg[3], 1) : '';
709 
710  $cachekey = $field . '@' . $table;
711  if (!empty($filter)) {
712  $cachekey .= ':' . $filter;
713  }
714 
715  // Load content of field@table into cache array
716  if (!is_array($this->cachefieldtable[$cachekey])) { // If content of field@table not already loaded into cache
717  $sql = "SELECT " . $field . " as aliasfield FROM " . $table;
718  if (!empty($filter)) {
719  $sql .= ' WHERE ' . $filter;
720  }
721 
722  $resql = $this->db->query($sql);
723  if ($resql) {
724  $num = $this->db->num_rows($resql);
725  $i = 0;
726  while ($i < $num) {
727  $obj = $this->db->fetch_object($resql);
728  if ($obj) {
729  $this->cachefieldtable[$cachekey][] = $obj->aliasfield;
730  }
731  $i++;
732  }
733  } else {
734  dol_print_error($this->db);
735  }
736  }
737 
738  // Now we check cache is not empty (should not) and key is into cache
739  if (!is_array($this->cachefieldtable[$cachekey]) || !in_array($newval, $this->cachefieldtable[$cachekey])) {
740  $tableforerror = $table;
741  if (!empty($filter)) {
742  $tableforerror .= ':' . $filter;
743  }
744  $this->errors[$error]['lib'] = $langs->transnoentitiesnoconv('ErrorFieldValueNotIn', $key, $newval, $field, $tableforerror);
745  $this->errors[$error]['type'] = 'FOREIGNKEY';
746  $errorforthistable++;
747  $error++;
748  }
749  } elseif (!preg_match('/' . $objimport->array_import_regex[0][$val] . '/i', $newval)) {
750  // If test is just a static regex
751  //if ($key == 19) print "xxx".$newval."zzz".$objimport->array_import_regex[0][$val]."<br>";
752  $this->errors[$error]['lib'] = $langs->transnoentitiesnoconv('ErrorWrongValueForField', $key, $newval, $objimport->array_import_regex[0][$val]);
753  $this->errors[$error]['type'] = 'REGEX';
754  $errorforthistable++;
755  $error++;
756  }
757  }
758 
759  // Check HTML injection
760  $inj = testSqlAndScriptInject($newval, 0);
761  if ($inj) {
762  $this->errors[$error]['lib'] = $langs->transnoentitiesnoconv('ErrorHtmlInjectionForField', $key, dol_trunc($newval, 100));
763  $this->errors[$error]['type'] = 'HTMLINJECTION';
764  $errorforthistable++;
765  $error++;
766  }
767 
768  // Other tests
769  // ...
770  }
771 
772  // Define $listfields and $listvalues to build SQL request
773  if ($conf->socialnetworks->enabled && strpos($fieldname, "socialnetworks") !== false) {
774  if (!in_array("socialnetworks", $listfields)) {
775  $listfields[] = "socialnetworks";
776  }
777  if (!empty($newval) && $arrayrecord[($key)]['type'] > 0) {
778  $socialkey = array_search("socialnetworks", $listfields);
779  $socialnetwork = explode("_", $fieldname)[1];
780  if (empty($listvalues[$socialkey]) || $listvalues[$socialkey] == "null") {
781  $json = new stdClass();
782  $json->$socialnetwork = $newval;
783  $listvalues[$socialkey] = json_encode($json);
784  } else {
785  $jsondata = $listvalues[$socialkey];
786  $json = json_decode($jsondata);
787  $json->$socialnetwork = $newval;
788  $listvalues[$socialkey] = json_encode($json);
789  }
790  }
791  } else {
792  $listfields[] = $fieldname;
793 
794  // Note: arrayrecord (and 'type') is filled with ->import_read_record called by import.php page before calling import_insert
795  if (empty($newval) && $arrayrecord[($key)]['type'] < 0) {
796  $listvalues[] = ($newval == '0' ? $newval : "null");
797  } elseif (empty($newval) && $arrayrecord[($key)]['type'] == 0) {
798  $listvalues[] = "''";
799  } else {
800  $listvalues[] = "'" . $this->db->escape($newval) . "'";
801  }
802  }
803  }
804  $i++;
805  }
806 
807  // We add hidden fields (but only if there is at least one field to add into table)
808  // We process here all the fields that were declared into the array $this->import_fieldshidden_array of the descriptor file.
809  // Previously we processed the ->import_fields_array.
810  if (!empty($listfields) && is_array($objimport->array_import_fieldshidden[0])) {
811  // Loop on each hidden fields to add them into listfields/listvalues
812  foreach ($objimport->array_import_fieldshidden[0] as $key => $val) {
813  if (!preg_match('/^' . preg_quote($alias, '/') . '\./', $key)) {
814  continue; // Not a field of current table
815  }
816  if ($val == 'user->id') {
817  $listfields[] = preg_replace('/^' . preg_quote($alias, '/') . '\./', '', $key);
818  $listvalues[] = ((int) $user->id);
819  } elseif (preg_match('/^lastrowid-/', $val)) {
820  $tmp = explode('-', $val);
821  $lastinsertid = (isset($last_insert_id_array[$tmp[1]])) ? $last_insert_id_array[$tmp[1]] : 0;
822  $keyfield = preg_replace('/^' . preg_quote($alias, '/') . '\./', '', $key);
823  $listfields[] = $keyfield;
824  $listvalues[] = $lastinsertid;
825  //print $key."-".$val."-".$listfields."-".$listvalues."<br>";exit;
826  } elseif (preg_match('/^const-/', $val)) {
827  $tmp = explode('-', $val, 2);
828  $listfields[] = preg_replace('/^' . preg_quote($alias, '/') . '\./', '', $key);
829  $listvalues[] = "'".$this->db->escape($tmp[1])."'";
830  } elseif (preg_match('/^rule-/', $val)) {
831  $fieldname = $key;
832  if (!empty($objimport->array_import_convertvalue[0][$fieldname])) {
833  if ($objimport->array_import_convertvalue[0][$fieldname]['rule'] == 'compute') {
834  $file = (empty($objimport->array_import_convertvalue[0][$fieldname]['classfile']) ? $objimport->array_import_convertvalue[0][$fieldname]['file'] : $objimport->array_import_convertvalue[0][$fieldname]['classfile']);
835  $class = $objimport->array_import_convertvalue[0][$fieldname]['class'];
836  $method = $objimport->array_import_convertvalue[0][$fieldname]['method'];
837  $resultload = dol_include_once($file);
838  if (empty($resultload)) {
839  dol_print_error('', 'Error trying to call file=' . $file . ', class=' . $class . ', method=' . $method);
840  break;
841  }
842  $classinstance = new $class($this->db);
843  $res = call_user_func_array(array($classinstance, $method), array(&$arrayrecord, $listfields, $key));
844  $fieldArr = explode('.', $fieldname);
845  if (count($fieldArr) > 0) {
846  $fieldname = $fieldArr[1];
847  }
848  $listfields[] = $fieldname;
849  $listvalues[] = $res;
850  }
851  }
852  } else {
853  $this->errors[$error]['lib'] = 'Bad value of profile setup ' . $val . ' for array_import_fieldshidden';
854  $this->errors[$error]['type'] = 'Import profile setup';
855  $error++;
856  }
857  }
858  }
859  //print 'listfields='.$listfields.'<br>listvalues='.$listvalues.'<br>';
860 
861  // If no error for this $alias/$tablename, we have a complete $listfields and $listvalues that are defined
862  // so we can try to make the insert or update now.
863  if (!$errorforthistable) {
864  //print "$alias/$tablename/$listfields/$listvalues<br>";
865  if (!empty($listfields)) {
866  $updatedone = false;
867  $insertdone = false;
868 
869  $is_table_category_link = false;
870  $fname = 'rowid';
871  if (strpos($tablename, '_categorie_') !== false) {
872  $is_table_category_link = true;
873  $fname='*';
874  }
875 
876  if (!empty($updatekeys)) {
877  // We do SELECT to get the rowid, if we already have the rowid, it's to be used below for related tables (extrafields)
878 
879  if (empty($lastinsertid)) { // No insert done yet for a parent table
880  $sqlSelect = "SELECT ".$fname." FROM " . $tablename;
881 
882  $data = array_combine($listfields, $listvalues);
883  $where = array();
884  $filters = array();
885  foreach ($updatekeys as $key) {
886  $col = $objimport->array_import_updatekeys[0][$key];
887  $key = preg_replace('/^.*\./i', '', $key);
888  if ($conf->socialnetworks->enabled && strpos($key, "socialnetworks") !== false) {
889  $tmp = explode("_", $key);
890  $key = $tmp[0];
891  $socialnetwork = $tmp[1];
892  $jsondata = $data[$key];
893  $json = json_decode($jsondata);
894  $stringtosearch = json_encode($socialnetwork).':'.json_encode($json->$socialnetwork);
895  //var_dump($stringtosearch);
896  //var_dump($this->db->escape($stringtosearch)); // This provide a value for sql string (but not for a like)
897  $where[] = $key." LIKE '%".$this->db->escapeforlike($this->db->escape($stringtosearch))."%'";
898  $filters[] = $col." LIKE '%".$this->db->escapeforlike($this->db->escape($stringtosearch))."%'";
899  //var_dump($where[1]); // This provide a value for sql string inside a like
900  } else {
901  $where[] = $key.' = '.$data[$key];
902  $filters[] = $col.' = '.$data[$key];
903  }
904  }
905  $sqlSelect .= " WHERE " . implode(' AND ', $where);
906 
907  $resql = $this->db->query($sqlSelect);
908  if ($resql) {
909  $num_rows = $this->db->num_rows($resql);
910  if ($num_rows == 1) {
911  $res = $this->db->fetch_object($resql);
912  $lastinsertid = $res->rowid;
913  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
914  $last_insert_id_array[$tablename] = $lastinsertid;
915  } elseif ($num_rows > 1) {
916  $this->errors[$error]['lib'] = $langs->trans('MultipleRecordFoundWithTheseFilters', implode(', ', $filters));
917  $this->errors[$error]['type'] = 'SQL';
918  $error++;
919  } else {
920  // No record found with filters, insert will be tried below
921  }
922  } else {
923  //print 'E';
924  $this->errors[$error]['lib'] = $this->db->lasterror();
925  $this->errors[$error]['type'] = 'SQL';
926  $error++;
927  }
928  } else {
929  // We have a last INSERT ID (got by previous pass), so we check if we have a row referencing this foreign key.
930  // This is required when updating table with some extrafields. When inserting a record in parent table, we can make
931  // a direct insert into subtable extrafields, but when me wake an update, the insertid is defined and the child record
932  // may already exists. So we rescan the extrafield table to know if record exists or not for the rowid.
933  // Note: For extrafield tablename, we have in importfieldshidden_array an enty 'extra.fk_object'=>'lastrowid-tableparent' so $keyfield is 'fk_object'
934  $sqlSelect = "SELECT rowid FROM " . $tablename;
935 
936  if (empty($keyfield)) {
937  $keyfield = 'rowid';
938  }
939  $sqlSelect .= " WHERE ".$keyfield." = ".((int) $lastinsertid);
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  $sql = $sqlstart . $sqlend;
987 
988  // Run update request
989  $resql = $this->db->query($sql);
990  if ($resql) {
991  // No error, update has been done. $this->db->db->affected_rows can be 0 if data hasn't changed
992  $updatedone = true;
993  } else {
994  //print 'E';
995  $this->errors[$error]['lib'] = $this->db->lasterror();
996  $this->errors[$error]['type'] = 'SQL';
997  $error++;
998  }
999  }
1000  }
1001 
1002  // Update not done, we do insert
1003  if (!$error && !$updatedone) {
1004  // We db escape social network field because he isn't in field creation
1005  if (in_array("socialnetworks", $listfields)) {
1006  $socialkey = array_search("socialnetworks", $listfields);
1007  $tmpsql = $listvalues[$socialkey];
1008  $listvalues[$socialkey] = "'".$this->db->escape($tmpsql)."'";
1009  }
1010 
1011  // Build SQL INSERT request
1012  $sqlstart = "INSERT INTO " . $tablename . "(" . implode(", ", $listfields) . ", import_key";
1013  $sqlend = ") VALUES(" . implode(', ', $listvalues) . ", '" . $this->db->escape($importid) . "'";
1014  if (!empty($tablewithentity_cache[$tablename])) {
1015  $sqlstart .= ", entity";
1016  $sqlend .= ", " . $conf->entity;
1017  }
1018  if (!empty($objimport->array_import_tables_creator[0][$alias])) {
1019  $sqlstart .= ", " . $objimport->array_import_tables_creator[0][$alias];
1020  $sqlend .= ", " . $user->id;
1021  }
1022  $sql = $sqlstart . $sqlend . ")";
1023  //dol_syslog("import_xlsx.modules", LOG_DEBUG);
1024 
1025  // Run insert request
1026  if ($sql) {
1027  $resql = $this->db->query($sql);
1028  if ($resql) {
1029  $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).
1030  $insertdone = true;
1031  } else {
1032  //print 'E';
1033  $this->errors[$error]['lib'] = $this->db->lasterror();
1034  $this->errors[$error]['type'] = 'SQL';
1035  $error++;
1036  }
1037  }
1038  }
1039  }
1040  /*else
1041  {
1042  dol_print_error('','ErrorFieldListEmptyFor '.$alias."/".$tablename);
1043  }*/
1044  }
1045 
1046  if ($error) {
1047  break;
1048  }
1049  }
1050 
1051  if ($updatedone) {
1052  $this->nbupdate++;
1053  }
1054  if ($insertdone) {
1055  $this->nbinsert++;
1056  }
1057  }
1058 
1059  return 1;
1060  }
1061 }
if(!function_exists('dol_getprefix')) dol_include_once($relpath, $classname= '')
Make an include_once using default root and alternate root if it fails.
import_open_file($file)
Open input file.
$conf db
API class for accounts.
Definition: inc.php:41
import_read_header()
Input header line from file.
Class to import Excel files.
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:383
num2Alpha($n)
Return a numeric value into an Excel like column number.
write_footer_example($outputlangs)
Output footer of an example file for this format.
import_get_nb_of_lines($file)
Return nb of records.
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.
Class to manage third parties objects (customers, suppliers, prospects...)
dol_strlen($string, $stringencoding= 'UTF-8')
Make a strlen call.
price2num($amount, $rounding= '', $option=0)
Function that return a number with universal decimal format (decimal separator is &#39;...
__construct($db, $datatoimport)
Constructor.
dol_syslog($message, $level=LOG_INFO, $ident=0, $suffixinfilename= '', $restricttologhandler= '', $logcontext=null)
Write log message into outputs.
import_insert($arrayrecord, $array_match_file_to_database, $objimport, $maxfields, $importid, $updatekeys)
Insert a record into database.
if(isModEnabled('facture')&&!empty($user->rights->facture->lire)) if((isModEnabled('fournisseur')&&empty($conf->global->MAIN_USE_NEW_SUPPLIERMOD)&&$user->rights->fournisseur->facture->lire)||(isModEnabled('supplier_invoice')&&$user->rights->supplier_invoice->lire)) if(isModEnabled('don')&&!empty($user->rights->don->lire)) if(isModEnabled('tax')&&!empty($user->rights->tax->charges->lire)) if(isModEnabled('facture')&&isModEnabled('commande')&&$user->rights->commande->lire &&empty($conf->global->WORKFLOW_DISABLE_CREATE_INVOICE_FROM_ORDER)) $resql
Social contributions to pay.
Definition: index.php:742
write_header_example($outputlangs)
Output header of an example file for this format.
write_title_example($outputlangs, $headerlinefields)
Output title line of an example file for this format.
Parent class for import file readers.
testSqlAndScriptInject($val, $type)
Security: WAF layer for SQL Injection and XSS Injection (scripts) protection (Filters on GET...
Definition: main.inc.php:87
dol_print_error($db= '', $error= '', $errors=null)
Displays error message system with all the information to facilitate the diagnosis and the escalation...
dol_trunc($string, $size=40, $trunc= 'right', $stringencoding= 'UTF-8', $nodot=0, $display=0)
Truncate a string to a particular length adding &#39;…&#39; if string larger than length. ...
import_close_file()
Close file handle.