added project importer and grandtotal converter (#1468)
This commit is contained in:
121
src/Importer/AbstractCustomerImporter.php
Normal file
121
src/Importer/AbstractCustomerImporter.php
Normal file
@@ -0,0 +1,121 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Kimai time-tracking app.
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace App\Importer;
|
||||
|
||||
use App\Customer\CustomerService;
|
||||
use App\Entity\Customer;
|
||||
|
||||
abstract class AbstractCustomerImporter implements CustomerImporterInterface
|
||||
{
|
||||
private $customerService;
|
||||
|
||||
public function __construct(CustomerService $repository)
|
||||
{
|
||||
$this->customerService = $repository;
|
||||
}
|
||||
|
||||
protected function findCustomerByName(string $name): ?Customer
|
||||
{
|
||||
return $this->customerService->findCustomerByName($name);
|
||||
}
|
||||
|
||||
protected function findCustomerByNumber(string $number): ?Customer
|
||||
{
|
||||
return $this->customerService->findCustomerByNumber($number);
|
||||
}
|
||||
|
||||
public function convertEntryToCustomer(array $entry): Customer
|
||||
{
|
||||
$customer = $this->findCustomer($entry);
|
||||
|
||||
$this->mapEntryToCustomer($customer, $entry);
|
||||
|
||||
return $customer;
|
||||
}
|
||||
|
||||
protected function createNewCustomer(string $name): Customer
|
||||
{
|
||||
$customer = $this->customerService->createNewCustomer();
|
||||
$customer->setName(substr($name, 0, 149));
|
||||
|
||||
return $customer;
|
||||
}
|
||||
|
||||
protected function findCustomer(array $entry): ?Customer
|
||||
{
|
||||
$name = $this->findCustomerName($entry);
|
||||
$customer = $this->findCustomerByName($name);
|
||||
|
||||
if ($customer === null) {
|
||||
$number = $this->findCustomerNumber($entry);
|
||||
if ($number !== null) {
|
||||
$customer = $this->findCustomerByNumber($number);
|
||||
}
|
||||
}
|
||||
|
||||
if ($customer === null) {
|
||||
$customer = $this->createNewCustomer($name);
|
||||
}
|
||||
|
||||
return $customer;
|
||||
}
|
||||
|
||||
/**
|
||||
* Find the unique customer name inside $entry.
|
||||
*
|
||||
* @param array $entry
|
||||
* @return string
|
||||
* @throws UnsupportedFormatException
|
||||
*/
|
||||
protected function findCustomerName(array $entry): string
|
||||
{
|
||||
foreach ($entry as $name => $value) {
|
||||
switch (strtolower($name)) {
|
||||
case 'name':
|
||||
if (!empty($value)) {
|
||||
return $value;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
throw new UnsupportedFormatException('Missing customer name, expected in column: "Name"');
|
||||
}
|
||||
|
||||
/**
|
||||
* Find the unique customer number inside $entry.
|
||||
*
|
||||
* @param array $entry
|
||||
* @return string
|
||||
* @throws UnsupportedFormatException
|
||||
*/
|
||||
protected function findCustomerNumber(array $entry): ?string
|
||||
{
|
||||
foreach ($entry as $name => $value) {
|
||||
switch (strtolower($name)) {
|
||||
case 'number':
|
||||
case 'account':
|
||||
if (!empty($value)) {
|
||||
return $value;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Applies all supported values from $entry to $customer.
|
||||
*
|
||||
* @param Customer $customer
|
||||
* @param array $entry
|
||||
* @return mixed
|
||||
*/
|
||||
abstract protected function mapEntryToCustomer(Customer $customer, array $entry);
|
||||
}
|
||||
158
src/Importer/AbstractProjectImporter.php
Normal file
158
src/Importer/AbstractProjectImporter.php
Normal file
@@ -0,0 +1,158 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Kimai time-tracking app.
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace App\Importer;
|
||||
|
||||
use App\Customer\CustomerService;
|
||||
use App\Entity\Customer;
|
||||
use App\Entity\Project;
|
||||
use App\Project\ProjectService;
|
||||
|
||||
abstract class AbstractProjectImporter implements ProjectImporterInterface
|
||||
{
|
||||
private $projectService;
|
||||
private $customerService;
|
||||
/**
|
||||
* @var Customer[]
|
||||
*/
|
||||
private $customerCache = [];
|
||||
|
||||
public function __construct(ProjectService $projectService, CustomerService $customerService)
|
||||
{
|
||||
$this->projectService = $projectService;
|
||||
$this->customerService = $customerService;
|
||||
}
|
||||
|
||||
protected function findProjectByName(string $name): ?Project
|
||||
{
|
||||
return $this->projectService->findProjectByName($name);
|
||||
}
|
||||
|
||||
protected function findCustomerByName(string $name): ?Customer
|
||||
{
|
||||
return $this->customerService->findCustomerByName($name);
|
||||
}
|
||||
|
||||
public function convertEntryToProject(array $entry): Project
|
||||
{
|
||||
$project = $this->findProject($entry);
|
||||
|
||||
$this->convertEntry($project, $entry);
|
||||
|
||||
return $project;
|
||||
}
|
||||
|
||||
protected function createNewProject(Customer $customer, string $name): Project
|
||||
{
|
||||
$project = $this->projectService->createNewProject($customer);
|
||||
$project->setName($name);
|
||||
|
||||
return $project;
|
||||
}
|
||||
|
||||
protected function findProject(array $entry): ?Project
|
||||
{
|
||||
$name = $this->findCustomerName($entry);
|
||||
$customer = $this->findCustomer($name);
|
||||
|
||||
$name = $this->findProjectName($entry);
|
||||
$project = $this->findProjectByName($name);
|
||||
|
||||
if ($project === null) {
|
||||
$project = $this->createNewProject($customer, $name);
|
||||
}
|
||||
|
||||
if ($customer->getId() !== $project->getCustomer()->getId()) {
|
||||
throw new \InvalidArgumentException(
|
||||
sprintf(
|
||||
'Customer mismatch for project %s with attached customer %s and new customer %s',
|
||||
$project->getName(),
|
||||
$project->getCustomer()->getName(),
|
||||
$customer->getName()
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
return $project;
|
||||
}
|
||||
|
||||
private function findCustomer(string $customerName): Customer
|
||||
{
|
||||
if (!\array_key_exists($customerName, $this->customerCache)) {
|
||||
$customer = $this->findCustomerByName($customerName);
|
||||
|
||||
if ($customer === null) {
|
||||
$customer = $this->customerService->createNewCustomer();
|
||||
$customer->setName($customerName);
|
||||
}
|
||||
|
||||
$this->customerCache[$customerName] = $customer;
|
||||
}
|
||||
|
||||
return $this->customerCache[$customerName];
|
||||
}
|
||||
|
||||
/**
|
||||
* Find the unique project name inside $entry.
|
||||
*
|
||||
* @param array $entry
|
||||
* @return string
|
||||
* @throws UnsupportedFormatException
|
||||
*/
|
||||
protected function findProjectName(array $entry): string
|
||||
{
|
||||
foreach ($entry as $name => $value) {
|
||||
switch (strtolower($name)) {
|
||||
case 'project':
|
||||
case 'projectname':
|
||||
case 'project name':
|
||||
case 'project-name':
|
||||
case 'name':
|
||||
if (!empty($value)) {
|
||||
return substr($value, 0, 149);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
throw new UnsupportedFormatException('Missing project name, expected in one of the columns: "Name", "Project , "Project Name", "Project-Name", "ProjectName"');
|
||||
}
|
||||
|
||||
/**
|
||||
* Find the unique project name inside $entry.
|
||||
*
|
||||
* @param array $entry
|
||||
* @return string
|
||||
* @throws UnsupportedFormatException
|
||||
*/
|
||||
protected function findCustomerName(array $entry): string
|
||||
{
|
||||
foreach ($entry as $name => $value) {
|
||||
switch (strtolower($name)) {
|
||||
case 'customer':
|
||||
case 'customername':
|
||||
case 'customer-name':
|
||||
case 'customer name':
|
||||
if (!empty($value)) {
|
||||
return substr($value, 0, 149);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
throw new UnsupportedFormatException('Missing customer name, expected in one of the columns: "Customer", "Customer Name", "Customer-Name" or "CustomerName"');
|
||||
}
|
||||
|
||||
/**
|
||||
* Applies all supported values from $entry to $project.
|
||||
*
|
||||
* @param Project $project
|
||||
* @param array $entry
|
||||
* @return mixed
|
||||
*/
|
||||
abstract protected function convertEntry(Project $project, array $entry);
|
||||
}
|
||||
35
src/Importer/CsvReader.php
Normal file
35
src/Importer/CsvReader.php
Normal file
@@ -0,0 +1,35 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Kimai time-tracking app.
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace App\Importer;
|
||||
|
||||
use League\Csv\Reader;
|
||||
|
||||
final class CsvReader implements ImportReaderInterface
|
||||
{
|
||||
private $delimiter;
|
||||
|
||||
public function __construct(string $delimiter)
|
||||
{
|
||||
$this->delimiter = $delimiter;
|
||||
}
|
||||
|
||||
public function read(string $input): \Iterator
|
||||
{
|
||||
if (!is_readable($input)) {
|
||||
throw new ImportNotFoundException();
|
||||
}
|
||||
|
||||
$csv = Reader::createFromPath($input, 'r');
|
||||
$csv->setDelimiter($this->delimiter);
|
||||
$csv->setHeaderOffset(0);
|
||||
|
||||
return $csv->getRecords();
|
||||
}
|
||||
}
|
||||
17
src/Importer/CustomerImporterInterface.php
Normal file
17
src/Importer/CustomerImporterInterface.php
Normal file
@@ -0,0 +1,17 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Kimai time-tracking app.
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace App\Importer;
|
||||
|
||||
use App\Entity\Customer;
|
||||
|
||||
interface CustomerImporterInterface
|
||||
{
|
||||
public function convertEntryToCustomer(array $entry): Customer;
|
||||
}
|
||||
162
src/Importer/DefaultCustomerImporter.php
Normal file
162
src/Importer/DefaultCustomerImporter.php
Normal file
@@ -0,0 +1,162 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Kimai time-tracking app.
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace App\Importer;
|
||||
|
||||
use App\Entity\Customer;
|
||||
use App\Entity\CustomerMeta;
|
||||
|
||||
final class DefaultCustomerImporter extends AbstractCustomerImporter
|
||||
{
|
||||
protected function mapEntryToCustomer(Customer $customer, array $row)
|
||||
{
|
||||
foreach ($row as $name => $value) {
|
||||
switch (strtolower($name)) {
|
||||
case 'name':
|
||||
$customer->setName(substr($value, 0, 149));
|
||||
if (empty($customer->getCompany())) {
|
||||
$customer->setCompany($value);
|
||||
}
|
||||
break;
|
||||
|
||||
case 'company':
|
||||
case 'company-name':
|
||||
case 'company name':
|
||||
$customer->setCompany($value);
|
||||
break;
|
||||
|
||||
case 'email':
|
||||
case 'e-mail':
|
||||
case 'e mail':
|
||||
if (!empty($value)) {
|
||||
$customer->setEmail($value);
|
||||
}
|
||||
break;
|
||||
|
||||
case 'country':
|
||||
if (!empty($value)) {
|
||||
$customer->setCountry($value);
|
||||
}
|
||||
break;
|
||||
|
||||
case 'number':
|
||||
case 'account':
|
||||
case 'customer number':
|
||||
case 'customer account':
|
||||
if (!empty($value)) {
|
||||
$customer->setNumber($value);
|
||||
}
|
||||
break;
|
||||
|
||||
case 'vat':
|
||||
case 'vat-id':
|
||||
case 'vat id':
|
||||
case 'tax-id':
|
||||
case 'tax id':
|
||||
if (!empty($value)) {
|
||||
$customer->setVatId($value);
|
||||
}
|
||||
break;
|
||||
|
||||
case 'comment':
|
||||
case 'description':
|
||||
if (!empty($value)) {
|
||||
$customer->setComment($value);
|
||||
}
|
||||
break;
|
||||
|
||||
case 'address':
|
||||
if (!empty($value)) {
|
||||
$customer->setAddress($value);
|
||||
}
|
||||
break;
|
||||
|
||||
case 'contact':
|
||||
if (!empty($value)) {
|
||||
$customer->setContact($value);
|
||||
}
|
||||
break;
|
||||
|
||||
case 'currency':
|
||||
if (!empty($value)) {
|
||||
$customer->setCurrency($value);
|
||||
}
|
||||
break;
|
||||
|
||||
case 'timezone':
|
||||
if (!empty($value)) {
|
||||
$customer->setTimezone($value);
|
||||
}
|
||||
break;
|
||||
|
||||
case 'phone':
|
||||
if (!empty($value)) {
|
||||
$customer->setPhone($value);
|
||||
}
|
||||
break;
|
||||
|
||||
case 'mobile':
|
||||
if (!empty($value)) {
|
||||
$customer->setMobile($value);
|
||||
}
|
||||
break;
|
||||
|
||||
case 'fax':
|
||||
if (!empty($value)) {
|
||||
$customer->setFax($value);
|
||||
}
|
||||
break;
|
||||
|
||||
case 'homepage':
|
||||
if (!empty($value)) {
|
||||
$customer->setHomepage($value);
|
||||
}
|
||||
break;
|
||||
|
||||
case 'color':
|
||||
if (!empty($value)) {
|
||||
$customer->setColor($value);
|
||||
}
|
||||
break;
|
||||
|
||||
case 'visible':
|
||||
if ($value !== '') {
|
||||
$customer->setVisible((bool) $value);
|
||||
}
|
||||
break;
|
||||
|
||||
case 'budget':
|
||||
if (!empty($value)) {
|
||||
$customer->setBudget($value);
|
||||
}
|
||||
break;
|
||||
|
||||
case 'time budget':
|
||||
case 'time-budget':
|
||||
if (!empty($value)) {
|
||||
$customer->setTimeBudget($value);
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
if (stripos($name, 'meta.') === 0) {
|
||||
$tmpName = str_replace('meta.', '', $name);
|
||||
$meta = new CustomerMeta();
|
||||
$meta->setIsVisible(true);
|
||||
$meta->setName($tmpName);
|
||||
$meta->setValue($value);
|
||||
$customer->setMetaField($meta);
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return $customer;
|
||||
}
|
||||
}
|
||||
90
src/Importer/DefaultProjectImporter.php
Normal file
90
src/Importer/DefaultProjectImporter.php
Normal file
@@ -0,0 +1,90 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Kimai time-tracking app.
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace App\Importer;
|
||||
|
||||
use App\Entity\Project;
|
||||
use App\Entity\ProjectMeta;
|
||||
|
||||
final class DefaultProjectImporter extends AbstractProjectImporter
|
||||
{
|
||||
protected function convertEntry(Project $project, array $row)
|
||||
{
|
||||
foreach ($row as $name => $value) {
|
||||
switch (strtolower($name)) {
|
||||
case 'name':
|
||||
$project->setName(substr($value, 0, 149));
|
||||
break;
|
||||
|
||||
case 'comment':
|
||||
case 'description':
|
||||
if (!empty($value)) {
|
||||
$project->setComment($value);
|
||||
}
|
||||
break;
|
||||
|
||||
case 'ordernumber':
|
||||
case 'order-number':
|
||||
case 'order number':
|
||||
if (!empty($value)) {
|
||||
$project->setOrderNumber($value);
|
||||
}
|
||||
break;
|
||||
|
||||
case 'orderdate':
|
||||
case 'order-date':
|
||||
case 'order date':
|
||||
if (!empty($value)) {
|
||||
$timezone = $project->getCustomer()->getTimezone();
|
||||
$timezone = new \DateTimeZone($timezone ?? date_default_timezone_get());
|
||||
$project->setOrderDate(new \DateTime($value, $timezone));
|
||||
}
|
||||
break;
|
||||
|
||||
case 'color':
|
||||
if (!empty($value)) {
|
||||
$project->setColor($value);
|
||||
}
|
||||
break;
|
||||
|
||||
case 'budget':
|
||||
if (!empty($value)) {
|
||||
$project->setBudget($value);
|
||||
}
|
||||
break;
|
||||
|
||||
case 'time budget':
|
||||
case 'time-budget':
|
||||
if (!empty($value)) {
|
||||
$project->setTimeBudget($value);
|
||||
}
|
||||
break;
|
||||
|
||||
case 'visible':
|
||||
if ($value !== '') {
|
||||
$project->setVisible((bool) $value);
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
if (stripos($name, 'meta.') === 0) {
|
||||
$tmpName = str_replace('meta.', '', $name);
|
||||
$meta = new ProjectMeta();
|
||||
$meta->setIsVisible(true);
|
||||
$meta->setName($tmpName);
|
||||
$meta->setValue($value);
|
||||
$project->setMetaField($meta);
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return $project;
|
||||
}
|
||||
}
|
||||
159
src/Importer/GrandtotalCustomerImporter.php
Normal file
159
src/Importer/GrandtotalCustomerImporter.php
Normal file
@@ -0,0 +1,159 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Kimai time-tracking app.
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace App\Importer;
|
||||
|
||||
use App\Entity\Customer;
|
||||
|
||||
final class GrandtotalCustomerImporter extends AbstractCustomerImporter
|
||||
{
|
||||
protected function findCustomerName(array $row): string
|
||||
{
|
||||
foreach ($row as $name => $value) {
|
||||
switch (strtolower($name)) {
|
||||
case 'organization':
|
||||
case 'firma':
|
||||
if (!empty($value)) {
|
||||
return $value;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
throw new UnsupportedFormatException('Missing customer name, expected in one of the columns: "Organization", "Firma"');
|
||||
}
|
||||
|
||||
protected function findCustomerNumber(array $row): ?string
|
||||
{
|
||||
foreach ($row as $name => $value) {
|
||||
switch (strtolower($name)) {
|
||||
case 'customer number':
|
||||
case 'kundennummer':
|
||||
if (!empty($value)) {
|
||||
return $value;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
protected function mapEntryToCustomer(Customer $customer, array $row)
|
||||
{
|
||||
$names = ['first' => '', 'middle' => '', 'last' => '', 'title' => ''];
|
||||
$address = ['street' => '', 'city' => '', 'code' => ''];
|
||||
|
||||
foreach ($row as $name => $value) {
|
||||
switch (strtolower($name)) {
|
||||
case 'department':
|
||||
case 'abteilung':
|
||||
|
||||
case 'salutation':
|
||||
case 'briefanrede':
|
||||
|
||||
case 'state':
|
||||
case 'bundesland':
|
||||
|
||||
case 'iban':
|
||||
case 'bic':
|
||||
|
||||
case 'sepa mandate id':
|
||||
case 'sepa mandat':
|
||||
// not supported in Kimai
|
||||
break;
|
||||
|
||||
case 'organization':
|
||||
case 'firma':
|
||||
$customer->setCompany($value);
|
||||
break;
|
||||
|
||||
case 'e-mail':
|
||||
if (!empty($value)) {
|
||||
$customer->setEmail($value);
|
||||
}
|
||||
break;
|
||||
|
||||
case 'country':
|
||||
case 'land':
|
||||
if (!empty($value)) {
|
||||
$customer->setCountry($value);
|
||||
}
|
||||
break;
|
||||
|
||||
case 'customer number':
|
||||
case 'kundennummer':
|
||||
if (!empty($value)) {
|
||||
$customer->setNumber($value);
|
||||
}
|
||||
break;
|
||||
|
||||
case 'tax-id':
|
||||
case 'umsatzsteuer':
|
||||
if (!empty($value)) {
|
||||
$customer->setVatId($value);
|
||||
}
|
||||
break;
|
||||
|
||||
case 'note':
|
||||
case 'notiz':
|
||||
if (!empty($value)) {
|
||||
$customer->setComment(strip_tags($value));
|
||||
}
|
||||
break;
|
||||
|
||||
case 'title':
|
||||
case 'titel':
|
||||
$names['title'] = $value;
|
||||
break;
|
||||
|
||||
case 'first name':
|
||||
case 'vorname':
|
||||
$names['first'] = $value;
|
||||
break;
|
||||
|
||||
case 'middle name':
|
||||
case 'zweiter vorname':
|
||||
$names['middle'] = $value;
|
||||
break;
|
||||
|
||||
case 'last name':
|
||||
case 'nachname':
|
||||
$names['last'] = $value;
|
||||
break;
|
||||
|
||||
case 'street':
|
||||
case 'straße':
|
||||
$address['street'] = $value;
|
||||
break;
|
||||
|
||||
case 'zip':
|
||||
case 'plz':
|
||||
$address['code'] = $value;
|
||||
break;
|
||||
|
||||
case 'city':
|
||||
case 'ort':
|
||||
$address['city'] = $value;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
$calculatedAddress = trim($address['street'] . PHP_EOL . $address['code'] . ' ' . $address['city']);
|
||||
$calculatedContact = trim(str_replace(' ', ' ', $names['title'] . ' ' . $names['first'] . ' ' . $names['middle'] . ' ' . $names['last']));
|
||||
|
||||
if (!empty($calculatedAddress)) {
|
||||
$customer->setAddress($calculatedAddress);
|
||||
}
|
||||
|
||||
if (!empty($calculatedContact)) {
|
||||
$customer->setContact($calculatedContact);
|
||||
}
|
||||
|
||||
return $customer;
|
||||
}
|
||||
}
|
||||
14
src/Importer/ImportNotFoundException.php
Normal file
14
src/Importer/ImportNotFoundException.php
Normal file
@@ -0,0 +1,14 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Kimai time-tracking app.
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace App\Importer;
|
||||
|
||||
final class ImportNotFoundException extends \Exception
|
||||
{
|
||||
}
|
||||
14
src/Importer/ImportNotReadableException.php
Normal file
14
src/Importer/ImportNotReadableException.php
Normal file
@@ -0,0 +1,14 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Kimai time-tracking app.
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace App\Importer;
|
||||
|
||||
final class ImportNotReadableException extends \Exception
|
||||
{
|
||||
}
|
||||
20
src/Importer/ImportReaderInterface.php
Normal file
20
src/Importer/ImportReaderInterface.php
Normal file
@@ -0,0 +1,20 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Kimai time-tracking app.
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace App\Importer;
|
||||
|
||||
interface ImportReaderInterface
|
||||
{
|
||||
/**
|
||||
* @param string $input
|
||||
* @return \Iterator
|
||||
* @throws ImportNotFoundException
|
||||
*/
|
||||
public function read(string $input): \Iterator;
|
||||
}
|
||||
102
src/Importer/ImporterService.php
Normal file
102
src/Importer/ImporterService.php
Normal file
@@ -0,0 +1,102 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Kimai time-tracking app.
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace App\Importer;
|
||||
|
||||
use App\Customer\CustomerService;
|
||||
use App\Entity\Customer;
|
||||
use App\Entity\Project;
|
||||
use App\Project\ProjectService;
|
||||
|
||||
class ImporterService
|
||||
{
|
||||
private $customers;
|
||||
private $projects;
|
||||
/**
|
||||
* @var ProjectImporterInterface[]
|
||||
*/
|
||||
private $projectImporter = [];
|
||||
/**
|
||||
* @var CustomerImporterInterface[]
|
||||
*/
|
||||
private $customerImporter = [];
|
||||
|
||||
public function __construct(CustomerService $customers, ProjectService $projects)
|
||||
{
|
||||
$this->customers = $customers;
|
||||
$this->projects = $projects;
|
||||
}
|
||||
|
||||
public function registerProjectImporter(string $name, ProjectImporterInterface $importer): void
|
||||
{
|
||||
$this->projectImporter[$name] = $importer;
|
||||
}
|
||||
|
||||
public function registerCustomerImporter(string $name, CustomerImporterInterface $importer): void
|
||||
{
|
||||
$this->customerImporter[$name] = $importer;
|
||||
}
|
||||
|
||||
public function getProjectImporter(string $name): ProjectImporterInterface
|
||||
{
|
||||
if (!\array_key_exists('default', $this->projectImporter)) {
|
||||
$this->registerProjectImporter('default', new DefaultProjectImporter($this->projects, $this->customers));
|
||||
}
|
||||
|
||||
if (!\array_key_exists($name, $this->projectImporter)) {
|
||||
throw new \InvalidArgumentException('Unknown project importer: ' . $name);
|
||||
}
|
||||
|
||||
return $this->projectImporter[$name];
|
||||
}
|
||||
|
||||
public function getCustomerImporter(string $name): CustomerImporterInterface
|
||||
{
|
||||
if (!\array_key_exists('default', $this->customerImporter)) {
|
||||
$this->registerCustomerImporter('default', new DefaultCustomerImporter($this->customers));
|
||||
$this->registerCustomerImporter('grandtotal', new GrandtotalCustomerImporter($this->customers));
|
||||
}
|
||||
|
||||
if (!\array_key_exists($name, $this->customerImporter)) {
|
||||
throw new \InvalidArgumentException('Unknown customer importer: ' . $name);
|
||||
}
|
||||
|
||||
return $this->customerImporter[$name];
|
||||
}
|
||||
|
||||
public function importProject(Project $project): void
|
||||
{
|
||||
if ($project->getId() === null) {
|
||||
$this->projects->saveNewProject($project);
|
||||
} else {
|
||||
$this->projects->updateProject($project);
|
||||
}
|
||||
}
|
||||
|
||||
public function getReader(string $name): ImportReaderInterface
|
||||
{
|
||||
switch ($name) {
|
||||
case 'csv':
|
||||
return new CsvReader(',');
|
||||
case 'csv-semicolon':
|
||||
return new CsvReader(';');
|
||||
}
|
||||
|
||||
throw new \InvalidArgumentException('Unknown import reader: ' . $name);
|
||||
}
|
||||
|
||||
public function importCustomer(Customer $customer): void
|
||||
{
|
||||
if ($customer->getId() === null) {
|
||||
$this->customers->saveNewCustomer($customer);
|
||||
} else {
|
||||
$this->customers->updateCustomer($customer);
|
||||
}
|
||||
}
|
||||
}
|
||||
17
src/Importer/ProjectImporterInterface.php
Normal file
17
src/Importer/ProjectImporterInterface.php
Normal file
@@ -0,0 +1,17 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Kimai time-tracking app.
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace App\Importer;
|
||||
|
||||
use App\Entity\Project;
|
||||
|
||||
interface ProjectImporterInterface
|
||||
{
|
||||
public function convertEntryToProject(array $entry): Project;
|
||||
}
|
||||
20
src/Importer/UnsupportedFormatException.php
Normal file
20
src/Importer/UnsupportedFormatException.php
Normal file
@@ -0,0 +1,20 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Kimai time-tracking app.
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace App\Importer;
|
||||
|
||||
use Throwable;
|
||||
|
||||
class UnsupportedFormatException extends \Exception
|
||||
{
|
||||
public function __construct(string $message, int $code = 0, Throwable $previous = null)
|
||||
{
|
||||
parent::__construct($message, $code, $previous);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user