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