fix migration for MySQL (#430)
This commit is contained in:
@@ -247,7 +247,7 @@ class KimaiImporterCommand extends Command
|
||||
$allImports += $counter;
|
||||
$io->success('Imported users: ' . $counter);
|
||||
} catch (\Exception $ex) {
|
||||
$io->error('Failed to import users: ' . $ex->getMessage());
|
||||
$io->error('Failed to import users: ' . $ex->getMessage() . PHP_EOL . $ex->getTraceAsString());
|
||||
|
||||
return;
|
||||
}
|
||||
@@ -257,7 +257,7 @@ class KimaiImporterCommand extends Command
|
||||
$allImports += $counter;
|
||||
$io->success('Imported customers: ' . $counter);
|
||||
} catch (\Exception $ex) {
|
||||
$io->error('Failed to import customers: ' . $ex->getMessage());
|
||||
$io->error('Failed to import customers: ' . $ex->getMessage() . PHP_EOL . $ex->getTraceAsString());
|
||||
|
||||
return;
|
||||
}
|
||||
@@ -267,7 +267,7 @@ class KimaiImporterCommand extends Command
|
||||
$allImports += $counter;
|
||||
$io->success('Imported projects: ' . $counter);
|
||||
} catch (\Exception $ex) {
|
||||
$io->error('Failed to import projects: ' . $ex->getMessage());
|
||||
$io->error('Failed to import projects: ' . $ex->getMessage() . PHP_EOL . $ex->getTraceAsString());
|
||||
|
||||
return;
|
||||
}
|
||||
@@ -277,7 +277,7 @@ class KimaiImporterCommand extends Command
|
||||
$allImports += $counter;
|
||||
$io->success('Imported activities: ' . $counter);
|
||||
} catch (\Exception $ex) {
|
||||
$io->error('Failed to import activities: ' . $ex->getMessage());
|
||||
$io->error('Failed to import activities: ' . $ex->getMessage() . PHP_EOL . $ex->getTraceAsString());
|
||||
|
||||
return;
|
||||
}
|
||||
@@ -287,7 +287,7 @@ class KimaiImporterCommand extends Command
|
||||
$allImports += $counter;
|
||||
$io->success('Imported timesheet records: ' . $counter);
|
||||
} catch (\Exception $ex) {
|
||||
$io->error('Failed to import timesheet records: ' . $ex->getMessage());
|
||||
$io->error('Failed to import timesheet records: ' . $ex->getMessage() . PHP_EOL . $ex->getTraceAsString());
|
||||
|
||||
return;
|
||||
}
|
||||
@@ -385,8 +385,10 @@ class KimaiImporterCommand extends Command
|
||||
protected function bytesHumanReadable($size)
|
||||
{
|
||||
$unit = ['b', 'kB', 'MB', 'GB'];
|
||||
$i = floor(log($size, 1024));
|
||||
$a = (int) $i;
|
||||
|
||||
return @round($size / pow(1024, ($i = floor(log($size, 1024)))), 2) . ' ' . $unit[$i];
|
||||
return @round($size / pow(1024, $i), 2) . ' ' . $unit[$a];
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -422,12 +424,8 @@ class KimaiImporterCommand extends Command
|
||||
if ($errors->count() > 0) {
|
||||
/** @var \Symfony\Component\Validator\ConstraintViolation $error */
|
||||
foreach ($errors as $error) {
|
||||
$value = $error->getInvalidValue();
|
||||
$io->error(
|
||||
$error->getPropertyPath()
|
||||
. ' (' . (is_array($value) ? implode(',', $value) : $value) . ')'
|
||||
. "\n "
|
||||
. $error->getMessage()
|
||||
(string) $error
|
||||
);
|
||||
}
|
||||
|
||||
@@ -941,7 +939,7 @@ class KimaiImporterCommand extends Command
|
||||
;
|
||||
|
||||
if (!$this->validateImport($io, $timesheet)) {
|
||||
throw new \Exception('Failed to validate timesheet record: ' . $timesheet->getId());
|
||||
throw new \Exception('Failed to validate timesheet record: ' . $oldRecord['timeEntryID']);
|
||||
}
|
||||
|
||||
try {
|
||||
@@ -952,8 +950,7 @@ class KimaiImporterCommand extends Command
|
||||
}
|
||||
++$counter;
|
||||
} catch (\Exception $ex) {
|
||||
$io->error('Failed to create timesheet record: ' . $timesheet->getId());
|
||||
$io->error('Reason: ' . $ex->getMessage());
|
||||
$io->error('Failed to create timesheet record: ' . $ex->getMessage());
|
||||
}
|
||||
|
||||
$io->write('.');
|
||||
|
||||
@@ -376,21 +376,21 @@ class Timesheet
|
||||
->addViolation();
|
||||
}
|
||||
|
||||
if ($activity->getVisible() === false) {
|
||||
if (null === $this->getEnd() && $activity->getVisible() === false) {
|
||||
$context->buildViolation('Cannot start a disabled activity.')
|
||||
->atPath('activity')
|
||||
->setTranslationDomain('validators')
|
||||
->addViolation();
|
||||
}
|
||||
|
||||
if ($project->getVisible() === false) {
|
||||
if (null === $this->getEnd() && $project->getVisible() === false) {
|
||||
$context->buildViolation('Cannot start a disabled project.')
|
||||
->atPath('project')
|
||||
->setTranslationDomain('validators')
|
||||
->addViolation();
|
||||
}
|
||||
|
||||
if ($project->getCustomer()->getVisible() === false) {
|
||||
if (null === $this->getEnd() && $project->getCustomer()->getVisible() === false) {
|
||||
$context->buildViolation('Cannot start a disabled customer.')
|
||||
->atPath('customer')
|
||||
->setTranslationDomain('validators')
|
||||
|
||||
@@ -56,10 +56,11 @@ final class Version20181031220003 extends AbstractMigration
|
||||
$this->addSql('CREATE INDEX IDX_4F60C6B1166D1F9C ON ' . $timesheet . ' (project_id)');
|
||||
} else {
|
||||
// project table
|
||||
$this->addSql('ALTER TABLE ' . $projects . ' DROP FOREIGN KEY FK_407F12069395C3F3');
|
||||
$this->addSql('ALTER TABLE ' . $projects . ' CHANGE customer_id customer_id INT NOT NULL');
|
||||
$this->addSql('ALTER TABLE ' . $projects . ' ADD CONSTRAINT FK_407F12069395C3F3 FOREIGN KEY (customer_id) REFERENCES ' . $customers . ' (id) ON DELETE CASCADE');
|
||||
// timesheet table
|
||||
$this->addSql('ALTER TABLE ' . $timesheet . ' ADD project_id INT DEFAULT NULL AFTER activity_id');
|
||||
$this->addSql('ALTER TABLE ' . $timesheet . ' ADD CONSTRAINT FK_4F60C6B1166D1F9C FOREIGN KEY (project_id) REFERENCES ' . $projects . ' (id) ON DELETE CASCADE');
|
||||
$this->addSql('CREATE INDEX IDX_4F60C6B1166D1F9C ON ' . $timesheet . ' (project_id)');
|
||||
}
|
||||
|
||||
@@ -80,7 +81,12 @@ final class Version20181031220003 extends AbstractMigration
|
||||
$this->addSql('CREATE INDEX IDX_4F60C6B18D93D649 ON ' . $timesheet . ' (user)');
|
||||
$this->addSql('CREATE INDEX IDX_4F60C6B1166D1F9C ON ' . $timesheet . ' (project_id)');
|
||||
} else {
|
||||
$this->addSql('ALTER TABLE ' . $timesheet . ' DROP FOREIGN KEY FK_4F60C6B18D93D649');
|
||||
$this->addSql('ALTER TABLE ' . $timesheet . ' DROP FOREIGN KEY FK_4F60C6B181C06096');
|
||||
$this->addSql('ALTER TABLE ' . $timesheet . ' CHANGE project_id project_id INT NOT NULL, CHANGE user user INT NOT NULL, CHANGE activity_id activity_id INT NOT NULL');
|
||||
$this->addSql('ALTER TABLE ' . $timesheet . ' ADD CONSTRAINT FK_4F60C6B1166D1F9C FOREIGN KEY (project_id) REFERENCES ' . $projects . ' (id) ON DELETE CASCADE');
|
||||
$this->addSql('ALTER TABLE ' . $timesheet . ' ADD CONSTRAINT FK_4F60C6B18D93D649 FOREIGN KEY (user) REFERENCES ' . $users . ' (id) ON DELETE CASCADE');
|
||||
$this->addSql('ALTER TABLE ' . $timesheet . ' ADD CONSTRAINT FK_4F60C6B181C06096 FOREIGN KEY (activity_id) REFERENCES ' . $activities . ' (id) ON DELETE CASCADE');
|
||||
}
|
||||
}
|
||||
|
||||
@@ -94,6 +100,7 @@ final class Version20181031220003 extends AbstractMigration
|
||||
|
||||
$timesheet = $this->getTableName('timesheet');
|
||||
$projects = $this->getTableName('projects');
|
||||
$customers = $this->getTableName('customers');
|
||||
|
||||
if ($platform === 'sqlite') {
|
||||
// project table
|
||||
@@ -117,7 +124,9 @@ final class Version20181031220003 extends AbstractMigration
|
||||
$this->addSql('CREATE INDEX IDX_4F60C6B181C06096 ON ' . $timesheet . ' (activity_id)');
|
||||
} else {
|
||||
// project table
|
||||
$this->addSql('ALTER TABLE ' . $projects . ' DROP FOREIGN KEY FK_407F12069395C3F3');
|
||||
$this->addSql('ALTER TABLE ' . $projects . ' CHANGE customer_id customer_id INT DEFAULT NULL');
|
||||
$this->addSql('ALTER TABLE ' . $projects . ' ADD CONSTRAINT FK_407F12069395C3F3 FOREIGN KEY (customer_id) REFERENCES ' . $customers . ' (id) ON DELETE CASCADE');
|
||||
// timesheet table
|
||||
$this->addSql('ALTER TABLE ' . $timesheet . ' DROP FOREIGN KEY FK_4F60C6B1166D1F9C');
|
||||
$this->addSql('DROP INDEX IDX_4F60C6B1166D1F9C ON ' . $timesheet);
|
||||
|
||||
@@ -121,6 +121,24 @@ class TimesheetTest extends AbstractEntityTest
|
||||
$this->assertHasViolationForField($entity, 'customer');
|
||||
}
|
||||
|
||||
public function testValidationCustomerInvisibleDoesNotTriggerOnStoppedEntites()
|
||||
{
|
||||
$customer = (new Customer())->setVisible(false);
|
||||
$project = (new Project())->setName('foo')->setCustomer($customer);
|
||||
$activity = (new Activity())->setName('hello-world')->setProject($project);
|
||||
|
||||
$entity = new Timesheet();
|
||||
$entity
|
||||
->setUser(new User())
|
||||
->setActivity($activity)
|
||||
->setProject($project)
|
||||
->setBegin(new \DateTime())
|
||||
->setEnd(new \DateTime())
|
||||
;
|
||||
|
||||
$this->assertHasNoViolations($entity);
|
||||
}
|
||||
|
||||
public function testValidationProjectInvisible()
|
||||
{
|
||||
$customer = new Customer();
|
||||
@@ -138,6 +156,24 @@ class TimesheetTest extends AbstractEntityTest
|
||||
$this->assertHasViolationForField($entity, 'project');
|
||||
}
|
||||
|
||||
public function testValidationProjectInvisibleDoesNotTriggerOnStoppedEntites()
|
||||
{
|
||||
$customer = new Customer();
|
||||
$project = (new Project())->setName('foo')->setCustomer($customer)->setVisible(false);
|
||||
$activity = (new Activity())->setName('hello-world')->setProject($project);
|
||||
|
||||
$entity = new Timesheet();
|
||||
$entity
|
||||
->setUser(new User())
|
||||
->setActivity($activity)
|
||||
->setProject($project)
|
||||
->setBegin(new \DateTime())
|
||||
->setEnd(new \DateTime())
|
||||
;
|
||||
|
||||
$this->assertHasNoViolations($entity);
|
||||
}
|
||||
|
||||
public function testValidationActivityInvisible()
|
||||
{
|
||||
$customer = new Customer();
|
||||
@@ -155,6 +191,24 @@ class TimesheetTest extends AbstractEntityTest
|
||||
$this->assertHasViolationForField($entity, 'activity');
|
||||
}
|
||||
|
||||
public function testValidationActivityInvisibleDoesNotTriggerOnStoppedEntites()
|
||||
{
|
||||
$customer = new Customer();
|
||||
$project = (new Project())->setName('foo')->setCustomer($customer);
|
||||
$activity = (new Activity())->setName('hello-world')->setProject($project)->setVisible(false);
|
||||
|
||||
$entity = new Timesheet();
|
||||
$entity
|
||||
->setUser(new User())
|
||||
->setActivity($activity)
|
||||
->setProject($project)
|
||||
->setBegin(new \DateTime())
|
||||
->setEnd(new \DateTime())
|
||||
;
|
||||
|
||||
$this->assertHasNoViolations($entity);
|
||||
}
|
||||
|
||||
public function testValidationEndNotEarlierThanBegin()
|
||||
{
|
||||
$entity = $this->getEntity();
|
||||
|
||||
@@ -42,3 +42,19 @@ SQLite is a great database engine for testing, but when it comes to production u
|
||||
|
||||
- It does not support ALTER TABLE commands and makes update procedures very clunky and problematic/errorsome (we still try to support updates, but they are heavy on large databases)
|
||||
- It does not support FOREIGN KEY constraints out of the box, which can lead to critical bugs when deleting activities/projects/customers
|
||||
|
||||
## Dotenv::populate() must be an instance of Symfony\\Component\\Dotenv\\void
|
||||
|
||||
If you encounter an error like this:
|
||||
|
||||
```
|
||||
PHP Fatal error: Uncaught TypeError: Return value of Symfony\\Component\\Dotenv\\Dotenv::populate() must be an instance of Symfony\\Component\\Dotenv\\void, none returned in /var/www/kimai2/vendor/symfony/dotenv/Dotenv.php:95
|
||||
Stack trace:
|
||||
#0 /var/www/kimai2/vendor/symfony/dotenv/Dotenv.php(57): Symfony\\Component\\Dotenv\\Dotenv->populate(Array)
|
||||
#1 /var/www//kimai2/public/index.php(15): Symfony\\Component\\Dotenv\\Dotenv->load('/var/www/html/k...')
|
||||
#2 {main}\n thrown in /var/www/kimai2/vendor/symfony/dotenv/Dotenv.php on line 95
|
||||
|
||||
```
|
||||
|
||||
you are running PHP 7.0. Probably you were able to install Kimai v2, because your PHP-CLI uses a different PHP version than your webserver.
|
||||
Upgrade PHP and the error will be gone.
|
||||
Reference in New Issue
Block a user