From 05cf8af5c189a70d1d603335cb88d417eb20d548 Mon Sep 17 00:00:00 2001 From: Franck Allimant Date: Tue, 7 Jan 2025 19:30:52 +0100 Subject: [PATCH] Added checks to allow more robust import --- Config/module.xml | 2 +- Service/CsvParserService.php | 28 ++++++++++++++++++++++++- Service/CsvProductImporterService.php | 30 +++++++++++++++++++++++---- 3 files changed, 54 insertions(+), 6 deletions(-) diff --git a/Config/module.xml b/Config/module.xml index bfbd6d1..ebacc9a 100755 --- a/Config/module.xml +++ b/Config/module.xml @@ -7,7 +7,7 @@ CSV Importer - 1.0.10 + 1.0.11 Thelia info@thelia.net diff --git a/Service/CsvParserService.php b/Service/CsvParserService.php index 5c30134..ed75964 100644 --- a/Service/CsvParserService.php +++ b/Service/CsvParserService.php @@ -3,6 +3,7 @@ namespace CsvImporter\Service; use Symfony\Component\Yaml\Yaml; +use Thelia\Log\Tlog; class CsvParserService { @@ -27,7 +28,7 @@ public function mapToArray(array $productData): array } foreach ($this->mapping as $fieldName => $header) { $mappedData[$fieldName] = $productData[$header] ?? null; - } + } return $mappedData; @@ -50,4 +51,29 @@ private function getAttributeColumns(array $productData): array return $this->getColumns($productData, self::ATTRIBUTE_DISCRIMINATOR); } + /** + * Issue a warning for each missing column in a file header + * + * @param array $headers + * @return void + */ + public function checkHeaders(array $headers): void + { + foreach ($this->mapping as $fieldName => $headerLabel) { + if (! in_array($headerLabel, $headers, true)) { + Tlog::getInstance()->warning("Column \"$headerLabel\" not found, some data could be missing after import"); + } + } + + foreach ($headers as $headerLabel) { + if (!in_array($headerLabel, $this->mapping, true) + && + !str_starts_with($headerLabel, self::ATTRIBUTE_DISCRIMINATOR) + && + !str_starts_with($headerLabel, self::FEATURE_DISCRIMINATOR) + ) { + Tlog::getInstance()->warning("Found additional \"$headerLabel\" column. This column will be ignored."); + } + } + } } diff --git a/Service/CsvProductImporterService.php b/Service/CsvProductImporterService.php index fe35a12..ec94ff3 100644 --- a/Service/CsvProductImporterService.php +++ b/Service/CsvProductImporterService.php @@ -12,6 +12,7 @@ namespace CsvImporter\Service; +use Propel\Runtime\ActiveQuery\Criteria; use Propel\Runtime\Exception\PropelException; use Psr\EventDispatcher\EventDispatcherInterface; use Symfony\Component\Console\Output\OutputInterface; @@ -35,6 +36,7 @@ use Thelia\Core\Event\Template\TemplateAddFeatureEvent; use Thelia\Core\Event\Template\TemplateCreateEvent; use Thelia\Core\Event\TheliaEvents; +use Thelia\Exception\TheliaProcessException; use Thelia\Files\FileManager; use Thelia\Log\Tlog; use Thelia\Model\Attribute; @@ -169,6 +171,9 @@ public function importProductsFromCsv(string $filePath, string $basedir, Country $line = 0; $headers = fgetcsv($handle); + + $this->csvParser->checkHeaders($headers); + while (($data = fgetcsv($handle)) !== false) { $line++; @@ -358,6 +363,17 @@ private function findOrCreateProduct(array $productData, Country $country, strin Tlog::getInstance()->addInfo('Created product ' . $productData[self::TITLE_COLUMN]); } + // Si un produit avec la même ref existe, refuser la modification + if (ProductQuery::create() + ->filterById($product->getId(), Criteria::NOT_EQUAL) + ->filterByRef($productData[self::REF_COLUMN]) + ->count() > 0 + ) { + throw new TheliaProcessException( + 'Failed to import product ref. ' . $productData[self::REF_COLUMN] . ': a product with the same ref. already exists.' + ); + } + return $this->dispatchProductEvent(new ProductUpdateEvent($product->getId()), $productData, $locale, $category, $country); } @@ -377,8 +393,8 @@ private function dispatchProductEvent( ->setLocale($locale) ->setTitle($productData[self::TITLE_COLUMN]) ->setDefaultCategory($category->getId()) - ->setBasePrice($productData[self::PRICE_EXCL_TAX_COLUMN]) - ->setBaseWeight($productData[self::WEIGHT_COLUMN] ?? 0) + ->setBasePrice($this->cleanupNumber($productData[self::PRICE_EXCL_TAX_COLUMN])) + ->setBaseWeight($this->cleanupNumber($productData[self::WEIGHT_COLUMN] ?? 0)) ->setTaxRuleId($this->findOrCreateTax($productData, $country, $locale)->getId()) ->setVisible(1) ->setCurrencyId(1); @@ -449,9 +465,10 @@ private function createOrUpdateProductSaleElements(Product $product, array $prod } } $event = new ProductSaleElementUpdateEvent($product, $productSaleElement->getId()); - $event->setWeight($productData[self::WEIGHT_COLUMN]) + $event + ->setWeight($this->cleanupNumber($productData[self::WEIGHT_COLUMN])) ->setProductSaleElement($productSaleElement) - ->setPrice($productData[self::PRICE_EXCL_TAX_COLUMN]) + ->setPrice($this->cleanupNumber($productData[self::PRICE_EXCL_TAX_COLUMN])) ->setEanCode($productData[self::EAN_COLUMN]) ->setReference($productData[self::REF_COLUMN]) ->setCurrencyId(1) @@ -724,4 +741,9 @@ private function copyFile(string $filePath): string return $newFilePath; } + + protected function cleanupNumber(string $number) + { + return (float) preg_replace("/[^0-9.,]/", '', str_replace(',', '.', $number)); + } }