diff --git a/src/Command/ImportProjectCommand.php b/src/Command/ImportProjectCommand.php index 4582860d..292d3ad7 100644 --- a/src/Command/ImportProjectCommand.php +++ b/src/Command/ImportProjectCommand.php @@ -14,6 +14,7 @@ use App\Importer\ImporterService; use App\Importer\ImportNotFoundException; use App\Repository\TeamRepository; use App\Repository\UserRepository; +use App\Validator\ValidationFailedException; use Symfony\Component\Console\Command\Command; use Symfony\Component\Console\Helper\ProgressBar; use Symfony\Component\Console\Input\InputArgument; @@ -56,6 +57,8 @@ class ImportProjectCommand extends Command ->addOption('reader', null, InputOption::VALUE_REQUIRED, 'The reader to use (supported: csv, csv-semicolon)', 'csv') ->addOption('teamlead', null, InputOption::VALUE_REQUIRED, 'If you want to create empty teams for each project, give the username of the teamlead to be assigned') ->addOption('no-update', null, InputOption::VALUE_NONE, 'If you want to create new project, but not update existing ones') + ->addOption('date-format', null, InputOption::VALUE_REQUIRED, 'Date format for imports', 'Y-m-d') + ->addOption('timezone', null, InputOption::VALUE_REQUIRED, 'Timezone for imports', date_default_timezone_get()) ; } @@ -125,9 +128,17 @@ class ImportProjectCommand extends Command $progressBar = new ProgressBar($output, $amount); + $options = []; + if (null !== ($dateFormat = $input->getOption('date-format'))) { + $options['dateformat'] = $dateFormat; + } + if (null !== ($timezone = $input->getOption('timezone'))) { + $options['timezone'] = $timezone; + } + foreach ($records as $record) { try { - $projects[] = $importer->convertEntryToProject($record); + $projects[] = $importer->convertEntryToProject($record, $options); } catch (\Exception $ex) { $io->error(sprintf('Invalid row %s: %s', $row, $ex->getMessage())); $doImport = false; @@ -158,7 +169,6 @@ class ImportProjectCommand extends Command $progressBar = new ProgressBar($output, $amount); foreach ($projects as $project) { - $row++; $progressBar->advance(); try { if ($project->getCustomer()->getId() === null) { @@ -194,8 +204,16 @@ class ImportProjectCommand extends Command $this->teams->saveTeam($team); $createdTeams++; + } catch (ValidationFailedException $ex) { + $io->error(sprintf('Failed importing project "%s" with: %s', $project->getName(), $ex->getMessage())); + for ($i = 0; $i < $ex->getViolations()->count(); $i++) { + $violation = $ex->getViolations()->get($i); + $io->error(sprintf('Failed validating field "%s" with value "%s": %s', $violation->getPropertyPath(), $violation->getInvalidValue(), $violation->getMessage())); + } + + return 4; } catch (\Exception $ex) { - $io->error(sprintf('Failed importing project row %s with: %s', $row, $ex->getMessage())); + $io->error(sprintf('Failed importing project "%s" with: %s', $project->getName(), $ex->getMessage())); return 4; } diff --git a/src/Importer/AbstractProjectImporter.php b/src/Importer/AbstractProjectImporter.php index 90046a7f..bd33d056 100644 --- a/src/Importer/AbstractProjectImporter.php +++ b/src/Importer/AbstractProjectImporter.php @@ -39,11 +39,11 @@ abstract class AbstractProjectImporter implements ProjectImporterInterface return $this->customerService->findCustomerByName($name); } - public function convertEntryToProject(array $entry): Project + public function convertEntryToProject(array $entry, array $options = []): Project { $project = $this->findProject($entry); - $this->convertEntry($project, $entry); + $this->convertEntry($project, $entry, $options); return $project; } @@ -71,7 +71,7 @@ abstract class AbstractProjectImporter implements ProjectImporterInterface if ($customer->getId() !== $project->getCustomer()->getId()) { throw new \InvalidArgumentException( sprintf( - 'Customer mismatch for project %s with attached customer %s and new customer %s', + 'Customer mismatch for project "%s" with attached customer "%s" and new customer "%s"', $project->getName(), $project->getCustomer()->getName(), $customer->getName() @@ -84,7 +84,10 @@ abstract class AbstractProjectImporter implements ProjectImporterInterface private function findCustomer(string $customerName): Customer { - if (!\array_key_exists($customerName, $this->customerCache)) { + $customerName = trim($customerName); + $key = strtolower($customerName); + + if (!\array_key_exists($key, $this->customerCache)) { $customer = $this->findCustomerByName($customerName); if ($customer === null) { @@ -92,10 +95,10 @@ abstract class AbstractProjectImporter implements ProjectImporterInterface $customer->setName($customerName); } - $this->customerCache[$customerName] = $customer; + $this->customerCache[$key] = $customer; } - return $this->customerCache[$customerName]; + return $this->customerCache[$key]; } /** @@ -152,7 +155,8 @@ abstract class AbstractProjectImporter implements ProjectImporterInterface * * @param Project $project * @param array $entry - * @return mixed + * @param array $options + * @return void */ - abstract protected function convertEntry(Project $project, array $entry); + abstract protected function convertEntry(Project $project, array $entry, array $options = []): void; } diff --git a/src/Importer/DefaultProjectImporter.php b/src/Importer/DefaultProjectImporter.php index e01ccea6..fe5c2f98 100644 --- a/src/Importer/DefaultProjectImporter.php +++ b/src/Importer/DefaultProjectImporter.php @@ -14,7 +14,7 @@ use App\Entity\ProjectMeta; final class DefaultProjectImporter extends AbstractProjectImporter { - protected function convertEntry(Project $project, array $row) + protected function convertEntry(Project $project, array $row, array $options = []): void { foreach ($row as $name => $value) { switch (strtolower($name)) { @@ -33,7 +33,7 @@ final class DefaultProjectImporter extends AbstractProjectImporter case 'order-number': case 'order number': if (!empty($value)) { - $project->setOrderNumber($value); + $project->setOrderNumber(substr($value, 0, 20)); } break; @@ -42,8 +42,20 @@ final class DefaultProjectImporter extends AbstractProjectImporter case 'order date': if (!empty($value)) { $timezone = $project->getCustomer()->getTimezone(); + if (isset($options['timezone'])) { + $timezone = $options['timezone']; + } $timezone = new \DateTimeZone($timezone ?? date_default_timezone_get()); - $project->setOrderDate(new \DateTime($value, $timezone)); + if (isset($options['dateformat'])) { + $date = \DateTime::createFromFormat($options['dateformat'], $value, $timezone); + } else { + $date = new \DateTime($value, $timezone); + } + if ($date instanceof \DateTime) { + $project->setOrderDate($date); + } else { + throw new \InvalidArgumentException('Invalid order date: ' . $value); + } } break; @@ -84,7 +96,5 @@ final class DefaultProjectImporter extends AbstractProjectImporter break; } } - - return $project; } } diff --git a/src/Importer/ProjectImporterInterface.php b/src/Importer/ProjectImporterInterface.php index 16006ac3..aa47a80a 100644 --- a/src/Importer/ProjectImporterInterface.php +++ b/src/Importer/ProjectImporterInterface.php @@ -13,5 +13,13 @@ use App\Entity\Project; interface ProjectImporterInterface { - public function convertEntryToProject(array $entry): Project; + /** + * Convert an entry (key-value pairs) to a Project entity. + * Accepts an optional array of options (like "dateformat" or "timezone"). + * + * @param array $entry + * @param array $options + * @return Project + */ + public function convertEntryToProject(array $entry, array $options = []): Project; }