dolibarr  16.0.5
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 
506  $result = call_user_func_array(array($classinstance, $method), $param_array);
507 
508  // If duplicate record found
509  if (!($classinstance->id != '') && $result == -2) {
510  $this->errors[$error]['lib'] = $langs->trans('ErrorMultipleRecordFoundFromRef', $newval);
511  $this->errors[$error]['type'] = 'FOREIGNKEY';
512  $errorforthistable++;
513  $error++;
514  }
515 
516  // If not found, try the fetch from label
517  if (!($classinstance->id != '') && $objimport->array_import_convertvalue[0][$val]['rule'] == 'fetchidfromcodeorlabel') {
518  $param_array = array('', '', $newval);
519  call_user_func_array(array($classinstance, $method), $param_array);
520  }
521  $this->cacheconvert[$file . '_' . $class . '_' . $method . '_'][$newval] = $classinstance->id;
522 
523  //print 'We have made a '.$class.'->'.$method.' to get id from code '.$newval.'. ';
524  if ($classinstance->id != '') { // id may be 0, it is a found value
525  $newval = $classinstance->id;
526  } elseif (! $error) {
527  if (!empty($objimport->array_import_convertvalue[0][$val]['dict'])) {
528  $this->errors[$error]['lib'] = $langs->trans('ErrorFieldValueNotIn', $key, $newval, 'code', $langs->transnoentitiesnoconv($objimport->array_import_convertvalue[0][$val]['dict']));
529  } elseif (!empty($objimport->array_import_convertvalue[0][$val]['element'])) {
530  $this->errors[$error]['lib'] = $langs->trans('ErrorFieldRefNotIn', $key, $newval, $langs->transnoentitiesnoconv($objimport->array_import_convertvalue[0][$val]['element']));
531  } else {
532  $this->errors[$error]['lib'] = 'ErrorBadDefinitionOfImportProfile';
533  }
534  $this->errors[$error]['type'] = 'FOREIGNKEY';
535  $errorforthistable++;
536  $error++;
537  }
538  }
539  }
540  } elseif ($objimport->array_import_convertvalue[0][$val]['rule'] == 'fetchidfromcodeandlabel') {
541  $isidorref = 'id';
542  if (!is_numeric($newval) && $newval != '' && !preg_match('/^id:/i', $newval)) {
543  $isidorref = 'ref';
544  }
545  $newval = preg_replace('/^(id|ref):/i', '', $newval);
546 
547  if ($isidorref == 'ref') {
548  $file = (empty($objimport->array_import_convertvalue[0][$val]['classfile']) ? $objimport->array_import_convertvalue[0][$val]['file'] : $objimport->array_import_convertvalue[0][$val]['classfile']);
549  $class = $objimport->array_import_convertvalue[0][$val]['class'];
550  $method = $objimport->array_import_convertvalue[0][$val]['method'];
551  $codefromfield = $objimport->array_import_convertvalue[0][$val]['codefromfield'];
552  $code = $arrayrecord[$arrayfield[$codefromfield]]['val'];
553  if ($this->cacheconvert[$file . '_' . $class . '_' . $method . '_' . $code][$newval] != '') {
554  $newval = $this->cacheconvert[$file . '_' . $class . '_' . $method . '_' . $code][$newval];
555  } else {
556  $resultload = dol_include_once($file);
557  if (empty($resultload)) {
558  dol_print_error('', 'Error trying to call file=' . $file . ', class=' . $class . ', method=' . $method . ', code=' . $code);
559  break;
560  }
561  $classinstance = new $class($this->db);
562  // Try the fetch from code and ref
563  $param_array = array('', $newval, $code);
564  call_user_func_array(array($classinstance, $method), $param_array);
565  $this->cacheconvert[$file . '_' . $class . '_' . $method . '_' . $code][$newval] = $classinstance->id;
566  if ($classinstance->id > 0) { // we found record
567  $newval = $classinstance->id;
568  } else {
569  if (!empty($objimport->array_import_convertvalue[0][$val]['dict'])) {
570  $this->errors[$error]['lib'] = $langs->trans('ErrorFieldValueNotIn', $key, $newval, 'scale', $langs->transnoentitiesnoconv($objimport->array_import_convertvalue[0][$val]['dict']));
571  } else {
572  $this->errors[$error]['lib'] = 'ErrorFieldValueNotIn';
573  }
574  $this->errors[$error]['type'] = 'FOREIGNKEY';
575  $errorforthistable++;
576  $error++;
577  }
578  }
579  }
580  } elseif ($objimport->array_import_convertvalue[0][$val]['rule'] == 'zeroifnull') {
581  if (empty($newval)) {
582  $newval = '0';
583  }
584  } elseif ($objimport->array_import_convertvalue[0][$val]['rule'] == 'fetchidfromcodeunits' || $objimport->array_import_convertvalue[0][$val]['rule'] == 'fetchscalefromcodeunits') {
585  $file = (empty($objimport->array_import_convertvalue[0][$val]['classfile']) ? $objimport->array_import_convertvalue[0][$val]['file'] : $objimport->array_import_convertvalue[0][$val]['classfile']);
586  $class = $objimport->array_import_convertvalue[0][$val]['class'];
587  $method = $objimport->array_import_convertvalue[0][$val]['method'];
588  $units = $objimport->array_import_convertvalue[0][$val]['units'];
589  if ($this->cacheconvert[$file . '_' . $class . '_' . $method . '_' . $units][$newval] != '') {
590  $newval = $this->cacheconvert[$file . '_' . $class . '_' . $method . '_' . $units][$newval];
591  } else {
592  $resultload = dol_include_once($file);
593  if (empty($resultload)) {
594  dol_print_error('', 'Error trying to call file=' . $file . ', class=' . $class . ', method=' . $method . ', units=' . $units);
595  break;
596  }
597  $classinstance = new $class($this->db);
598  // Try the fetch from code or ref
599  call_user_func_array(array($classinstance, $method), array('', '', $newval, $units));
600  $scaleorid = (($objimport->array_import_convertvalue[0][$val]['rule'] == 'fetchidfromcodeunits') ? $classinstance->id : $classinstance->scale);
601  $this->cacheconvert[$file . '_' . $class . '_' . $method . '_' . $units][$newval] = $scaleorid;
602  //print 'We have made a '.$class.'->'.$method." to get a value from key '".$newval."' and we got '".$scaleorid."'.";exit;
603  if ($classinstance->id > 0) { // we found record
604  $newval = $scaleorid ? $scaleorid : 0;
605  } else {
606  if (!empty($objimport->array_import_convertvalue[0][$val]['dict'])) {
607  $this->errors[$error]['lib'] = $langs->trans('ErrorFieldValueNotIn', $key, $newval, 'scale', $langs->transnoentitiesnoconv($objimport->array_import_convertvalue[0][$val]['dict']));
608  } else {
609  $this->errors[$error]['lib'] = 'ErrorFieldValueNotIn';
610  }
611  $this->errors[$error]['type'] = 'FOREIGNKEY';
612  $errorforthistable++;
613  $error++;
614  }
615  }
616  } elseif ($objimport->array_import_convertvalue[0][$val]['rule'] == 'getcustomercodeifauto') {
617  if (strtolower($newval) == 'auto') {
618  $this->thirpartyobject->get_codeclient(0, 0);
619  $newval = $this->thirpartyobject->code_client;
620  //print 'code_client='.$newval;
621  }
622  if (empty($newval)) {
623  $arrayrecord[($key)]['type'] = -1; // If we get empty value, we will use "null"
624  }
625  } elseif ($objimport->array_import_convertvalue[0][$val]['rule'] == 'getsuppliercodeifauto') {
626  if (strtolower($newval) == 'auto') {
627  $newval = $this->thirpartyobject->get_codefournisseur(0, 1);
628  $newval = $this->thirpartyobject->code_fournisseur;
629  //print 'code_fournisseur='.$newval;
630  }
631  if (empty($newval)) {
632  $arrayrecord[($key)]['type'] = -1; // If we get empty value, we will use "null"
633  }
634  } elseif ($objimport->array_import_convertvalue[0][$val]['rule'] == 'getcustomeraccountancycodeifauto') {
635  if (strtolower($newval) == 'auto') {
636  $this->thirpartyobject->get_codecompta('customer');
637  $newval = $this->thirpartyobject->code_compta;
638  //print 'code_compta='.$newval;
639  }
640  if (empty($newval)) {
641  $arrayrecord[($key)]['type'] = -1; // If we get empty value, we will use "null"
642  }
643  } elseif ($objimport->array_import_convertvalue[0][$val]['rule'] == 'getsupplieraccountancycodeifauto') {
644  if (strtolower($newval) == 'auto') {
645  $this->thirpartyobject->get_codecompta('supplier');
646  $newval = $this->thirpartyobject->code_compta_fournisseur;
647  if (empty($newval)) {
648  $arrayrecord[($key)]['type'] = -1; // If we get empty value, we will use "null"
649  }
650  //print 'code_compta_fournisseur='.$newval;
651  }
652  if (empty($newval)) {
653  $arrayrecord[($key)]['type'] = -1; // If we get empty value, we will use "null"
654  }
655  } elseif ($objimport->array_import_convertvalue[0][$val]['rule'] == 'getrefifauto') {
656  if (strtolower($newval) == 'auto') {
657  $defaultref = '';
658 
659  $classModForNumber = $objimport->array_import_convertvalue[0][$val]['class'];
660  $pathModForNumber = $objimport->array_import_convertvalue[0][$val]['path'];
661 
662  if (!empty($classModForNumber) && !empty($pathModForNumber) && is_readable(DOL_DOCUMENT_ROOT.$pathModForNumber)) {
663  require_once DOL_DOCUMENT_ROOT.$pathModForNumber;
664  $modForNumber = new $classModForNumber;
665 
666  $tmpobject = null;
667  // Set the object with the date property when we can
668  if (!empty($objimport->array_import_convertvalue[0][$val]['classobject'])) {
669  $pathForObject = $objimport->array_import_convertvalue[0][$val]['pathobject'];
670  require_once DOL_DOCUMENT_ROOT.$pathForObject;
671  $tmpclassobject = $objimport->array_import_convertvalue[0][$val]['classobject'];
672  $tmpobject = new $tmpclassobject($this->db);
673  foreach ($arrayfield as $tmpkey => $tmpval) { // $arrayfield is array('c.ref'=>1, ...)
674  if (in_array($tmpkey, array('t.date', 'c.date_commande'))) {
675  $tmpobject->date = dol_stringtotime($arrayrecord[$arrayfield[$tmpkey]]['val'], 1);
676  }
677  }
678  }
679 
680  $defaultref = $modForNumber->getNextValue(null, $tmpobject);
681  }
682  if (is_numeric($defaultref) && $defaultref <= 0) { // If error
683  $defaultref = '';
684  }
685  $newval = $defaultref;
686  }
687  } elseif ($objimport->array_import_convertvalue[0][$val]['rule'] == 'compute') {
688  $file = (empty($objimport->array_import_convertvalue[0][$val]['classfile']) ? $objimport->array_import_convertvalue[0][$val]['file'] : $objimport->array_import_convertvalue[0][$val]['classfile']);
689  $class = $objimport->array_import_convertvalue[0][$val]['class'];
690  $method = $objimport->array_import_convertvalue[0][$val]['method'];
691  $resultload = dol_include_once($file);
692  if (empty($resultload)) {
693  dol_print_error('', 'Error trying to call file=' . $file . ', class=' . $class . ', method=' . $method);
694  break;
695  }
696  $classinstance = new $class($this->db);
697  $res = call_user_func_array(array($classinstance, $method), array(&$arrayrecord, $listfields, $key));
698  $newval = $res; // We get new value computed.
699  } elseif ($objimport->array_import_convertvalue[0][$val]['rule'] == 'numeric') {
700  $newval = price2num($newval);
701  } elseif ($objimport->array_import_convertvalue[0][$val]['rule'] == 'accountingaccount') {
702  if (empty($conf->global->ACCOUNTING_MANAGE_ZERO)) {
703  $newval = rtrim(trim($newval), "0");
704  } else {
705  $newval = trim($newval);
706  }
707  }
708 
709  //print 'Val to use as insert is '.$newval.'<br>';
710  }
711 
712  // Test regexp
713  if (!empty($objimport->array_import_regex[0][$val]) && ($newval != '')) {
714  // If test is "Must exist in a field@table or field@table:..."
715  $reg = array();
716  if (preg_match('/^(.+)@([^:]+)(:.+)?$/', $objimport->array_import_regex[0][$val], $reg)) {
717  $field = $reg[1];
718  $table = $reg[2];
719  $filter = !empty($reg[3]) ? substr($reg[3], 1) : '';
720 
721  $cachekey = $field . '@' . $table;
722  if (!empty($filter)) {
723  $cachekey .= ':' . $filter;
724  }
725 
726  // Load content of field@table into cache array
727  if (!is_array($this->cachefieldtable[$cachekey])) { // If content of field@table not already loaded into cache
728  $sql = "SELECT " . $field . " as aliasfield FROM " . $table;
729  if (!empty($filter)) {
730  $sql .= ' WHERE ' . $filter;
731  }
732 
733  $resql = $this->db->query($sql);
734  if ($resql) {
735  $num = $this->db->num_rows($resql);
736  $i = 0;
737  while ($i < $num) {
738  $obj = $this->db->fetch_object($resql);
739  if ($obj) {
740  $this->cachefieldtable[$cachekey][] = $obj->aliasfield;
741  }
742  $i++;
743  }
744  } else {
745  dol_print_error($this->db);
746  }
747  }
748 
749  // Now we check cache is not empty (should not) and key is into cache
750  if (!is_array($this->cachefieldtable[$cachekey]) || !in_array($newval, $this->cachefieldtable[$cachekey])) {
751  $tableforerror = $table;
752  if (!empty($filter)) {
753  $tableforerror .= ':' . $filter;
754  }
755  $this->errors[$error]['lib'] = $langs->transnoentitiesnoconv('ErrorFieldValueNotIn', $key, $newval, $field, $tableforerror);
756  $this->errors[$error]['type'] = 'FOREIGNKEY';
757  $errorforthistable++;
758  $error++;
759  }
760  } elseif (!preg_match('/' . $objimport->array_import_regex[0][$val] . '/i', $newval)) {
761  // If test is just a static regex
762  //if ($key == 19) print "xxx".$newval."zzz".$objimport->array_import_regex[0][$val]."<br>";
763  $this->errors[$error]['lib'] = $langs->transnoentitiesnoconv('ErrorWrongValueForField', $key, $newval, $objimport->array_import_regex[0][$val]);
764  $this->errors[$error]['type'] = 'REGEX';
765  $errorforthistable++;
766  $error++;
767  }
768  }
769 
770  // Check HTML injection
771  $inj = testSqlAndScriptInject($newval, 0);
772  if ($inj) {
773  $this->errors[$error]['lib'] = $langs->transnoentitiesnoconv('ErrorHtmlInjectionForField', $key, dol_trunc($newval, 100));
774  $this->errors[$error]['type'] = 'HTMLINJECTION';
775  $errorforthistable++;
776  $error++;
777  }
778 
779  // Other tests
780  // ...
781  }
782 
783  // Define $listfields and $listvalues to build SQL request
784  if (isModEnabled("socialnetworks") && strpos($fieldname, "socialnetworks") !== false) {
785  if (!in_array("socialnetworks", $listfields)) {
786  $listfields[] = "socialnetworks";
787  $socialkey = array_search("socialnetworks", $listfields); // Return position of 'socialnetworks' key in array. Example socialkey=19
788  $listvalues[$socialkey] = '';
789  }
790  if (!empty($newval) && $arrayrecord[($key)]['type'] > 0) {
791  $socialkey = array_search("socialnetworks", $listfields); // Return position of 'socialnetworks' key in array. Example socialkey=19
792  $socialnetwork = explode("_", $fieldname)[1];
793  if (empty($listvalues[$socialkey]) || $listvalues[$socialkey] == "null") {
794  $json = new stdClass();
795  $json->$socialnetwork = $newval;
796  $listvalues[$socialkey] = json_encode($json);
797  } else {
798  $jsondata = $listvalues[$socialkey];
799  $json = json_decode($jsondata);
800  $json->$socialnetwork = $newval;
801  $listvalues[$socialkey] = json_encode($json);
802  }
803  }
804  } else {
805  $listfields[] = $fieldname;
806 
807  // Note: arrayrecord (and 'type') is filled with ->import_read_record called by import.php page before calling import_insert
808  if (empty($newval) && $arrayrecord[($key)]['type'] < 0) {
809  $listvalues[] = ($newval == '0' ? $newval : "null");
810  } elseif (empty($newval) && $arrayrecord[($key)]['type'] == 0) {
811  $listvalues[] = "''";
812  } else {
813  $listvalues[] = "'".$this->db->escape($newval)."'";
814  }
815  }
816  }
817  $i++;
818  }
819 
820  // We add hidden fields (but only if there is at least one field to add into table)
821  // We process here all the fields that were declared into the array $this->import_fieldshidden_array of the descriptor file.
822  // Previously we processed the ->import_fields_array.
823  if (!empty($listfields) && is_array($objimport->array_import_fieldshidden[0])) {
824  // Loop on each hidden fields to add them into listfields/listvalues
825  foreach ($objimport->array_import_fieldshidden[0] as $key => $val) {
826  if (!preg_match('/^'.preg_quote($alias, '/').'\./', $key)) {
827  continue; // Not a field of current table
828  }
829  if ($val == 'user->id') {
830  $listfields[] = preg_replace('/^' . preg_quote($alias, '/') . '\./', '', $key);
831  $listvalues[] = ((int) $user->id);
832  } elseif (preg_match('/^lastrowid-/', $val)) {
833  $tmp = explode('-', $val);
834  $lastinsertid = (isset($last_insert_id_array[$tmp[1]])) ? $last_insert_id_array[$tmp[1]] : 0;
835  $keyfield = preg_replace('/^' . preg_quote($alias, '/') . '\./', '', $key);
836  $listfields[] = $keyfield;
837  $listvalues[] = $lastinsertid;
838  //print $key."-".$val."-".$listfields."-".$listvalues."<br>";exit;
839  } elseif (preg_match('/^const-/', $val)) {
840  $tmp = explode('-', $val, 2);
841  $listfields[] = preg_replace('/^' . preg_quote($alias, '/') . '\./', '', $key);
842  $listvalues[] = "'".$this->db->escape($tmp[1])."'";
843  } elseif (preg_match('/^rule-/', $val)) {
844  $fieldname = $key;
845  if (!empty($objimport->array_import_convertvalue[0][$fieldname])) {
846  if ($objimport->array_import_convertvalue[0][$fieldname]['rule'] == 'compute') {
847  $file = (empty($objimport->array_import_convertvalue[0][$fieldname]['classfile']) ? $objimport->array_import_convertvalue[0][$fieldname]['file'] : $objimport->array_import_convertvalue[0][$fieldname]['classfile']);
848  $class = $objimport->array_import_convertvalue[0][$fieldname]['class'];
849  $method = $objimport->array_import_convertvalue[0][$fieldname]['method'];
850  $resultload = dol_include_once($file);
851  if (empty($resultload)) {
852  dol_print_error('', 'Error trying to call file=' . $file . ', class=' . $class . ', method=' . $method);
853  break;
854  }
855  $classinstance = new $class($this->db);
856  $res = call_user_func_array(array($classinstance, $method), array(&$arrayrecord, $listfields, $key));
857  $fieldArr = explode('.', $fieldname);
858  if (count($fieldArr) > 0) {
859  $fieldname = $fieldArr[1];
860  }
861  $listfields[] = $fieldname;
862  $listvalues[] = $res;
863  }
864  }
865  } else {
866  $this->errors[$error]['lib'] = 'Bad value of profile setup ' . $val . ' for array_import_fieldshidden';
867  $this->errors[$error]['type'] = 'Import profile setup';
868  $error++;
869  }
870  }
871  }
872  //print 'listfields='.$listfields.'<br>listvalues='.$listvalues.'<br>';
873 
874  // If no error for this $alias/$tablename, we have a complete $listfields and $listvalues that are defined
875  // so we can try to make the insert or update now.
876  if (!$errorforthistable) {
877  //print "$alias/$tablename/$listfields/$listvalues<br>";
878  if (!empty($listfields)) {
879  $updatedone = false;
880  $insertdone = false;
881 
882  $is_table_category_link = false;
883  $fname = 'rowid';
884  if (strpos($tablename, '_categorie_') !== false) {
885  $is_table_category_link = true;
886  $fname='*';
887  }
888 
889  if (!empty($updatekeys)) {
890  // We do SELECT to get the rowid, if we already have the rowid, it's to be used below for related tables (extrafields)
891 
892  if (empty($lastinsertid)) { // No insert done yet for a parent table
893  $sqlSelect = "SELECT ".$fname." FROM " . $tablename;
894 
895  $data = array_combine($listfields, $listvalues);
896 
897  $where = array(); // filters to forge SQL request
898  $filters = array(); // filters to forge output error message
899  foreach ($updatekeys as $key) {
900  $col = $objimport->array_import_updatekeys[0][$key];
901  $key = preg_replace('/^.*\./i', '', $key);
902  if (isModEnabled("socialnetworks") && strpos($key, "socialnetworks") !== false) {
903  $tmp = explode("_", $key);
904  $key = $tmp[0];
905  $socialnetwork = $tmp[1];
906  $jsondata = $data[$key];
907  $json = json_decode($jsondata);
908  $stringtosearch = json_encode($socialnetwork).':'.json_encode($json->$socialnetwork);
909  //var_dump($stringtosearch);
910  //var_dump($this->db->escape($stringtosearch)); // This provide a value for sql string (but not for a like)
911  $where[] = $key." LIKE '%".$this->db->escape($this->db->escapeforlike($stringtosearch))."%'";
912  $filters[] = $col." LIKE '%".$this->db->escape($this->db->escapeforlike($stringtosearch))."%'";
913  //var_dump($where[1]); // This provide a value for sql string inside a like
914  } else {
915  $where[] = $key.' = '.$data[$key];
916  $filters[] = $col.' = '.$data[$key];
917  }
918  }
919  $sqlSelect .= " WHERE " . implode(' AND ', $where);
920 
921  $resql = $this->db->query($sqlSelect);
922  if ($resql) {
923  $num_rows = $this->db->num_rows($resql);
924  if ($num_rows == 1) {
925  $res = $this->db->fetch_object($resql);
926  $lastinsertid = $res->rowid;
927  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
928  $last_insert_id_array[$tablename] = $lastinsertid;
929  } elseif ($num_rows > 1) {
930  $this->errors[$error]['lib'] = $langs->trans('MultipleRecordFoundWithTheseFilters', implode(', ', $filters));
931  $this->errors[$error]['type'] = 'SQL';
932  $error++;
933  } else {
934  // No record found with filters, insert will be tried below
935  }
936  } else {
937  //print 'E';
938  $this->errors[$error]['lib'] = $this->db->lasterror();
939  $this->errors[$error]['type'] = 'SQL';
940  $error++;
941  }
942  } else {
943  // We have a last INSERT ID (got by previous pass), so we check if we have a row referencing this foreign key.
944  // This is required when updating table with some extrafields. When inserting a record in parent table, we can make
945  // a direct insert into subtable extrafields, but when me wake an update, the insertid is defined and the child record
946  // may already exists. So we rescan the extrafield table to know if record exists or not for the rowid.
947  // Note: For extrafield tablename, we have in importfieldshidden_array an enty 'extra.fk_object'=>'lastrowid-tableparent' so $keyfield is 'fk_object'
948  $sqlSelect = "SELECT rowid FROM " . $tablename;
949 
950  if (empty($keyfield)) {
951  $keyfield = 'rowid';
952  }
953  $sqlSelect .= " WHERE ".$keyfield." = ".((int) $lastinsertid);
954 
955  $resql = $this->db->query($sqlSelect);
956  if ($resql) {
957  $res = $this->db->fetch_object($resql);
958  if ($this->db->num_rows($resql) == 1) {
959  // We have a row referencing this last foreign key, continue with UPDATE.
960  } else {
961  // No record found referencing this last foreign key,
962  // force $lastinsertid to 0 so we INSERT below.
963  $lastinsertid = 0;
964  }
965  } else {
966  //print 'E';
967  $this->errors[$error]['lib'] = $this->db->lasterror();
968  $this->errors[$error]['type'] = 'SQL';
969  $error++;
970  }
971  }
972 
973  if (!empty($lastinsertid)) {
974  // We db escape social network field because he isn't in field creation
975  if (in_array("socialnetworks", $listfields)) {
976  $socialkey = array_search("socialnetworks", $listfields);
977  $tmpsql = $listvalues[$socialkey];
978  $listvalues[$socialkey] = "'".$this->db->escape($tmpsql)."'";
979  }
980 
981  // Build SQL UPDATE request
982  $sqlstart = "UPDATE " . $tablename;
983 
984  $data = array_combine($listfields, $listvalues);
985  $set = array();
986  foreach ($data as $key => $val) {
987  $set[] = $key." = ".$val;
988  }
989  $sqlstart .= " SET " . implode(', ', $set);
990 
991  if (empty($keyfield)) {
992  $keyfield = 'rowid';
993  }
994  $sqlend = " WHERE " . $keyfield . " = ".((int) $lastinsertid);
995 
996  if ($is_table_category_link) {
997  $sqlend = " WHERE " . implode(' AND ', $where);
998  }
999 
1000  $sql = $sqlstart . $sqlend;
1001 
1002  // Run update request
1003  $resql = $this->db->query($sql);
1004  if ($resql) {
1005  // No error, update has been done. $this->db->db->affected_rows can be 0 if data hasn't changed
1006  $updatedone = true;
1007  } else {
1008  //print 'E';
1009  $this->errors[$error]['lib'] = $this->db->lasterror();
1010  $this->errors[$error]['type'] = 'SQL';
1011  $error++;
1012  }
1013  }
1014  }
1015 
1016  // Update not done, we do insert
1017  if (!$error && !$updatedone) {
1018  // We db escape social network field because he isn't in field creation
1019  if (in_array("socialnetworks", $listfields)) {
1020  $socialkey = array_search("socialnetworks", $listfields);
1021  $tmpsql = $listvalues[$socialkey];
1022  $listvalues[$socialkey] = "'".$this->db->escape($tmpsql)."'";
1023  }
1024 
1025  // Build SQL INSERT request
1026  $sqlstart = "INSERT INTO " . $tablename . "(" . implode(", ", $listfields) . ", import_key";
1027  $sqlend = ") VALUES(" . implode(', ', $listvalues) . ", '" . $this->db->escape($importid) . "'";
1028  if (!empty($tablewithentity_cache[$tablename])) {
1029  $sqlstart .= ", entity";
1030  $sqlend .= ", " . $conf->entity;
1031  }
1032  if (!empty($objimport->array_import_tables_creator[0][$alias])) {
1033  $sqlstart .= ", " . $objimport->array_import_tables_creator[0][$alias];
1034  $sqlend .= ", " . $user->id;
1035  }
1036  $sql = $sqlstart . $sqlend . ")";
1037  //dol_syslog("import_xlsx.modules", LOG_DEBUG);
1038 
1039  // Run insert request
1040  if ($sql) {
1041  $resql = $this->db->query($sql);
1042  if ($resql) {
1043  $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).
1044  $insertdone = true;
1045  } else {
1046  //print 'E';
1047  $this->errors[$error]['lib'] = $this->db->lasterror();
1048  $this->errors[$error]['type'] = 'SQL';
1049  $error++;
1050  }
1051  }
1052  }
1053  }
1054  /*else
1055  {
1056  dol_print_error('','ErrorFieldListEmptyFor '.$alias."/".$tablename);
1057  }*/
1058  }
1059 
1060  if ($error) {
1061  break;
1062  }
1063  }
1064 
1065  if ($updatedone) {
1066  $this->nbupdate++;
1067  }
1068  if ($insertdone) {
1069  $this->nbinsert++;
1070  }
1071  }
1072 
1073  return 1;
1074  }
1075 }
Societe
Class to manage third parties objects (customers, suppliers, prospects...)
Definition: societe.class.php:48
db
$conf db
API class for accounts.
Definition: inc.php:41
dol_trunc
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.
Definition: functions.lib.php:3805
ImportXlsx\write_footer_example
write_footer_example($outputlangs)
Output footer of an example file for this format.
Definition: import_xlsx.modules.php:219
ImportXlsx\import_read_header
import_read_header()
Input header line from file.
Definition: import_xlsx.modules.php:288
dol_print_error
dol_print_error($db='', $error='', $errors=null)
Displays error message system with all the information to facilitate the diagnosis and the escalation...
Definition: functions.lib.php:4844
ImportXlsx\import_get_nb_of_lines
import_get_nb_of_lines($file)
Return nb of records.
Definition: import_xlsx.modules.php:267
dol_include_once
if(!function_exists('dol_getprefix')) dol_include_once($relpath, $classname='')
Make an include_once using default root and alternate root if it fails.
Definition: functions.lib.php:1033
ImportXlsx\import_read_record
import_read_record()
Return array of next record in input file.
Definition: import_xlsx.modules.php:309
price2num
price2num($amount, $rounding='', $option=0)
Function that return a number with universal decimal format (decimal separator is '.
Definition: functions.lib.php:5661
ImportXlsx\write_header_example
write_header_example($outputlangs)
Output header of an example file for this format.
Definition: import_xlsx.modules.php:148
num2Alpha
num2Alpha($n)
Return a numeric value into an Excel like column number.
Definition: functions.lib.php:236
ModeleImports
Parent class for import file readers.
Definition: modules_import.php:31
dol_syslog
dol_syslog($message, $level=LOG_INFO, $ident=0, $suffixinfilename='', $restricttologhandler='', $logcontext=null)
Write log message into outputs.
Definition: functions.lib.php:1603
ImportXlsx\write_record_example
write_record_example($outputlangs, $contentlinevalues)
Output record of an example file for this format.
Definition: import_xlsx.modules.php:199
dol_strlen
dol_strlen($string, $stringencoding='UTF-8')
Make a strlen call.
Definition: functions.lib.php:3747
isModEnabled
isModEnabled($module)
Is Dolibarr module enabled.
Definition: functions.lib.php:105
testSqlAndScriptInject
testSqlAndScriptInject($val, $type)
Security: WAF layer for SQL Injection and XSS Injection (scripts) protection (Filters on GET,...
Definition: main.inc.php:87
ImportXlsx\import_close_file
import_close_file()
Close file handle.
Definition: import_xlsx.modules.php:337
$resql
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
ImportXlsx\import_insert
import_insert($arrayrecord, $array_match_file_to_database, $objimport, $maxfields, $importid, $updatekeys)
Insert a record into database.
Definition: import_xlsx.modules.php:358
ImportXlsx\__construct
__construct($db, $datatoimport)
Constructor.
Definition: import_xlsx.modules.php:106
ImportXlsx\import_open_file
import_open_file($file)
Open input file.
Definition: import_xlsx.modules.php:243
ImportXlsx
Class to import Excel files.
Definition: import_xlsx.modules.php:38
ImportXlsx\write_title_example
write_title_example($outputlangs, $headerlinefields)
Output title line of an example file for this format.
Definition: import_xlsx.modules.php:173
dol_stringtotime
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