Completed
Pull Request — master (#25)
by
unknown
02:59
created

Standard::getSupplierMap()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 12
Code Lines 6

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 1 Features 0
Metric Value
cc 2
eloc 6
c 1
b 1
f 0
nc 2
nop 1
dl 0
loc 12
rs 10
1
<?php
2
3
/**
4
 * @license LGPLv3, http://opensource.org/licenses/LGPL-3.0
5
 * @copyright Aimeos (aimeos.org), 2018-2020
6
 * @package Controller
7
 * @subpackage Jobs
8
 */
9
10
11
namespace Aimeos\Controller\Jobs\Supplier\Import\Csv;
12
13
14
/**
15
 * Job controller for CSV supplier imports.
16
 *
17
 * @package Controller
18
 * @subpackage Jobs
19
 */
20
class Standard
21
	extends \Aimeos\Controller\Common\Supplier\Import\Csv\Base
22
	implements \Aimeos\Controller\Jobs\Iface
23
{
24
	/**
25
	 * Returns the localized name of the job.
26
	 *
27
	 * @return string Name of the job
28
	 */
29
	public function getName() : string
30
	{
31
		return $this->getContext()->getI18n()->dt( 'controller/jobs', 'Supplier import CSV' );
32
	}
33
34
35
	/**
36
	 * Returns the localized description of the job.
37
	 *
38
	 * @return string Description of the job
39
	 */
40
	public function getDescription() : string
41
	{
42
		return $this->getContext()->getI18n()->dt( 'controller/jobs', 'Imports new and updates existing suppliers from CSV files' );
43
	}
44
45
46
	/**
47
	 * Executes the job.
48
	 *
49
	 * @throws \Aimeos\Controller\Jobs\Exception If an error occurs
50
	 */
51
	public function run()
52
	{
53
		$total = $errors = 0;
54
		$context = $this->getContext();
55
		$config = $context->getConfig();
56
		$logger = $context->getLogger();
57
		$domains = array( 'media', 'text', 'supplier/address' );
58
		$mappings = $this->getDefaultMapping();
59
60
61
		if( file_exists( $config->get( 'controller/jobs/supplier/import/csv/location' ) ) === false )
62
		{
63
			return;
64
		}
65
66
67
		/** controller/common/supplier/import/csv/domains
68
		 * List of item domain names that should be retrieved along with the supplier items
69
		 *
70
		 * For efficient processing, the items associated to the suppliers can be
71
		 * fetched to, minimizing the number of database queries required. To be
72
		 * most effective, the list of item domain names should be used in the
73
		 * mapping configuration too, so the retrieved items will be used during
74
		 * the import.
75
		 *
76
		 * @param array Associative list of MShop item domain names
77
		 * @since 2020.07
78
		 * @category Developer
79
		 * @see controller/common/supplier/import/csv/mapping
80
		 * @see controller/common/supplier/import/csv/converter
81
		 * @see controller/common/supplier/import/csv/max-size
82
		 */
83
		$domains = $config->get( 'controller/common/supplier/import/csv/domains', $domains );
84
85
		/** controller/jobs/supplier/import/csv/domains
86
		 * List of item domain names that should be retrieved along with the supplier items
87
		 *
88
		 * This configuration setting overwrites the shared option
89
		 * "controller/common/supplier/import/csv/domains" if you need a
90
		 * specific setting for the job controller. Otherwise, you should
91
		 * use the shared option for consistency.
92
		 *
93
		 * @param array Associative list of MShop item domain names
94
		 * @since 2020.07
95
		 * @category Developer
96
		 * @see controller/jobs/supplier/import/csv/mapping
97
		 * @see controller/jobs/supplier/import/csv/skip-lines
98
		 * @see controller/jobs/supplier/import/csv/converter
99
		 * @see controller/jobs/supplier/import/csv/strict
100
		 * @see controller/jobs/supplier/import/csv/backup
101
		 * @see controller/common/supplier/import/csv/max-size
102
		 */
103
		$domains = $config->get( 'controller/jobs/supplier/import/csv/domains', $domains );
104
105
106
		/** controller/common/supplier/import/csv/mapping
107
		 * List of mappings between the position in the CSV file and item keys
108
		 *
109
		 * The importer have to know which data is at which position in the CSV
110
		 * file. Therefore, you need to specify a mapping between each position
111
		 * and the MShop domain item key (e.g. "supplier.code") it represents.
112
		 *
113
		 * You can use all domain item keys which are used in the fromArray()
114
		 * methods of the item classes.
115
		 *
116
		 * These mappings are grouped together by their processor names, which
117
		 * are responsible for importing the data, e.g. all mappings in "item"
118
		 * will be processed by the base supplier importer while the mappings in
119
		 * "text" will be imported by the text processor.
120
		 *
121
		 * @param array Associative list of processor names and lists of key/position pairs
122
		 * @since 2020.07
123
		 * @category Developer
124
		 * @see controller/common/supplier/import/csv/domains
125
		 * @see controller/common/supplier/import/csv/converter
126
		 * @see controller/common/supplier/import/csv/max-size
127
		 */
128
		$mappings = $config->get( 'controller/common/supplier/import/csv/mapping', $mappings );
129
130
		/** controller/jobs/supplier/import/csv/mapping
131
		 * List of mappings between the position in the CSV file and item keys
132
		 *
133
		 * This configuration setting overwrites the shared option
134
		 * "controller/common/supplier/import/csv/mapping" if you need a
135
		 * specific setting for the job controller. Otherwise, you should
136
		 * use the shared option for consistency.
137
		 *
138
		 * @param array Associative list of processor names and lists of key/position pairs
139
		 * @since 2020.07
140
		 * @category Developer
141
		 * @see controller/jobs/supplier/import/csv/domains
142
		 * @see controller/jobs/supplier/import/csv/skip-lines
143
		 * @see controller/jobs/supplier/import/csv/converter
144
		 * @see controller/jobs/supplier/import/csv/strict
145
		 * @see controller/jobs/supplier/import/csv/backup
146
		 * @see controller/common/supplier/import/csv/max-size
147
		 */
148
		$mappings = $config->get( 'controller/jobs/supplier/import/csv/mapping', $mappings );
149
150
151
		/** controller/common/supplier/import/csv/converter
152
		 * List of converter names for the values at the position in the CSV file
153
		 *
154
		 * Not all data in the CSV file is already in the required format. Maybe
155
		 * the text encoding isn't UTF-8, the date is not in ISO format or something
156
		 * similar. In order to convert the data before it's imported, you can
157
		 * specify a list of converter objects that should be applied to the data
158
		 * from the CSV file.
159
		 *
160
		 * To each field in the CSV file, you can apply one or more converters,
161
		 * e.g. to encode a Latin text to UTF8 for the second CSV field:
162
		 *
163
		 *  array( 1 => 'Text/LatinUTF8' )
164
		 *
165
		 * Similarly, you can also apply several converters at once to the same
166
		 * field:
167
		 *
168
		 *  array( 1 => array( 'Text/LatinUTF8', 'DateTime/EnglishISO' ) )
169
		 *
170
		 * It would convert the data of the second CSV field first to UTF-8 and
171
		 * afterwards try to translate it to an ISO date format.
172
		 *
173
		 * The available converter objects are named "\Aimeos\MW\Convert\<type>_<conversion>"
174
		 * where <type> is the data type and <conversion> the way of the conversion.
175
		 * In the configuration, the type and conversion must be separated by a
176
		 * slash (<type>/<conversion>).
177
		 *
178
		 * '''Note:''' Keep in mind that the position of the CSV fields start at
179
		 * zero (0). If you only need to convert a few fields, you don't have to
180
		 * configure all fields. Only specify the positions in the array you
181
		 * really need!
182
		 *
183
		 * @param array Associative list of position/converter name (or list of names) pairs
184
		 * @since 2020.07
185
		 * @category Developer
186
		 * @see controller/common/supplier/import/csv/domains
187
		 * @see controller/common/supplier/import/csv/mapping
188
		 * @see controller/common/supplier/import/csv/max-size
189
		 */
190
		$converters = $config->get( 'controller/common/supplier/import/csv/converter', [] );
191
192
		/** controller/jobs/supplier/import/csv/converter
193
		 * List of converter names for the values at the position in the CSV file
194
		 *
195
		 * This configuration setting overwrites the shared option
196
		 * "controller/common/supplier/import/csv/converter" if you need a
197
		 * specific setting for the job controller. Otherwise, you should
198
		 * use the shared option for consistency.
199
		 *
200
		 * @param array Associative list of position/converter name (or list of names) pairs
201
		 * @since 2020.07
202
		 * @category Developer
203
		 * @see controller/jobs/supplier/import/csv/domains
204
		 * @see controller/jobs/supplier/import/csv/mapping
205
		 * @see controller/jobs/supplier/import/csv/skip-lines
206
		 * @see controller/jobs/supplier/import/csv/strict
207
		 * @see controller/jobs/supplier/import/csv/backup
208
		 * @see controller/common/supplier/import/csv/max-size
209
		 */
210
		$converters = $config->get( 'controller/jobs/supplier/import/csv/converter', $converters );
211
212
213
		/** controller/common/supplier/import/csv/max-size
214
		 * Maximum number of CSV rows to import at once
215
		 *
216
		 * It's more efficient to read and import more than one row at a time
217
		 * to speed up the import. Usually, the bigger the chunk that is imported
218
		 * at once, the less time the importer will need. The downside is that
219
		 * the amount of memory required by the import process will increase as
220
		 * well. Therefore, it's a trade-off between memory consumption and
221
		 * import speed.
222
		 *
223
		 * @param integer Number of rows
224
		 * @since 2020.07
225
		 * @category Developer
226
		 * @see controller/common/supplier/import/csv/domains
227
		 * @see controller/common/supplier/import/csv/mapping
228
		 * @see controller/common/supplier/import/csv/converter
229
		 */
230
		$maxcnt = (int)$config->get( 'controller/common/supplier/import/csv/max-size', 1000 );
231
232
233
		/** controller/jobs/supplier/import/csv/skip-lines
234
		 * Number of rows skipped in front of each CSV files
235
		 *
236
		 * Some CSV files contain header information describing the content of
237
		 * the column values. These data is for informational purpose only and
238
		 * can't be imported into the database. Using this option, you can
239
		 * define the number of lines that should be left out before the import
240
		 * begins.
241
		 *
242
		 * @param integer Number of rows
243
		 * @since 2020.07
244
		 * @category Developer
245
		 * @see controller/jobs/supplier/import/csv/domains
246
		 * @see controller/jobs/supplier/import/csv/mapping
247
		 * @see controller/jobs/supplier/import/csv/converter
248
		 * @see controller/jobs/supplier/import/csv/strict
249
		 * @see controller/jobs/supplier/import/csv/backup
250
		 * @see controller/common/supplier/import/csv/max-size
251
		 */
252
		$skiplines = (int)$config->get( 'controller/jobs/supplier/import/csv/skip-lines', 0 );
253
254
255
		/** controller/jobs/supplier/import/csv/strict
256
		 * Log all columns from the file that are not mapped and therefore not imported
257
		 *
258
		 * Depending on the mapping, there can be more columns in the CSV file
259
		 * than those which will be imported. This can be by purpose if you want
260
		 * to import only selected columns or if you've missed to configure one
261
		 * or more columns. This configuration option will log all columns that
262
		 * have not been imported if set to true. Otherwise, the left over fields
263
		 * in the imported line will be silently ignored.
264
		 *
265
		 * @param boolen True if not imported columns should be logged, false if not
266
		 * @since 2020.07
267
		 * @category User
268
		 * @category Developer
269
		 * @see controller/jobs/supplier/import/csv/domains
270
		 * @see controller/jobs/supplier/import/csv/mapping
271
		 * @see controller/jobs/supplier/import/csv/skip-lines
272
		 * @see controller/jobs/supplier/import/csv/converter
273
		 * @see controller/jobs/supplier/import/csv/backup
274
		 * @see controller/common/supplier/import/csv/max-size
275
		 */
276
		$strict = (bool)$config->get( 'controller/jobs/supplier/import/csv/strict', true );
277
278
279
		/** controller/jobs/supplier/import/csv/backup
280
		 * Name of the backup for sucessfully imported files
281
		 *
282
		 * After a CSV file was imported successfully, you can move it to another
283
		 * location, so it won't be imported again and isn't overwritten by the
284
		 * next file that is stored at the same location in the file system.
285
		 *
286
		 * You should use an absolute path to be sure but can be relative path
287
		 * if you absolutely know from where the job will be executed from. The
288
		 * name of the new backup location can contain placeholders understood
289
		 * by the PHP strftime() function to create dynamic paths, e.g. "backup/%Y-%m-%d"
290
		 * which would create "backup/2000-01-01". For more information about the
291
		 * strftime() placeholders, please have a look into the PHP documentation of
292
		 * the {@link http://php.net/manual/en/function.strftime.php strftime() function}.
293
		 *
294
		 * '''Note:''' If no backup name is configured, the file or directory
295
		 * won't be moved away. Please make also sure that the parent directory
296
		 * and the new directory are writable so the file or directory could be
297
		 * moved.
298
		 *
299
		 * @param integer Name of the backup file, optionally with date/time placeholders
300
		 * @since 2020.07
301
		 * @category Developer
302
		 * @see controller/jobs/supplier/import/csv/domains
303
		 * @see controller/jobs/supplier/import/csv/mapping
304
		 * @see controller/jobs/supplier/import/csv/skip-lines
305
		 * @see controller/jobs/supplier/import/csv/converter
306
		 * @see controller/jobs/supplier/import/csv/strict
307
		 * @see controller/common/supplier/import/csv/max-size
308
		 */
309
		$backup = $config->get( 'controller/jobs/supplier/import/csv/backup' );
310
311
312
		if( !isset( $mappings['item'] ) || !is_array( $mappings['item'] ) )
313
		{
314
			$msg = sprintf( 'Required mapping key "%1$s" is missing or contains no array', 'item' );
315
			throw new \Aimeos\Controller\Jobs\Exception( $msg );
316
		}
317
318
		try
319
		{
320
			$procMappings = $mappings;
321
			unset( $procMappings['item'] );
322
323
			$codePos = $this->getCodePosition( $mappings['item'] );
324
			$convlist = $this->getConverterList( $converters );
325
			$processor = $this->getProcessors( $procMappings );
326
			$supplierMap = $this->getSupplierMap( $domains );
327
			$container = $this->getContainer();
328
			$path = $container->getName();
329
330
331
			$msg = sprintf( 'Started supplier import from "%1$s" (%2$s)', $path, __CLASS__ );
332
			$logger->log( $msg, \Aimeos\MW\Logger\Base::NOTICE );
333
334
			foreach( $container as $content )
335
			{
336
				$name = $content->getName();
337
338
				for( $i = 0; $i < $skiplines; $i++ )
339
				{
340
					$content->next();
341
				}
342
343
				while( ($data = $this->getData( $content, $maxcnt, $codePos )) !== [] )
344
				{
345
					$data = $this->convertData( $convlist, $data );
346
					$errcnt = $this->import( $supplierMap, $data, $mappings['item'], $processor, $strict );
347
					$chunkcnt = count( $data );
348
349
					$msg = 'Imported supplier lines from "%1$s": %2$d/%3$d (%4$s)';
350
					$logger->log( sprintf( $msg, $name, $chunkcnt - $errcnt, $chunkcnt, __CLASS__ ), \Aimeos\MW\Logger\Base::NOTICE );
351
352
					$errors += $errcnt;
353
					$total += $chunkcnt;
354
					unset( $data );
355
				}
356
			}
357
358
			$container->close();
359
		} catch( \Exception $e )
360
		{
361
			$logger->log( 'Supplier import error: ' . $e->getMessage() . "\n" . $e->getTraceAsString() );
362
			$this->mail( 'Supplier CSV import error', $e->getMessage() . "\n" . $e->getTraceAsString() );
363
			throw new \Aimeos\Controller\Jobs\Exception( $e->getMessage() );
364
		}
365
366
		$msg = 'Finished supplier import from "%1$s": %2$d successful, %3$s errors, %4$s total (%5$s)';
367
		$logger->log( sprintf( $msg, $path, $total - $errors, $errors, $total, __CLASS__ ), \Aimeos\MW\Logger\Base::NOTICE );
368
369
		if( $errors > 0 )
370
		{
371
			$msg = sprintf( 'Invalid supplier lines in "%1$s": %2$d/%3$d', $path, $errors, $total );
372
			$this->mail( 'Supplier CSV import error', $msg );
373
			throw new \Aimeos\Controller\Jobs\Exception( $msg );
374
		}
375
376
		if( !empty( $backup ) && @rename( $path, strftime( $backup ) ) === false )
377
		{
378
			$msg = sprintf( 'Unable to move imported file "%1$s" to "%2$s"', $path, strftime( $backup ) );
379
			throw new \Aimeos\Controller\Jobs\Exception( $msg );
380
		}
381
	}
382
383
384
	/**
385
	 * Returns the position of the "supplier.code" column from the supplier item mapping
386
	 *
387
	 * @param array $mapping Mapping of the "item" columns with position as key and code as value
388
	 * @return int Position of the "supplier.code" column
389
	 * @throws \Aimeos\Controller\Jobs\Exception If no mapping for "supplier.code" is found
390
	 */
391
	protected function getCodePosition( array $mapping ) : int
392
	{
393
		foreach( $mapping as $pos => $key )
394
		{
395
			if( $key === 'supplier.code' )
396
			{
397
				return $pos;
398
			}
399
		}
400
401
		throw new \Aimeos\Controller\Jobs\Exception( sprintf( 'No "supplier.code" column in CSV mapping found' ) );
402
	}
403
404
405
	/**
406
	 * Opens and returns the container which includes the supplier data
407
	 *
408
	 * @return \Aimeos\MW\Container\Iface Container object
409
	 */
410
	protected function getContainer() : \Aimeos\MW\Container\Iface
411
	{
412
		$config = $this->getContext()->getConfig();
413
414
		/** controller/jobs/supplier/import/csv/location
415
		 * File or directory where the content is stored which should be imported
416
		 *
417
		 * You need to configure the file or directory that acts as container
418
		 * for the CSV files that should be imported. It should be an absolute
419
		 * path to be sure but can be relative path if you absolutely know from
420
		 * where the job will be executed from.
421
		 *
422
		 * The path can point to any supported container format as long as the
423
		 * content is in CSV format, e.g.
424
		 * * Directory container / CSV file
425
		 * * Zip container / compressed CSV file
426
		 * * PHPExcel container / PHPExcel sheet
427
		 *
428
		 * @param string Absolute file or directory path
429
		 * @since 2020.07
430
		 * @category Developer
431
		 * @category User
432
		 * @see controller/jobs/supplier/import/csv/container/type
433
		 * @see controller/jobs/supplier/import/csv/container/content
434
		 * @see controller/jobs/supplier/import/csv/container/options
435
		 */
436
		$location = $config->get( 'controller/jobs/supplier/import/csv/location' );
437
438
		/** controller/jobs/supplier/import/csv/container/type
439
		 * Nave of the container type to read the data from
440
		 *
441
		 * The container type tells the importer how it should retrieve the data.
442
		 * There are currently three container types that support the necessary
443
		 * CSV content:
444
		 * * Directory
445
		 * * Zip
446
		 * * PHPExcel
447
		 *
448
		 * '''Note:''' For the PHPExcel container, you need to install the
449
		 * "ai-container" extension.
450
		 *
451
		 * @param string Container type name
452
		 * @since 2020.07
453
		 * @category Developer
454
		 * @category User
455
		 * @see controller/jobs/supplier/import/csv/location
456
		 * @see controller/jobs/supplier/import/csv/container/content
457
		 * @see controller/jobs/supplier/import/csv/container/options
458
		 */
459
		$container = $config->get( 'controller/jobs/supplier/import/csv/container/type', 'Directory' );
460
461
		/** controller/jobs/supplier/import/csv/container/content
462
		 * Name of the content type inside the container to read the data from
463
		 *
464
		 * The content type must always be a CSV-like format and there are
465
		 * currently two format types that are supported:
466
		 * * CSV
467
		 * * PHPExcel
468
		 *
469
		 * '''Note:''' for the PHPExcel content type, you need to install the
470
		 * "ai-container" extension.
471
		 *
472
		 * @param array Content type name
473
		 * @since 2020.07
474
		 * @category Developer
475
		 * @category User
476
		 * @see controller/jobs/supplier/import/csv/location
477
		 * @see controller/jobs/supplier/import/csv/container/type
478
		 * @see controller/jobs/supplier/import/csv/container/options
479
		 */
480
		$content = $config->get( 'controller/jobs/supplier/import/csv/container/content', 'CSV' );
481
482
		/** controller/jobs/supplier/import/csv/container/options
483
		 * List of file container options for the supplier import files
484
		 *
485
		 * Some container/content type allow you to hand over additional settings
486
		 * for configuration. Please have a look at the article about
487
		 * {@link http://aimeos.org/docs/Developers/Utility/Create_and_read_files container/content files}
488
		 * for more information.
489
		 *
490
		 * @param array Associative list of option name/value pairs
491
		 * @since 2020.07
492
		 * @category Developer
493
		 * @category User
494
		 * @see controller/jobs/supplier/import/csv/location
495
		 * @see controller/jobs/supplier/import/csv/container/content
496
		 * @see controller/jobs/supplier/import/csv/container/type
497
		 */
498
		$options = $config->get( 'controller/jobs/supplier/import/csv/container/options', [] );
499
500
		if( $location === null )
501
		{
502
			$msg = sprintf( 'Required configuration for "%1$s" is missing', 'controller/jobs/supplier/import/csv/location' );
503
			throw new \Aimeos\Controller\Jobs\Exception( $msg );
504
		}
505
506
		return \Aimeos\MW\Container\Factory::getContainer( $location, $container, $content, $options );
507
	}
508
509
510
	/**
511
	 * Returns the supplier items building the tree as list
512
	 *
513
	 * @param array $domains List of domain names whose items should be fetched too
514
	 * @return array Associative list of supplier codes as keys and items implementing \Aimeos\MShop\Supplier\Item\Iface as values
515
	 */
516
	protected function getSupplierMap( array $domains ) : array
517
	{
518
		$map = [];
519
		$manager = \Aimeos\MShop::create( $this->getContext(), 'supplier' );
520
		$search = $manager->createSearch()->setSlice( 0, 0x7fffffff );
521
522
		foreach( $manager->searchItems( $search, $domains ) as $item )
523
		{
524
			$map[$item->getCode()] = $item;
525
		}
526
527
		return $map;
528
	}
529
530
531
	/**
532
	 * Imports the CSV data and creates new suppliers or updates existing ones
533
	 *
534
	 * @param array &$supplierMap Associative list of supplier items with codes as keys and items implementing \Aimeos\MShop\Supplier\Item\Iface as values
535
	 * @param array $data Associative list of import data as index/value pairs
536
	 * @param array $mapping Associative list of positions and domain item keys
537
	 * @param \Aimeos\Controller\Common\Supplier\Import\Csv\Processor\Iface $processor Processor object
538
	 * @param bool $strict Log columns not mapped or silently ignore them
539
	 * @return int Number of suppliers that couldn't be imported
540
	 * @throws \Aimeos\Controller\Jobs\Exception
541
	 */
542
	protected function import( array &$supplierMap, array $data, array $mapping,
543
		\Aimeos\Controller\Common\Supplier\Import\Csv\Processor\Iface $processor, bool $strict ) : int
544
	{
545
		$errors = 0;
546
		$context = $this->getContext();
547
		$manager = \Aimeos\MShop::create( $context, 'supplier' );
548
549
		foreach( $data as $code => $list )
550
		{
551
			$manager->begin();
552
553
			try
554
			{
555
				$code = trim( $code );
556
557
				if( isset( $supplierMap[$code] ) )
558
				{
559
					$item = $supplierMap[$code];
560
				} else
561
				{
562
					$item = $manager->createItem();
563
				}
564
565
				$map = $this->getMappedChunk( $list, $mapping );
566
567
				if( isset( $map[0] ) )
568
				{
569
					$map = $map[0]; // there can only be one chunk for the base supplier data
570
					$item->fromArray( $map, true );
571
572
					$list = $processor->process( $item, $list );
573
					$supplierMap[$code] = $item;
574
575
					$manager->saveItem( $item );
0 ignored issues
show
Bug introduced by
The method saveItem() does not exist on Aimeos\MShop\Common\Manager\Iface. Did you maybe mean saveItems()? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

575
					$manager->/** @scrutinizer ignore-call */ 
576
               saveItem( $item );

This check looks for calls to methods that do not seem to exist on a given type. It looks for the method on the type itself as well as in inherited classes or implemented interfaces.

This is most likely a typographical error or the method has been renamed.

Loading history...
576
				}
577
578
				$manager->commit();
579
			} catch( \Exception $e )
580
			{
581
				$manager->rollback();
582
583
				$msg = sprintf( 'Unable to import supplier with code "%1$s": %2$s', $code, $e->getMessage() );
584
				$context->getLogger()->log( $msg );
585
586
				$errors++;
587
			}
588
589
			if( $strict && !empty( $list ) )
590
			{
591
				$context->getLogger()->log( 'Not imported: ' . print_r( $list, true ) );
592
			}
593
		}
594
595
		return $errors;
596
	}
597
}
598