mirror of
https://github.com/solidtime-io/solidtime.git
synced 2026-10-15 09:13:16 +01:00
Skip running time entries in solidtime import
Imported running time entries could give a user a second running time entry, for example when an export is imported again while a timer runs.
This commit is contained in:
committed by
Constantin Graf
parent
1863953a23
commit
b42e84294c
@@ -211,6 +211,10 @@ class SolidtimeImporter extends DefaultImporter
|
|||||||
|
|
||||||
// Time entries
|
// Time entries
|
||||||
foreach ($timeEntriesReader as $timeEntryRow) {
|
foreach ($timeEntriesReader as $timeEntryRow) {
|
||||||
|
// Running time entries are skipped, since the user could already have a running time entry
|
||||||
|
if ($timeEntryRow['end'] === '') {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
$userId = $this->userImportHelper->getKeyByExternalIdentifier($timeEntryRow['user_id']);
|
$userId = $this->userImportHelper->getKeyByExternalIdentifier($timeEntryRow['user_id']);
|
||||||
if ($userId === null) {
|
if ($userId === null) {
|
||||||
throw new ImportException('Time entry references a user that does not exist in the import');
|
throw new ImportException('Time entry references a user that does not exist in the import');
|
||||||
@@ -286,22 +290,18 @@ class SolidtimeImporter extends DefaultImporter
|
|||||||
}
|
}
|
||||||
$timeEntry->start = $start->utc();
|
$timeEntry->start = $start->utc();
|
||||||
|
|
||||||
if ($timeEntryRow['end'] !== '') {
|
try {
|
||||||
try {
|
$end = Carbon::createFromFormat('Y-m-d\TH:i:s\Z', $timeEntryRow['end'], 'UTC');
|
||||||
$end = Carbon::createFromFormat('Y-m-d\TH:i:s\Z', $timeEntryRow['end'], 'UTC');
|
} catch (InvalidFormatException) {
|
||||||
} catch (InvalidFormatException) {
|
throw new ImportException('End date ("'.$timeEntryRow['end'].'") is invalid');
|
||||||
throw new ImportException('End date ("'.$timeEntryRow['end'].'") is invalid');
|
|
||||||
}
|
|
||||||
if ($end === null) {
|
|
||||||
throw new ImportException('End date ("'.$timeEntryRow['end'].'") is invalid');
|
|
||||||
}
|
|
||||||
if ($end->lt($start)) {
|
|
||||||
throw new ImportException('End date ("'.$timeEntryRow['end'].'") is before start date ("'.$timeEntryRow['start'].'")');
|
|
||||||
}
|
|
||||||
$timeEntry->end = $end->utc();
|
|
||||||
} else {
|
|
||||||
$timeEntry->end = null;
|
|
||||||
}
|
}
|
||||||
|
if ($end === null) {
|
||||||
|
throw new ImportException('End date ("'.$timeEntryRow['end'].'") is invalid');
|
||||||
|
}
|
||||||
|
if ($end->lt($start)) {
|
||||||
|
throw new ImportException('End date ("'.$timeEntryRow['end'].'") is before start date ("'.$timeEntryRow['start'].'")');
|
||||||
|
}
|
||||||
|
$timeEntry->end = $end->utc();
|
||||||
|
|
||||||
if ($timeEntryRow['still_active_email_sent_at'] !== '') {
|
if ($timeEntryRow['still_active_email_sent_at'] !== '') {
|
||||||
try {
|
try {
|
||||||
|
|||||||
3
resources/testfiles/solidtime_import_test_3/clients.csv
Normal file
3
resources/testfiles/solidtime_import_test_3/clients.csv
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
id,name,organization_id,archived_at,created_at,updated_at
|
||||||
|
b4187a44-41f4-46d7-8460-f15a25b3aad6,"Big Company",ee5a8cd6-312f-4ae6-b044-e2014f09ecc2,,2024-08-22T10:36:48Z,2024-08-22T10:36:48Z
|
||||||
|
e5a4d8f5-81ae-4606-8e84-6ab1ffa58b72,"Other Company (Archived)",ee5a8cd6-312f-4ae6-b044-e2014f09ecc2,2024-08-22T10:36:48Z,2024-08-22T10:36:48Z,2024-08-22T10:36:48Z
|
||||||
|
2
resources/testfiles/solidtime_import_test_3/members.csv
Normal file
2
resources/testfiles/solidtime_import_test_3/members.csv
Normal file
@@ -0,0 +1,2 @@
|
|||||||
|
id,user_id,name,email,organization_id,billable_rate,role,created_at,updated_at
|
||||||
|
06e6e605-86bd-417b-b75d-02f671e5d520,0446cdd8-3ad1-43d6-9231-9e0dc4eeb71c,"Peter Tester",peter.test@email.test,ee5a8cd6-312f-4ae6-b044-e2014f09ecc2,,admin,2024-08-22T10:36:48Z,2024-08-22T10:36:48Z
|
||||||
|
1
resources/testfiles/solidtime_import_test_3/meta.json
Normal file
1
resources/testfiles/solidtime_import_test_3/meta.json
Normal file
@@ -0,0 +1 @@
|
|||||||
|
{"id":"d6a324ee-58d5-4096-8069-c63bd55608f7","version":"1.0","organizations":["ee5a8cd6-312f-4ae6-b044-e2014f09ecc2"],"exported_at":"2024-08-26T18:21:59Z"}
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
id,email,organization_id,role,created_at,updated_at
|
||||||
|
@@ -0,0 +1,2 @@
|
|||||||
|
id,name,billable_rate,currency,created_at,updated_at
|
||||||
|
ee5a8cd6-312f-4ae6-b044-e2014f09ecc2,"ACME Corp",,EUR,2024-08-22T10:36:48Z,2024-08-22T10:36:48Z
|
||||||
|
@@ -0,0 +1,2 @@
|
|||||||
|
id,billable_rate,project_id,user_id,member_id,created_at,updated_at
|
||||||
|
180a1a98-2f1c-4596-86e4-63a6be0d7b1d,10002,06e79ec4-33f8-4730-804c-d03c014991d1,0446cdd8-3ad1-43d6-9231-9e0dc4eeb71c,06e6e605-86bd-417b-b75d-02f671e5d520,2024-08-22T10:36:48Z,2024-08-22T10:36:48Z
|
||||||
|
4
resources/testfiles/solidtime_import_test_3/projects.csv
Normal file
4
resources/testfiles/solidtime_import_test_3/projects.csv
Normal file
@@ -0,0 +1,4 @@
|
|||||||
|
id,name,color,billable_rate,is_public,client_id,organization_id,is_billable,archived_at,created_at,updated_at
|
||||||
|
06e79ec4-33f8-4730-804c-d03c014991d1,"Project for Big Company",#ec407a,10001,false,b4187a44-41f4-46d7-8460-f15a25b3aad6,ee5a8cd6-312f-4ae6-b044-e2014f09ecc2,true,,2024-08-22T10:36:48Z,2024-08-22T10:36:48Z
|
||||||
|
622c74a9-7e64-44c2-9426-2a37ac738206,"Project without Client",#ef5350,,false,,ee5a8cd6-312f-4ae6-b044-e2014f09ecc2,false,,2024-08-22T10:36:48Z,2024-08-22T10:36:48Z
|
||||||
|
aa831162-dbb2-4cfe-bfe0-5e3a252c66f0,"Project (Archived)",#6a407f,,true,e5a4d8f5-81ae-4606-8e84-6ab1ffa58b72,ee5a8cd6-312f-4ae6-b044-e2014f09ecc2,true,2024-08-25T10:00:00Z,2024-08-22T10:36:48Z,2024-08-22T10:36:48Z
|
||||||
|
3
resources/testfiles/solidtime_import_test_3/tags.csv
Normal file
3
resources/testfiles/solidtime_import_test_3/tags.csv
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
id,name,organization_id,created_at,updated_at
|
||||||
|
2c5c2da7-9ef8-4410-bb8f-6e0a90f9d2c7,Development,ee5a8cd6-312f-4ae6-b044-e2014f09ecc2,2024-08-22T10:36:48Z,2024-08-22T10:36:48Z
|
||||||
|
bf6c0ac5-2587-474b-8983-40bb3ea8002f,Backend,ee5a8cd6-312f-4ae6-b044-e2014f09ecc2,2024-08-22T10:36:48Z,2024-08-22T10:36:48Z
|
||||||
|
3
resources/testfiles/solidtime_import_test_3/tasks.csv
Normal file
3
resources/testfiles/solidtime_import_test_3/tasks.csv
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
id,name,project_id,organization_id,done_at,created_at,updated_at
|
||||||
|
b49688a0-94f3-4cb3-9ca1-5003de955fb0,"Task 1",06e79ec4-33f8-4730-804c-d03c014991d1,ee5a8cd6-312f-4ae6-b044-e2014f09ecc2,,2024-08-22T10:36:48Z,2024-08-22T10:36:48Z
|
||||||
|
b49688a0-94f3-4cb3-9ca1-5003de955fb0,"Task 2",06e79ec4-33f8-4730-804c-d03c014991d1,ee5a8cd6-312f-4ae6-b044-e2014f09ecc2,2024-08-24T10:00:00Z,2024-08-22T10:36:48Z,2024-08-22T10:36:48Z
|
||||||
|
@@ -0,0 +1,5 @@
|
|||||||
|
id,description,start,end,billable_rate,billable,type,member_id,user_id,organization_id,client_id,project_id,task_id,tags,is_imported,still_active_email_sent_at,created_at,updated_at
|
||||||
|
00aae3be-18fc-462d-bee4-350fb605b2f3,,2024-03-04T09:23:52Z,2024-03-04T09:23:52Z,,false,,06e6e605-86bd-417b-b75d-02f671e5d520,0446cdd8-3ad1-43d6-9231-9e0dc4eeb71c,ee5a8cd6-312f-4ae6-b044-e2014f09ecc2,,,,"[""2c5c2da7-9ef8-4410-bb8f-6e0a90f9d2c7"",""bf6c0ac5-2587-474b-8983-40bb3ea8002f""]",false,,2024-08-22T10:36:48Z,2024-08-22T10:36:48Z
|
||||||
|
1c7a905d-aa12-4d08-bc41-7e92577e7cdf,"Working hard",2024-03-04T09:23:00Z,2024-03-04T10:23:01Z,,true,work,06e6e605-86bd-417b-b75d-02f671e5d520,0446cdd8-3ad1-43d6-9231-9e0dc4eeb71c,ee5a8cd6-312f-4ae6-b044-e2014f09ecc2,b4187a44-41f4-46d7-8460-f15a25b3aad6,06e79ec4-33f8-4730-804c-d03c014991d1,b49688a0-94f3-4cb3-9ca1-5003de955fb0,[],false,,2024-08-22T10:36:48Z,2024-08-22T10:36:48Z
|
||||||
|
9f2f5b83-3a6b-4a9c-8d21-4f1e5cf1b7aa,"Lunch break",2024-03-04T12:00:00Z,2024-03-04T12:30:00Z,,false,break,06e6e605-86bd-417b-b75d-02f671e5d520,0446cdd8-3ad1-43d6-9231-9e0dc4eeb71c,ee5a8cd6-312f-4ae6-b044-e2014f09ecc2,,,,[],false,,2024-08-22T10:36:48Z,2024-08-22T10:36:48Z
|
||||||
|
5d0e8c4a-7b1f-4e2a-9c3d-8a6b2f1e4c7d,"Still running",2024-03-04T13:00:00Z,,,true,work,06e6e605-86bd-417b-b75d-02f671e5d520,0446cdd8-3ad1-43d6-9231-9e0dc4eeb71c,ee5a8cd6-312f-4ae6-b044-e2014f09ecc2,,,,[],false,,2024-08-22T10:36:48Z,2024-08-22T10:36:48Z
|
||||||
|
@@ -164,6 +164,31 @@ class SolidtimeImporterTest extends ImporterTestAbstract
|
|||||||
$this->fail();
|
$this->fail();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function test_import_skips_running_time_entries(): void
|
||||||
|
{
|
||||||
|
// Arrange
|
||||||
|
$zipPath = $this->createTestZip('solidtime_import_test_3');
|
||||||
|
$timezone = 'Europe/Vienna';
|
||||||
|
$organization = Organization::factory()->create();
|
||||||
|
$importer = new SolidtimeImporter;
|
||||||
|
$importer->init($organization);
|
||||||
|
$data = file_get_contents($zipPath);
|
||||||
|
Queue::fake([
|
||||||
|
RecalculateSpentTimeForProject::class,
|
||||||
|
RecalculateSpentTimeForTask::class,
|
||||||
|
]);
|
||||||
|
|
||||||
|
// Act
|
||||||
|
$importer->importData($data, $timezone);
|
||||||
|
$report = $importer->getReport();
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
$this->assertSame(3, $report->timeEntriesCreated);
|
||||||
|
$this->assertSame(3, TimeEntry::query()->count());
|
||||||
|
$this->assertSame(0, TimeEntry::query()->whereNull('end')->count());
|
||||||
|
$this->assertNull(TimeEntry::query()->where('description', 'Still running')->first());
|
||||||
|
}
|
||||||
|
|
||||||
public function test_import_of_test_file_twice_succeeds(): void
|
public function test_import_of_test_file_twice_succeeds(): void
|
||||||
{
|
{
|
||||||
// Arrange
|
// Arrange
|
||||||
|
|||||||
Reference in New Issue
Block a user