dolibarr  13.0.2
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 $workbook; // temporary import file
90 
91  public $record; // current record
92 
93  public $headers;
94 
95 
102  public function __construct($db, $datatoimport)
103  {
104  global $conf, $langs;
105  $this->db = $db;
106 
107  // this is used as an extension from the example file code, so we have to put xlsx here !!!
108  $this->id = 'xlsx'; // Same value as xxx in file name export_xxx.modules.php
109  $this->label = 'Excel 2007'; // Label of driver
110  $this->desc = $langs->trans("Excel2007FormatDesc");
111  $this->extension = 'xlsx'; // Extension for generated file by this driver
112  $this->picto = 'mime/xls'; // Picto (This is not used by the example file code as Mime type, too bad ...)
113  $this->version = '1.0'; // Driver version
114  // If driver use an external library, put its name here
115  require_once DOL_DOCUMENT_ROOT . '/includes/phpoffice/autoloader.php';
116  require_once DOL_DOCUMENT_ROOT . '/includes/Psr/autoloader.php';
117  require_once PHPEXCELNEW_PATH . 'Spreadsheet.php';
118  $this->workbook = new Spreadsheet();
119 
120  //if ($this->id == 'excel2007new')
121  {
122  if (!class_exists('ZipArchive')) // For Excel2007
123  {
124  $langs->load("errors");
125  $this->error = $langs->trans('ErrorPHPNeedModule', 'zip');
126  return -1;
127  }
128  }
129  $this->label_lib = 'PhpSpreadSheet';
130  $this->version_lib = '1.8.0';
131 
132  $this->datatoimport = $datatoimport;
133  if (preg_match('/^societe_/', $datatoimport)) $this->thirpartyobject = new Societe($this->db);
134  }
135 
136 
137  // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
144  public function write_header_example($outputlangs)
145  {
146  // phpcs:enable
147  global $user, $conf, $langs, $file;
148  // create a temporary object, the final output will be generated in footer
149  $this->workbook->getProperties()->setCreator($user->getFullName($outputlangs) . ' - Dolibarr ' . DOL_VERSION);
150  $this->workbook->getProperties()->setTitle($outputlangs->trans("Import") . ' - ' . $file);
151  $this->workbook->getProperties()->setSubject($outputlangs->trans("Import") . ' - ' . $file);
152  $this->workbook->getProperties()->setDescription($outputlangs->trans("Import") . ' - ' . $file);
153 
154  $this->workbook->setActiveSheetIndex(0);
155  $this->workbook->getActiveSheet()->setTitle($outputlangs->trans("Sheet"));
156  $this->workbook->getActiveSheet()->getDefaultRowDimension()->setRowHeight(16);
157 
158  return '';
159  }
160 
161  // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
169  public function write_title_example($outputlangs, $headerlinefields)
170  {
171  // phpcs:enable
172  global $conf;
173  $this->workbook->getActiveSheet()->getStyle('1')->getFont()->setBold(true);
174  $this->workbook->getActiveSheet()->getStyle('1')->getAlignment()->setHorizontal(Alignment::HORIZONTAL_LEFT);
175 
176  $col = 1;
177  foreach ($headerlinefields as $field) {
178  $this->workbook->getActiveSheet()->SetCellValueByColumnAndRow($col, 1, $outputlangs->transnoentities($field));
179  // set autowidth
180  //$this->workbook->getActiveSheet()->getColumnDimension($this->column2Letter($col + 1))->setAutoSize(true);
181  $col++;
182  }
183 
184  return ''; // final output will be generated in footer
185  }
186 
187  // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
195  public function write_record_example($outputlangs, $contentlinevalues)
196  {
197  // phpcs:enable
198  $col = 1;
199  $row = 2;
200  foreach ($contentlinevalues as $cell) {
201  $this->workbook->getActiveSheet()->SetCellValueByColumnAndRow($col, $row, $cell);
202  $col++;
203  }
204 
205  return ''; // final output will be generated in footer
206  }
207 
208  // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
215  public function write_footer_example($outputlangs)
216  {
217  // phpcs:enable
218  // return the file content as a string
219  $tempfile = tempnam(sys_get_temp_dir(), 'dol');
220  $objWriter = new PhpOffice\PhpSpreadsheet\Writer\Xlsx($this->workbook);
221  $objWriter->save($tempfile);
222  $this->workbook->disconnectWorksheets();
223  unset($this->workbook);
224 
225  $content = file_get_contents($tempfile);
226  unlink($tempfile);
227  return $content;
228  }
229 
230 
231 
232  // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
239  public function import_open_file($file)
240  {
241  // phpcs:enable
242  global $langs;
243  $ret = 1;
244 
245  dol_syslog(get_class($this) . "::open_file file=" . $file);
246 
247  $reader = new Xlsx();
248  $this->workbook = $reader->load($file);
249  $this->record = 1;
250  $this->file = $file;
251 
252  return $ret;
253  }
254 
255 
256  // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
263  public function import_get_nb_of_lines($file)
264  {
265  // phpcs:enable
266  $reader = new Xlsx();
267  $this->workbook = $reader->load($file);
268 
269  $rowcount = $this->workbook->getActiveSheet()->getHighestDataRow();
270 
271  $this->workbook->disconnectWorksheets();
272  unset($this->workbook);
273 
274  return $rowcount;
275  }
276 
277 
278  // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
284  public function import_read_header()
285  {
286  // phpcs:enable
287  // This is not called by the import code !!!
288  $this->headers = array();
289  $xlsx = new Xlsx();
290  $info = $xlsx->listWorksheetinfo($this->file);
291  $countcolumns = $info[0]['totalColumns'];
292  for ($col = 1; $col <= $countcolumns; $col++) {
293  $this->headers[$col] = $this->workbook->getActiveSheet()->getCellByColumnAndRow($col, 1)->getValue();
294  }
295  return 0;
296  }
297 
298 
299  // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
305  public function import_read_record()
306  {
307  // phpcs:enable
308  global $conf;
309 
310  $rowcount = $this->workbook->getActiveSheet()->getHighestDataRow();
311  if ($this->record > $rowcount)
312  return false;
313  $array = array();
314  $xlsx = new Xlsx();
315  $info = $xlsx->listWorksheetinfo($this->file);
316  $countcolumns = $info[0]['totalColumns'];
317  for ($col = 1; $col <= $countcolumns; $col++) {
318  $val = $this->workbook->getActiveSheet()->getCellByColumnAndRow($col, $this->record)->getValue();
319  $array[$col]['val'] = $val;
320  $array[$col]['type'] = (dol_strlen($val) ? 1 : -1); // If empty we consider it null
321  }
322  $this->record++;
323  return $array;
324  }
325 
326  // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
332  public function import_close_file()
333  {
334  // phpcs:enable
335  $this->workbook->disconnectWorksheets();
336  unset($this->workbook);
337  }
338 
339 
340  // What is this doing here ? it is common to all imports, is should be in the parent class
341  // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
353  public function import_insert($arrayrecord, $array_match_file_to_database, $objimport, $maxfields, $importid, $updatekeys)
354  {
355  // phpcs:enable
356  global $langs, $conf, $user;
357  global $thirdparty_static; // Specific to thirdparty import
358  global $tablewithentity_cache; // Cache to avoid to call desc at each rows on tables
359 
360  $error = 0;
361  $warning = 0;
362  $this->errors = array();
363  $this->warnings = array();
364 
365  //dol_syslog("import_csv.modules maxfields=".$maxfields." importid=".$importid);
366 
367  //var_dump($array_match_file_to_database);
368  //var_dump($arrayrecord);
369  $array_match_database_to_file = array_flip($array_match_file_to_database);
370  $sort_array_match_file_to_database = $array_match_file_to_database;
371  ksort($sort_array_match_file_to_database);
372 
373  //var_dump($sort_array_match_file_to_database);
374 
375  if (count($arrayrecord) == 0 || (count($arrayrecord) == 1 && empty($arrayrecord[1]['val']))) {
376  //print 'W';
377  $this->warnings[$warning]['lib'] = $langs->trans('EmptyLine');
378  $this->warnings[$warning]['type'] = 'EMPTY';
379  $warning++;
380  } else {
381  $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)
382  $updatedone = false;
383  $insertdone = false;
384  // For each table to insert, me make a separate insert
385  foreach ($objimport->array_import_tables[0] as $alias => $tablename) {
386  // Build sql request
387  $sql = '';
388  $listfields = array();
389  $listvalues = array();
390  $i = 0;
391  $errorforthistable = 0;
392 
393  // Define $tablewithentity_cache[$tablename] if not already defined
394  if (!isset($tablewithentity_cache[$tablename])) // keep this test with "isset"
395  {
396  dol_syslog("Check if table " . $tablename . " has an entity field");
397  $resql = $this->db->DDLDescTable($tablename, 'entity');
398  if ($resql) {
399  $obj = $this->db->fetch_object($resql);
400  if ($obj) $tablewithentity_cache[$tablename] = 1; // table contains entity field
401  else $tablewithentity_cache[$tablename] = 0; // table does not contains entity field
402  } else dol_print_error($this->db);
403  } else {
404  //dol_syslog("Table ".$tablename." check for entity into cache is ".$tablewithentity_cache[$tablename]);
405  }
406 
407  // array of fields to column index
408  $arrayfield = array();
409  foreach ($sort_array_match_file_to_database as $key => $val) {
410  $arrayfield[$val] = ($key - 1);
411  }
412 
413  // Loop on each fields in the match array: $key = 1..n, $val=alias of field (s.nom)
414  foreach ($sort_array_match_file_to_database as $key => $val) {
415  $fieldalias = preg_replace('/\..*$/i', '', $val);
416  $fieldname = preg_replace('/^.*\./i', '', $val);
417 
418  if ($alias != $fieldalias) continue; // Not a field of current table
419 
420  if ($key <= $maxfields) {
421  // Set $newval with value to insert and set $listvalues with sql request part for insert
422  $newval = '';
423  if ($arrayrecord[($key)]['type'] > 0) $newval = $arrayrecord[($key)]['val']; // If type of field into input file is not empty string (so defined into input file), we get value
424 
425  // Make some tests on $newval
426 
427  // Is it a required field ?
428  if (preg_match('/\*/', $objimport->array_import_fields[0][$val]) && ((string) $newval == '')) {
429  $this->errors[$error]['lib'] = $langs->trans('ErrorMissingMandatoryValue', $key);
430  $this->errors[$error]['type'] = 'NOTNULL';
431  $errorforthistable++;
432  $error++;
433  }
434  // Test format only if field is not a missing mandatory field (field may be a value or empty but not mandatory)
435  else {
436  // We convert field if required
437  if (!empty($objimport->array_import_convertvalue[0][$val])) {
438  //print 'Must convert '.$newval.' with rule '.join(',',$objimport->array_import_convertvalue[0][$val]).'. ';
439  if (
440  $objimport->array_import_convertvalue[0][$val]['rule'] == 'fetchidfromcodeid'
441  || $objimport->array_import_convertvalue[0][$val]['rule'] == 'fetchidfromref'
442  || $objimport->array_import_convertvalue[0][$val]['rule'] == 'fetchidfromcodeorlabel'
443  ) {
444  // 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.
445  $isidorref = 'id';
446  if (!is_numeric($newval) && $newval != '' && !preg_match('/^id:/i', $newval)) $isidorref = 'ref';
447  $newval = preg_replace('/^(id|ref):/i', '', $newval); // Remove id: or ref: that was used to force if field is id or ref
448  //print 'Val is now '.$newval.' and is type '.$isidorref."<br>\n";
449 
450  if ($isidorref == 'ref') { // If value into input import file is a ref, we apply the function defined into descriptor
451  $file = (empty($objimport->array_import_convertvalue[0][$val]['classfile']) ? $objimport->array_import_convertvalue[0][$val]['file'] : $objimport->array_import_convertvalue[0][$val]['classfile']);
452  $class = $objimport->array_import_convertvalue[0][$val]['class'];
453  $method = $objimport->array_import_convertvalue[0][$val]['method'];
454  if ($this->cacheconvert[$file . '_' . $class . '_' . $method . '_'][$newval] != '') {
455  $newval = $this->cacheconvert[$file . '_' . $class . '_' . $method . '_'][$newval];
456  } else {
457  $resultload = dol_include_once($file);
458  if (empty($resultload)) {
459  dol_print_error('', 'Error trying to call file=' . $file . ', class=' . $class . ', method=' . $method);
460  break;
461  }
462  $classinstance = new $class($this->db);
463  // Try the fetch from code or ref
464  $param_array = array('', $newval);
465  if ($class == 'AccountingAccount') {
466  //var_dump($arrayrecord[0]['val']);
467  /*include_once DOL_DOCUMENT_ROOT.'/accountancy/class/accountancysystem.class.php';
468  $tmpchartofaccount = new AccountancySystem($this->db);
469  $tmpchartofaccount->fetch($conf->global->CHARTOFACCOUNTS);
470  var_dump($tmpchartofaccount->ref.' - '.$arrayrecord[0]['val']);
471  if ((! ($conf->global->CHARTOFACCOUNTS > 0)) || $tmpchartofaccount->ref != $arrayrecord[0]['val'])
472  {
473  $this->errors[$error]['lib']=$langs->trans('ErrorImportOfChartLimitedToCurrentChart', $tmpchartofaccount->ref);
474  $this->errors[$error]['type']='RESTRICTONCURRENCTCHART';
475  $errorforthistable++;
476  $error++;
477  }*/
478  $param_array = array('', $newval, 0, $arrayrecord[0]['val']); // Param to fetch parent from account, in chart.
479  }
480  call_user_func_array(array($classinstance, $method), $param_array);
481  // If not found, try the fetch from label
482  if (!($classinstance->id != '') && $objimport->array_import_convertvalue[0][$val]['rule'] == 'fetchidfromcodeorlabel') {
483  $param_array = array('', '', $newval);
484  call_user_func_array(array($classinstance, $method), $param_array);
485  }
486  $this->cacheconvert[$file . '_' . $class . '_' . $method . '_'][$newval] = $classinstance->id;
487  //print 'We have made a '.$class.'->'.$method.' to get id from code '.$newval.'. ';
488  if ($classinstance->id != '') // id may be 0, it is a found value
489  {
490  $newval = $classinstance->id;
491  } else {
492  if (!empty($objimport->array_import_convertvalue[0][$val]['dict'])) $this->errors[$error]['lib'] = $langs->trans('ErrorFieldValueNotIn', $key, $newval, 'code', $langs->transnoentitiesnoconv($objimport->array_import_convertvalue[0][$val]['dict']));
493  elseif (!empty($objimport->array_import_convertvalue[0][$val]['element'])) $this->errors[$error]['lib'] = $langs->trans('ErrorFieldRefNotIn', $key, $newval, $langs->transnoentitiesnoconv($objimport->array_import_convertvalue[0][$val]['element']));
494  else $this->errors[$error]['lib'] = 'ErrorBadDefinitionOfImportProfile';
495  $this->errors[$error]['type'] = 'FOREIGNKEY';
496  $errorforthistable++;
497  $error++;
498  }
499  }
500  }
501  } elseif ($objimport->array_import_convertvalue[0][$val]['rule'] == 'fetchidfromcodeandlabel') {
502  $isidorref = 'id';
503  if (!is_numeric($newval) && $newval != '' && !preg_match('/^id:/i', $newval)) $isidorref = 'ref';
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  {
527  $newval = $classinstance->id;
528  } else {
529  if (!empty($objimport->array_import_convertvalue[0][$val]['dict'])) $this->errors[$error]['lib'] = $langs->trans('ErrorFieldValueNotIn', $key, $newval, 'scale', $langs->transnoentitiesnoconv($objimport->array_import_convertvalue[0][$val]['dict']));
530  else $this->errors[$error]['lib'] = 'ErrorFieldValueNotIn';
531  $this->errors[$error]['type'] = 'FOREIGNKEY';
532  $errorforthistable++;
533  $error++;
534  }
535  }
536  }
537  } elseif ($objimport->array_import_convertvalue[0][$val]['rule'] == 'zeroifnull') {
538  if (empty($newval)) $newval = '0';
539  } elseif ($objimport->array_import_convertvalue[0][$val]['rule'] == 'fetchidfromcodeunits' || $objimport->array_import_convertvalue[0][$val]['rule'] == 'fetchscalefromcodeunits') {
540  $file = (empty($objimport->array_import_convertvalue[0][$val]['classfile']) ? $objimport->array_import_convertvalue[0][$val]['file'] : $objimport->array_import_convertvalue[0][$val]['classfile']);
541  $class = $objimport->array_import_convertvalue[0][$val]['class'];
542  $method = $objimport->array_import_convertvalue[0][$val]['method'];
543  $units = $objimport->array_import_convertvalue[0][$val]['units'];
544  if ($this->cacheconvert[$file . '_' . $class . '_' . $method . '_' . $units][$newval] != '') {
545  $newval = $this->cacheconvert[$file . '_' . $class . '_' . $method . '_' . $units][$newval];
546  } else {
547  $resultload = dol_include_once($file);
548  if (empty($resultload)) {
549  dol_print_error('', 'Error trying to call file=' . $file . ', class=' . $class . ', method=' . $method . ', units=' . $units);
550  break;
551  }
552  $classinstance = new $class($this->db);
553  // Try the fetch from code or ref
554  call_user_func_array(array($classinstance, $method), array('', '', $newval, $units));
555  $scaleorid = (($objimport->array_import_convertvalue[0][$val]['rule'] == 'fetchidfromcodeunits') ? $classinstance->id : $classinstance->scale);
556  $this->cacheconvert[$file . '_' . $class . '_' . $method . '_' . $units][$newval] = $scaleorid;
557  //print 'We have made a '.$class.'->'.$method." to get a value from key '".$newval."' and we got '".$scaleorid."'.";exit;
558  if ($classinstance->id > 0) // we found record
559  {
560  $newval = $scaleorid ? $scaleorid : 0;
561  } else {
562  if (!empty($objimport->array_import_convertvalue[0][$val]['dict'])) $this->errors[$error]['lib'] = $langs->trans('ErrorFieldValueNotIn', $key, $newval, 'scale', $langs->transnoentitiesnoconv($objimport->array_import_convertvalue[0][$val]['dict']));
563  else $this->errors[$error]['lib'] = 'ErrorFieldValueNotIn';
564  $this->errors[$error]['type'] = 'FOREIGNKEY';
565  $errorforthistable++;
566  $error++;
567  }
568  }
569  } elseif ($objimport->array_import_convertvalue[0][$val]['rule'] == 'getcustomercodeifauto') {
570  if (strtolower($newval) == 'auto') {
571  $this->thirpartyobject->get_codeclient(0, 0);
572  $newval = $this->thirpartyobject->code_client;
573  //print 'code_client='.$newval;
574  }
575  if (empty($newval)) $arrayrecord[($key)]['type'] = -1; // If we get empty value, we will use "null"
576  } elseif ($objimport->array_import_convertvalue[0][$val]['rule'] == 'getsuppliercodeifauto') {
577  if (strtolower($newval) == 'auto') {
578  $newval = $this->thirpartyobject->get_codefournisseur(0, 1);
579  $newval = $this->thirpartyobject->code_fournisseur;
580  //print 'code_fournisseur='.$newval;
581  }
582  if (empty($newval)) $arrayrecord[($key)]['type'] = -1; // If we get empty value, we will use "null"
583  } elseif ($objimport->array_import_convertvalue[0][$val]['rule'] == 'getcustomeraccountancycodeifauto') {
584  if (strtolower($newval) == 'auto') {
585  $this->thirpartyobject->get_codecompta('customer');
586  $newval = $this->thirpartyobject->code_compta;
587  //print 'code_compta='.$newval;
588  }
589  if (empty($newval)) $arrayrecord[($key)]['type'] = -1; // If we get empty value, we will use "null"
590  } elseif ($objimport->array_import_convertvalue[0][$val]['rule'] == 'getsupplieraccountancycodeifauto') {
591  if (strtolower($newval) == 'auto') {
592  $this->thirpartyobject->get_codecompta('supplier');
593  $newval = $this->thirpartyobject->code_compta_fournisseur;
594  if (empty($newval)) $arrayrecord[($key - 1)]['type'] = -1; // If we get empty value, we will use "null"
595  //print 'code_compta_fournisseur='.$newval;
596  }
597  if (empty($newval)) $arrayrecord[($key)]['type'] = -1; // If we get empty value, we will use "null"
598  } elseif ($objimport->array_import_convertvalue[0][$val]['rule'] == 'getrefifauto') {
599  $defaultref = '';
600  // TODO provide the $modTask (module of generation of ref) as parameter of import_insert function
601  $obj = empty($conf->global->PROJECT_TASK_ADDON) ? 'mod_task_simple' : $conf->global->PROJECT_TASK_ADDON;
602  if (!empty($conf->global->PROJECT_TASK_ADDON) && is_readable(DOL_DOCUMENT_ROOT . "/core/modules/project/task/" . $conf->global->PROJECT_TASK_ADDON . ".php")) {
603  require_once DOL_DOCUMENT_ROOT . "/core/modules/project/task/" . $conf->global->PROJECT_TASK_ADDON . '.php';
604  $modTask = new $obj;
605  $defaultref = $modTask->getNextValue(null, null);
606  }
607  if (is_numeric($defaultref) && $defaultref <= 0) $defaultref = '';
608  $newval = $defaultref;
609  } elseif ($objimport->array_import_convertvalue[0][$val]['rule'] == 'compute') {
610  $file = (empty($objimport->array_import_convertvalue[0][$val]['classfile']) ? $objimport->array_import_convertvalue[0][$val]['file'] : $objimport->array_import_convertvalue[0][$val]['classfile']);
611  $class = $objimport->array_import_convertvalue[0][$val]['class'];
612  $method = $objimport->array_import_convertvalue[0][$val]['method'];
613  $resultload = dol_include_once($file);
614  if (empty($resultload)) {
615  dol_print_error('', 'Error trying to call file=' . $file . ', class=' . $class . ', method=' . $method);
616  break;
617  }
618  $classinstance = new $class($this->db);
619  $res = call_user_func_array(array($classinstance, $method), array(&$arrayrecord));
620  if ($res < 0) {
621  if (!empty($objimport->array_import_convertvalue[0][$val]['dict'])) $this->errors[$error]['lib'] = $langs->trans('ErrorFieldValueNotIn', $key, $newval, 'code', $langs->transnoentitiesnoconv($objimport->array_import_convertvalue[0][$val]['dict']));
622  else $this->errors[$error]['lib'] = 'ErrorFieldValueNotIn';
623  $this->errors[$error]['type'] = 'FOREIGNKEY';
624  $errorforthistable++;
625  $error++;
626  } else {
627  $newval = $arrayrecord[($key)]['val']; //We get new value computed.
628  }
629  } elseif ($objimport->array_import_convertvalue[0][$val]['rule'] == 'numeric') {
630  $newval = price2num($newval);
631  }
632 
633  //print 'Val to use as insert is '.$newval.'<br>';
634  }
635 
636  // Test regexp
637  if (!empty($objimport->array_import_regex[0][$val]) && ($newval != '')) {
638  // If test is "Must exist in a field@table or field@table:..."
639  $reg = array();
640  if (preg_match('/^(.+)@([^:]+)(:.+)?$/', $objimport->array_import_regex[0][$val], $reg)) {
641  $field = $reg[1];
642  $table = $reg[2];
643  $filter = !empty($reg[3]) ? substr($reg[3], 1) : '';
644 
645  $cachekey = $field . '@' . $table;
646  if (!empty($filter)) $cachekey .= ':' . $filter;
647 
648  // Load content of field@table into cache array
649  if (!is_array($this->cachefieldtable[$cachekey])) // If content of field@table not already loaded into cache
650  {
651  $sql = "SELECT " . $field . " as aliasfield FROM " . $table;
652  if (!empty($filter)) {
653  $sql .= ' WHERE ' . $filter;
654  }
655 
656  $resql = $this->db->query($sql);
657  if ($resql) {
658  $num = $this->db->num_rows($resql);
659  $i = 0;
660  while ($i < $num) {
661  $obj = $this->db->fetch_object($resql);
662  if ($obj) $this->cachefieldtable[$cachekey][] = $obj->aliasfield;
663  $i++;
664  }
665  } else {
666  dol_print_error($this->db);
667  }
668  }
669 
670  // Now we check cache is not empty (should not) and key is into cache
671  if (!is_array($this->cachefieldtable[$cachekey]) || !in_array($newval, $this->cachefieldtable[$cachekey])) {
672  $tableforerror = $table;
673  if (!empty($filter)) $tableforerror .= ':' . $filter;
674  $this->errors[$error]['lib'] = $langs->transnoentitiesnoconv('ErrorFieldValueNotIn', $key, $newval, $field, $tableforerror);
675  $this->errors[$error]['type'] = 'FOREIGNKEY';
676  $errorforthistable++;
677  $error++;
678  }
679  }
680  // If test is just a static regex
681  elseif (!preg_match('/' . $objimport->array_import_regex[0][$val] . '/i', $newval)) {
682  //if ($key == 19) print "xxx".$newval."zzz".$objimport->array_import_regex[0][$val]."<br>";
683  $this->errors[$error]['lib'] = $langs->transnoentitiesnoconv('ErrorWrongValueForField', $key, $newval, $objimport->array_import_regex[0][$val]);
684  $this->errors[$error]['type'] = 'REGEX';
685  $errorforthistable++;
686  $error++;
687  }
688  }
689 
690  // Other tests
691  // ...
692  }
693 
694  // Define $listfields and $listvalues to build SQL request
695  $listfields[] = $fieldname;
696 
697  // Note: arrayrecord (and 'type') is filled with ->import_read_record called by import.php page before calling import_insert
698  if (empty($newval) && $arrayrecord[($key)]['type'] < 0) $listvalues[] = ($newval == '0' ? $newval : "null");
699  elseif (empty($newval) && $arrayrecord[($key)]['type'] == 0) $listvalues[] = "''";
700  else $listvalues[] = "'" . $this->db->escape($newval) . "'";
701  }
702  $i++;
703  }
704 
705  // We add hidden fields (but only if there is at least one field to add into table)
706  if (!empty($listfields) && is_array($objimport->array_import_fieldshidden[0])) {
707  // Loop on each hidden fields to add them into listfields/listvalues
708  foreach ($objimport->array_import_fieldshidden[0] as $key => $val) {
709  if (!preg_match('/^' . preg_quote($alias, '/') . '\./', $key)) continue; // Not a field of current table
710  if ($val == 'user->id') {
711  $listfields[] = preg_replace('/^' . preg_quote($alias, '/') . '\./', '', $key);
712  $listvalues[] = $user->id;
713  } elseif (preg_match('/^lastrowid-/', $val)) {
714  $tmp = explode('-', $val);
715  $lastinsertid = (isset($last_insert_id_array[$tmp[1]])) ? $last_insert_id_array[$tmp[1]] : 0;
716  $keyfield = preg_replace('/^' . preg_quote($alias, '/') . '\./', '', $key);
717  $listfields[] = $keyfield;
718  $listvalues[] = $lastinsertid;
719  //print $key."-".$val."-".$listfields."-".$listvalues."<br>";exit;
720  } elseif (preg_match('/^const-/', $val)) {
721  $tmp = explode('-', $val, 2);
722  $listfields[] = preg_replace('/^' . preg_quote($alias, '/') . '\./', '', $key);
723  $listvalues[] = "'" . $tmp[1] . "'";
724  } else {
725  $this->errors[$error]['lib'] = 'Bad value of profile setup ' . $val . ' for array_import_fieldshidden';
726  $this->errors[$error]['type'] = 'Import profile setup';
727  $error++;
728  }
729  }
730  }
731  //print 'listfields='.$listfields.'<br>listvalues='.$listvalues.'<br>';
732 
733  // If no error for this $alias/$tablename, we have a complete $listfields and $listvalues that are defined
734  // so we can try to make the insert or update now.
735  if (!$errorforthistable) {
736  //print "$alias/$tablename/$listfields/$listvalues<br>";
737  if (!empty($listfields)) {
738  $updatedone = false;
739  $insertdone = false;
740  if (!empty($updatekeys)) {
741  // We do SELECT to get the rowid, if we already have the rowid, it's to be used below for related tables (extrafields)
742 
743  if (empty($lastinsertid)) { // No insert done yet for a parent table
744  $sqlSelect = 'SELECT rowid FROM ' . $tablename;
745 
746  $data = array_combine($listfields, $listvalues);
747  $where = array();
748  $filters = array();
749  foreach ($updatekeys as $key) {
750  $col = $objimport->array_import_updatekeys[0][$key];
751  $key = preg_replace('/^.*\./i', '', $key);
752  $where[] = $key . ' = ' . $data[$key];
753  $filters[] = $col . ' = ' . $data[$key];
754  }
755  $sqlSelect .= ' WHERE ' . implode(' AND ', $where);
756 
757  $resql = $this->db->query($sqlSelect);
758  if ($resql) {
759  $res = $this->db->fetch_object($resql);
760  if ($resql->num_rows == 1) {
761  $lastinsertid = $res->rowid;
762  $last_insert_id_array[$tablename] = $lastinsertid;
763  } elseif ($resql->num_rows > 1) {
764  $this->errors[$error]['lib'] = $langs->trans('MultipleRecordFoundWithTheseFilters', implode(', ', $filters));
765  $this->errors[$error]['type'] = 'SQL';
766  $error++;
767  } else {
768  // No record found with filters, insert will be tried below
769  }
770  } else {
771  //print 'E';
772  $this->errors[$error]['lib'] = $this->db->lasterror();
773  $this->errors[$error]['type'] = 'SQL';
774  $error++;
775  }
776  } else {
777  // We have a last INSERT ID (got by previous pass), so we check if we have a row referencing this foreign key.
778  // This is required when updating table with some extrafields. When inserting a record in parent table, we can make
779  // a direct insert into subtable extrafields, but when me wake an update, the insertid is defined and the child record
780  // may already exists. So we rescan the extrafield table to know if record exists or not for the rowid.
781  // Note: For extrafield tablename, we have in importfieldshidden_array an enty 'extra.fk_object'=>'lastrowid-tableparent' so $keyfield is 'fk_object'
782  $sqlSelect = 'SELECT rowid FROM ' . $tablename;
783 
784  if (empty($keyfield)) $keyfield = 'rowid';
785  $sqlSelect .= ' WHERE ' . $keyfield . ' = ' . $lastinsertid;
786 
787  $resql = $this->db->query($sqlSelect);
788  if ($resql) {
789  $res = $this->db->fetch_object($resql);
790  if ($resql->num_rows == 1) {
791  // We have a row referencing this last foreign key, continue with UPDATE.
792  } else {
793  // No record found referencing this last foreign key,
794  // force $lastinsertid to 0 so we INSERT below.
795  $lastinsertid = 0;
796  }
797  } else {
798  //print 'E';
799  $this->errors[$error]['lib'] = $this->db->lasterror();
800  $this->errors[$error]['type'] = 'SQL';
801  $error++;
802  }
803  }
804 
805  if (!empty($lastinsertid)) {
806  // Build SQL UPDATE request
807  $sqlstart = 'UPDATE ' . $tablename;
808 
809  $data = array_combine($listfields, $listvalues);
810  $set = array();
811  foreach ($data as $key => $val) {
812  $set[] = $key . ' = ' . $val;
813  }
814  $sqlstart .= ' SET ' . implode(', ', $set);
815 
816  if (empty($keyfield)) $keyfield = 'rowid';
817  $sqlend = ' WHERE ' . $keyfield . ' = ' . $lastinsertid;
818 
819  $sql = $sqlstart . $sqlend;
820 
821  // Run update request
822  $resql = $this->db->query($sql);
823  if ($resql) {
824  // No error, update has been done. $this->db->db->affected_rows can be 0 if data hasn't changed
825  $updatedone = true;
826  } else {
827  //print 'E';
828  $this->errors[$error]['lib'] = $this->db->lasterror();
829  $this->errors[$error]['type'] = 'SQL';
830  $error++;
831  }
832  }
833  }
834 
835  // Update not done, we do insert
836  if (!$error && !$updatedone) {
837  // Build SQL INSERT request
838  $sqlstart = 'INSERT INTO ' . $tablename . '(' . implode(', ', $listfields) . ', import_key';
839  $sqlend = ') VALUES(' . implode(', ', $listvalues) . ", '" . $this->db->escape($importid) . "'";
840  if (!empty($tablewithentity_cache[$tablename])) {
841  $sqlstart .= ', entity';
842  $sqlend .= ', ' . $conf->entity;
843  }
844  if (!empty($objimport->array_import_tables_creator[0][$alias])) {
845  $sqlstart .= ', ' . $objimport->array_import_tables_creator[0][$alias];
846  $sqlend .= ', ' . $user->id;
847  }
848  $sql = $sqlstart . $sqlend . ')';
849  //dol_syslog("import_xlsx.modules", LOG_DEBUG);
850 
851  // Run insert request
852  if ($sql) {
853  $resql = $this->db->query($sql);
854  if ($resql) {
855  $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).
856  $insertdone = true;
857  } else {
858  //print 'E';
859  $this->errors[$error]['lib'] = $this->db->lasterror();
860  $this->errors[$error]['type'] = 'SQL';
861  $error++;
862  }
863  }
864  }
865  }
866  /*else
867  {
868  dol_print_error('','ErrorFieldListEmptyFor '.$alias."/".$tablename);
869  }*/
870  }
871 
872  if ($error) break;
873  }
874 
875  if ($updatedone) $this->nbupdate++;
876  if ($insertdone) $this->nbinsert++;
877  }
878 
879  return 1;
880  }
881 }
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.
import_read_header()
Input header line from file.
Class to import Excel files.
write_footer_example($outputlangs)
Output footer of an example file for this format.
import_get_nb_of_lines($file)
Return nb of records.
$conf db
API class for accounts.
Definition: inc.php:54
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.
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.
if(!empty($conf->facture->enabled)&&$user->rights->facture->lire) if((!empty($conf->fournisseur->enabled)&&empty($conf->global->MAIN_USE_NEW_SUPPLIERMOD)||!empty($conf->supplier_invoice->enabled))&&$user->rights->fournisseur->facture->lire) if(!empty($conf->don->enabled)&&$user->rights->don->lire) if(!empty($conf->tax->enabled)&&$user->rights->tax->charges->lire) if(!empty($conf->facture->enabled)&&!empty($conf->commande->enabled)&&$user->rights->commande->lire &&empty($conf->global->WORKFLOW_DISABLE_CREATE_INVOICE_FROM_ORDER)) if(!empty($conf->facture->enabled)&&$user->rights->facture->lire) if((!empty($conf->fournisseur->enabled)&&empty($conf->global->MAIN_USE_NEW_SUPPLIERMOD)||!empty($conf->supplier_invoice->enabled))&&$user->rights->fournisseur->facture->lire) $resql
Social contributions to pay.
Definition: index.php:1232
dol_print_error($db= '', $error= '', $errors=null)
Displays error message system with all the information to facilitate the diagnosis and the escalation...
import_close_file()
Close file handle.