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